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