github.com/danielqsj/helm@v2.0.0-alpha.4.0.20160908204436-976e0ba5199b+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 "os" 25 "path/filepath" 26 27 "github.com/ghodss/yaml" 28 29 "k8s.io/helm/pkg/proto/hapi/chart" 30 ) 31 32 var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") 33 34 // Save creates an archived chart to the given directory. 35 // 36 // This takes an existing chart and a destination directory. 37 // 38 // If the directory is /foo, and the chart is named bar, with version 1.0.0, this 39 // will generate /foo/bar-1.0.0.tgz. 40 // 41 // This returns the absolute path to the chart archive file. 42 func Save(c *chart.Chart, outDir string) (string, error) { 43 // Create archive 44 if fi, err := os.Stat(outDir); err != nil { 45 return "", err 46 } else if !fi.IsDir() { 47 return "", fmt.Errorf("location %s is not a directory", outDir) 48 } 49 50 if c.Metadata == nil { 51 return "", errors.New("no Chart.yaml data") 52 } 53 54 cfile := c.Metadata 55 if cfile.Name == "" { 56 return "", errors.New("no chart name specified (Chart.yaml)") 57 } else if cfile.Version == "" { 58 return "", errors.New("no chart version specified (Chart.yaml)") 59 } 60 61 filename := fmt.Sprintf("%s-%s.tgz", cfile.Name, cfile.Version) 62 filename = filepath.Join(outDir, filename) 63 f, err := os.Create(filename) 64 if err != nil { 65 return "", err 66 } 67 68 // Wrap in gzip writer 69 zipper := gzip.NewWriter(f) 70 zipper.Header.Extra = headerBytes 71 zipper.Header.Comment = "Helm" 72 73 // Wrap in tar writer 74 twriter := tar.NewWriter(zipper) 75 rollback := false 76 defer func() { 77 twriter.Close() 78 zipper.Close() 79 f.Close() 80 if rollback { 81 os.Remove(filename) 82 } 83 }() 84 85 if err := writeTarContents(twriter, c, ""); err != nil { 86 rollback = true 87 } 88 return filename, err 89 } 90 91 func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { 92 base := filepath.Join(prefix, c.Metadata.Name) 93 94 // Save Chart.yaml 95 cdata, err := yaml.Marshal(c.Metadata) 96 if err != nil { 97 return err 98 } 99 if err := writeToTar(out, base+"/Chart.yaml", cdata); err != nil { 100 return err 101 } 102 103 // Save values.yaml 104 if c.Values != nil && len(c.Values.Raw) > 0 { 105 if err := writeToTar(out, base+"/values.yaml", []byte(c.Values.Raw)); err != nil { 106 return err 107 } 108 } 109 110 // Save templates 111 for _, f := range c.Templates { 112 n := filepath.Join(base, f.Name) 113 if err := writeToTar(out, n, f.Data); err != nil { 114 return err 115 } 116 } 117 118 // Save files 119 for _, f := range c.Files { 120 n := filepath.Join(base, f.TypeUrl) 121 if err := writeToTar(out, n, f.Value); err != nil { 122 return err 123 } 124 } 125 126 // Save dependencies 127 for _, dep := range c.Dependencies { 128 if err := writeTarContents(out, dep, base+"/charts"); err != nil { 129 return err 130 } 131 } 132 return nil 133 } 134 135 // writeToTar writes a single file to a tar archive. 136 func writeToTar(out *tar.Writer, name string, body []byte) error { 137 // TODO: Do we need to create dummy parent directory names if none exist? 138 h := &tar.Header{ 139 Name: name, 140 Mode: 0755, 141 Size: int64(len(body)), 142 } 143 if err := out.WriteHeader(h); err != nil { 144 return err 145 } 146 if _, err := out.Write(body); err != nil { 147 return err 148 } 149 return nil 150 }