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