github.com/umeshredd/helm@v3.0.0-alpha.1+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 "io/ioutil" 24 "os" 25 "path/filepath" 26 27 "github.com/ghodss/yaml" 28 "github.com/pkg/errors" 29 30 "helm.sh/helm/pkg/chart" 31 ) 32 33 var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") 34 35 // SaveDir saves a chart as files in a directory. 36 func SaveDir(c *chart.Chart, dest string) error { 37 // Create the chart directory 38 outdir := filepath.Join(dest, c.Name()) 39 if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() { 40 return errors.Errorf("file %s already exists and is not a directory", outdir) 41 } 42 if err := os.MkdirAll(outdir, 0755); err != nil { 43 return err 44 } 45 46 // Save the chart file. 47 if err := SaveChartfile(filepath.Join(outdir, ChartfileName), c.Metadata); err != nil { 48 return err 49 } 50 51 // Save values.yaml 52 if c.Values != nil { 53 vf := filepath.Join(outdir, ValuesfileName) 54 b, _ := yaml.Marshal(c.Values) 55 if err := ioutil.WriteFile(vf, b, 0755); err != nil { 56 return err 57 } 58 } 59 60 for _, d := range []string{TemplatesDir, ChartsDir} { 61 if err := os.MkdirAll(filepath.Join(outdir, d), 0755); err != nil { 62 return err 63 } 64 } 65 66 // Save templates 67 for _, f := range c.Templates { 68 n := filepath.Join(outdir, f.Name) 69 if err := ioutil.WriteFile(n, f.Data, 0755); err != nil { 70 return err 71 } 72 } 73 74 // Save files 75 for _, f := range c.Files { 76 n := filepath.Join(outdir, f.Name) 77 78 d := filepath.Dir(n) 79 if err := os.MkdirAll(d, 0755); err != nil { 80 return err 81 } 82 83 if err := ioutil.WriteFile(n, f.Data, 0755); err != nil { 84 return err 85 } 86 } 87 88 // Save dependencies 89 base := filepath.Join(outdir, ChartsDir) 90 for _, dep := range c.Dependencies() { 91 // Here, we write each dependency as a tar file. 92 if _, err := Save(dep, base); err != nil { 93 return err 94 } 95 } 96 return nil 97 } 98 99 // Save creates an archived chart to the given directory. 100 // 101 // This takes an existing chart and a destination directory. 102 // 103 // If the directory is /foo, and the chart is named bar, with version 1.0.0, this 104 // will generate /foo/bar-1.0.0.tgz. 105 // 106 // This returns the absolute path to the chart archive file. 107 func Save(c *chart.Chart, outDir string) (string, error) { 108 // Create archive 109 if fi, err := os.Stat(outDir); err != nil { 110 return "", err 111 } else if !fi.IsDir() { 112 return "", errors.Errorf("location %s is not a directory", outDir) 113 } 114 115 if err := c.Validate(); err != nil { 116 return "", errors.Wrap(err, "chart validation") 117 } 118 119 filename := fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version) 120 filename = filepath.Join(outDir, filename) 121 if stat, err := os.Stat(filepath.Dir(filename)); os.IsNotExist(err) { 122 if err := os.MkdirAll(filepath.Dir(filename), 0755); !os.IsExist(err) { 123 return "", err 124 } 125 } else if !stat.IsDir() { 126 return "", errors.Errorf("is not a directory: %s", filepath.Dir(filename)) 127 } 128 129 f, err := os.Create(filename) 130 if err != nil { 131 return "", err 132 } 133 134 // Wrap in gzip writer 135 zipper := gzip.NewWriter(f) 136 zipper.Header.Extra = headerBytes 137 zipper.Header.Comment = "Helm" 138 139 // Wrap in tar writer 140 twriter := tar.NewWriter(zipper) 141 rollback := false 142 defer func() { 143 twriter.Close() 144 zipper.Close() 145 f.Close() 146 if rollback { 147 os.Remove(filename) 148 } 149 }() 150 151 if err := writeTarContents(twriter, c, ""); err != nil { 152 rollback = true 153 } 154 return filename, err 155 } 156 157 func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { 158 base := filepath.Join(prefix, c.Name()) 159 160 // Save Chart.yaml 161 cdata, err := yaml.Marshal(c.Metadata) 162 if err != nil { 163 return err 164 } 165 if err := writeToTar(out, base+"/Chart.yaml", cdata); err != nil { 166 return err 167 } 168 169 // Save values.yaml 170 ydata, err := yaml.Marshal(c.Values) 171 if err != nil { 172 return err 173 } 174 if err := writeToTar(out, base+"/values.yaml", ydata); err != nil { 175 return err 176 } 177 178 // Save templates 179 for _, f := range c.Templates { 180 n := filepath.Join(base, f.Name) 181 if err := writeToTar(out, n, f.Data); err != nil { 182 return err 183 } 184 } 185 186 // Save files 187 for _, f := range c.Files { 188 n := filepath.Join(base, f.Name) 189 if err := writeToTar(out, n, f.Data); err != nil { 190 return err 191 } 192 } 193 194 // Save dependencies 195 for _, dep := range c.Dependencies() { 196 if err := writeTarContents(out, dep, base+"/charts"); err != nil { 197 return err 198 } 199 } 200 return nil 201 } 202 203 // writeToTar writes a single file to a tar archive. 204 func writeToTar(out *tar.Writer, name string, body []byte) error { 205 // TODO: Do we need to create dummy parent directory names if none exist? 206 h := &tar.Header{ 207 Name: name, 208 Mode: 0755, 209 Size: int64(len(body)), 210 } 211 if err := out.WriteHeader(h); err != nil { 212 return err 213 } 214 _, err := out.Write(body) 215 return err 216 }