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