github.com/canthefason/helm@v2.2.1-0.20170221172616-16b043b8d505+incompatible/cmd/helm/dependency.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package main 17 18 import ( 19 "fmt" 20 "io" 21 "os" 22 "path/filepath" 23 24 "github.com/gosuri/uitable" 25 "github.com/spf13/cobra" 26 27 "k8s.io/helm/pkg/chartutil" 28 ) 29 30 const dependencyDesc = ` 31 Manage the dependencies of a chart. 32 33 Helm charts store their dependencies in 'charts/'. For chart developers, it is 34 often easier to manage a single dependency file ('requirements.yaml') 35 which declares all dependencies. 36 37 The dependency commands operate on that file, making it easy to synchronize 38 between the desired dependencies and the actual dependencies stored in the 39 'charts/' directory. 40 41 A 'requirements.yaml' file is a YAML file in which developers can declare chart 42 dependencies, along with the location of the chart and the desired version. 43 For example, this requirements file declares two dependencies: 44 45 # requirements.yaml 46 dependencies: 47 - name: nginx 48 version: "1.2.3" 49 repository: "https://example.com/charts" 50 - name: memcached 51 version: "3.2.1" 52 repository: "https://another.example.com/charts" 53 54 The 'name' should be the name of a chart, where that name must match the name 55 in that chart's 'Chart.yaml' file. 56 57 The 'version' field should contain a semantic version or version range. 58 59 The 'repository' URL should point to a Chart Repository. Helm expects that by 60 appending '/index.yaml' to the URL, it should be able to retrieve the chart 61 repository's index. Note: 'repository' cannot be a repository alias. It must be 62 a URL. 63 ` 64 65 const dependencyListDesc = ` 66 List all of the dependencies declared in a chart. 67 68 This can take chart archives and chart directories as input. It will not alter 69 the contents of a chart. 70 71 This will produce an error if the chart cannot be loaded. It will emit a warning 72 if it cannot find a requirements.yaml. 73 ` 74 75 func newDependencyCmd(out io.Writer) *cobra.Command { 76 cmd := &cobra.Command{ 77 Use: "dependency update|build|list", 78 Aliases: []string{"dep", "dependencies"}, 79 Short: "manage a chart's dependencies", 80 Long: dependencyDesc, 81 } 82 83 cmd.AddCommand(newDependencyListCmd(out)) 84 cmd.AddCommand(newDependencyUpdateCmd(out)) 85 cmd.AddCommand(newDependencyBuildCmd(out)) 86 87 return cmd 88 } 89 90 type dependencyListCmd struct { 91 out io.Writer 92 chartpath string 93 } 94 95 func newDependencyListCmd(out io.Writer) *cobra.Command { 96 dlc := &dependencyListCmd{ 97 out: out, 98 } 99 cmd := &cobra.Command{ 100 Use: "list [flags] CHART", 101 Aliases: []string{"ls"}, 102 Short: "list the dependencies for the given chart", 103 Long: dependencyListDesc, 104 RunE: func(cmd *cobra.Command, args []string) error { 105 cp := "." 106 if len(args) > 0 { 107 cp = args[0] 108 } 109 110 var err error 111 dlc.chartpath, err = filepath.Abs(cp) 112 if err != nil { 113 return err 114 } 115 return dlc.run() 116 }, 117 } 118 return cmd 119 } 120 121 func (l *dependencyListCmd) run() error { 122 c, err := chartutil.Load(l.chartpath) 123 if err != nil { 124 return err 125 } 126 127 r, err := chartutil.LoadRequirements(c) 128 if err != nil { 129 if err == chartutil.ErrRequirementsNotFound { 130 fmt.Fprintf(l.out, "WARNING: no requirements at %s/charts\n", l.chartpath) 131 return nil 132 } 133 return err 134 } 135 136 l.printRequirements(r, l.out) 137 fmt.Fprintln(l.out) 138 l.printMissing(r, l.out) 139 return nil 140 } 141 142 func (l *dependencyListCmd) dependencyStatus(dep *chartutil.Dependency) string { 143 filename := fmt.Sprintf("%s-%s.tgz", dep.Name, dep.Version) 144 archive := filepath.Join(l.chartpath, "charts", filename) 145 if _, err := os.Stat(archive); err == nil { 146 c, err := chartutil.Load(archive) 147 if err != nil { 148 return "corrupt" 149 } 150 if c.Metadata.Name != dep.Name { 151 return "misnamed" 152 } 153 154 if c.Metadata.Version != dep.Version { 155 return "wrong version" 156 } 157 return "ok" 158 } 159 160 folder := filepath.Join(l.chartpath, "charts", dep.Name) 161 if fi, err := os.Stat(folder); err != nil { 162 return "missing" 163 } else if !fi.IsDir() { 164 return "mispackaged" 165 } 166 167 c, err := chartutil.Load(folder) 168 if err != nil { 169 return "corrupt" 170 } 171 172 if c.Metadata.Name != dep.Name { 173 return "misnamed" 174 } 175 176 if c.Metadata.Version != dep.Version { 177 return "wrong version" 178 } 179 180 return "unpacked" 181 } 182 183 // printRequirements prints all of the requirements in the yaml file. 184 func (l *dependencyListCmd) printRequirements(reqs *chartutil.Requirements, out io.Writer) { 185 table := uitable.New() 186 table.MaxColWidth = 80 187 table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS") 188 for _, row := range reqs.Dependencies { 189 table.AddRow(row.Name, row.Version, row.Repository, l.dependencyStatus(row)) 190 } 191 fmt.Fprintln(out, table) 192 } 193 194 // printMissing prints warnings about charts that are present on disk, but are not in the requirements. 195 func (l *dependencyListCmd) printMissing(reqs *chartutil.Requirements, out io.Writer) { 196 folder := filepath.Join(l.chartpath, "charts/*") 197 files, err := filepath.Glob(folder) 198 if err != nil { 199 fmt.Fprintln(l.out, err) 200 return 201 } 202 203 for _, f := range files { 204 fi, err := os.Stat(f) 205 if err != nil { 206 fmt.Fprintf(l.out, "Warning: %s\n", err) 207 } 208 // Skip anything that is not a directory and not a tgz file. 209 if !fi.IsDir() && filepath.Ext(f) != ".tgz" { 210 continue 211 } 212 c, err := chartutil.Load(f) 213 if err != nil { 214 fmt.Fprintf(l.out, "WARNING: %q is not a chart.\n", f) 215 continue 216 } 217 found := false 218 for _, d := range reqs.Dependencies { 219 if d.Name == c.Metadata.Name { 220 found = true 221 break 222 } 223 } 224 if !found { 225 fmt.Fprintf(l.out, "WARNING: %q is not in requirements.yaml.\n", f) 226 } 227 } 228 229 }