github.com/doitroot/helm@v3.0.0-beta.3+incompatible/pkg/chartutil/save.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package chartutil 18 19 import ( 20 "archive/tar" 21 "compress/gzip" 22 "fmt" 23 "os" 24 "path/filepath" 25 26 "github.com/pkg/errors" 27 "sigs.k8s.io/yaml" 28 29 "helm.sh/helm/pkg/chart" 30 ) 31 32 var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") 33 34 // SaveDir saves a chart as files in a directory. 35 func SaveDir(c *chart.Chart, dest string) error { 36 // Create the chart directory 37 outdir := filepath.Join(dest, c.Name()) 38 if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() { 39 return errors.Errorf("file %s already exists and is not a directory", outdir) 40 } 41 if err := os.MkdirAll(outdir, 0755); err != nil { 42 return err 43 } 44 45 // Save the chart file. 46 if err := SaveChartfile(filepath.Join(outdir, ChartfileName), c.Metadata); err != nil { 47 return err 48 } 49 50 // Save values.yaml 51 if c.Values != nil { 52 vf := filepath.Join(outdir, ValuesfileName) 53 b, _ := yaml.Marshal(c.Values) 54 if err := writeFile(vf, b); err != nil { 55 return err 56 } 57 } 58 59 // Save templates and files 60 for _, o := range [][]*chart.File{c.Templates, c.Files} { 61 for _, f := range o { 62 n := filepath.Join(outdir, f.Name) 63 if err := writeFile(n, f.Data); err != nil { 64 return err 65 } 66 } 67 } 68 69 // Save dependencies 70 base := filepath.Join(outdir, ChartsDir) 71 for _, dep := range c.Dependencies() { 72 // Here, we write each dependency as a tar file. 73 if _, err := Save(dep, base); err != nil { 74 return errors.Wrapf(err, "saving %s", dep.ChartFullPath()) 75 } 76 } 77 return nil 78 } 79 80 // Save creates an archived chart to the given directory. 81 // 82 // This takes an existing chart and a destination directory. 83 // 84 // If the directory is /foo, and the chart is named bar, with version 1.0.0, this 85 // will generate /foo/bar-1.0.0.tgz. 86 // 87 // This returns the absolute path to the chart archive file. 88 func Save(c *chart.Chart, outDir string) (string, error) { 89 if err := c.Validate(); err != nil { 90 return "", errors.Wrap(err, "chart validation") 91 } 92 93 filename := fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version) 94 filename = filepath.Join(outDir, filename) 95 if stat, err := os.Stat(filepath.Dir(filename)); os.IsNotExist(err) { 96 if err := os.MkdirAll(filepath.Dir(filename), 0755); !os.IsExist(err) { 97 return "", err 98 } 99 } else if !stat.IsDir() { 100 return "", errors.Errorf("is not a directory: %s", filepath.Dir(filename)) 101 } 102 103 f, err := os.Create(filename) 104 if err != nil { 105 return "", err 106 } 107 108 // Wrap in gzip writer 109 zipper := gzip.NewWriter(f) 110 zipper.Header.Extra = headerBytes 111 zipper.Header.Comment = "Helm" 112 113 // Wrap in tar writer 114 twriter := tar.NewWriter(zipper) 115 rollback := false 116 defer func() { 117 twriter.Close() 118 zipper.Close() 119 f.Close() 120 if rollback { 121 os.Remove(filename) 122 } 123 }() 124 125 if err := writeTarContents(twriter, c, ""); err != nil { 126 rollback = true 127 } 128 return filename, err 129 } 130 131 func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { 132 base := filepath.Join(prefix, c.Name()) 133 134 // Save Chart.yaml 135 cdata, err := yaml.Marshal(c.Metadata) 136 if err != nil { 137 return err 138 } 139 if err := writeToTar(out, base+"/Chart.yaml", cdata); err != nil { 140 return err 141 } 142 143 // Save values.yaml 144 ydata, err := yaml.Marshal(c.Values) 145 if err != nil { 146 return err 147 } 148 if err := writeToTar(out, base+"/values.yaml", ydata); err != nil { 149 return err 150 } 151 152 // Save templates 153 for _, f := range c.Templates { 154 n := filepath.Join(base, f.Name) 155 if err := writeToTar(out, n, f.Data); err != nil { 156 return err 157 } 158 } 159 160 // Save files 161 for _, f := range c.Files { 162 n := filepath.Join(base, f.Name) 163 if err := writeToTar(out, n, f.Data); err != nil { 164 return err 165 } 166 } 167 168 // Save dependencies 169 for _, dep := range c.Dependencies() { 170 if err := writeTarContents(out, dep, base+"/charts"); err != nil { 171 return err 172 } 173 } 174 return nil 175 } 176 177 // writeToTar writes a single file to a tar archive. 178 func writeToTar(out *tar.Writer, name string, body []byte) error { 179 // TODO: Do we need to create dummy parent directory names if none exist? 180 h := &tar.Header{ 181 Name: name, 182 Mode: 0644, 183 Size: int64(len(body)), 184 } 185 if err := out.WriteHeader(h); err != nil { 186 return err 187 } 188 _, err := out.Write(body) 189 return err 190 }