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