1
16
17 package action
18
19 import (
20 "fmt"
21 "io"
22 "os"
23 "path/filepath"
24 "strings"
25
26 "github.com/Masterminds/semver/v3"
27 "github.com/gosuri/uitable"
28
29 "helm.sh/helm/v3/pkg/chart"
30 "helm.sh/helm/v3/pkg/chart/loader"
31 )
32
33
34
35
36 type Dependency struct {
37 Verify bool
38 Keyring string
39 SkipRefresh bool
40 ColumnWidth uint
41 }
42
43
44 func NewDependency() *Dependency {
45 return &Dependency{
46 ColumnWidth: 80,
47 }
48 }
49
50
51 func (d *Dependency) List(chartpath string, out io.Writer) error {
52 c, err := loader.Load(chartpath)
53 if err != nil {
54 return err
55 }
56
57 if c.Metadata.Dependencies == nil {
58 fmt.Fprintf(out, "WARNING: no dependencies at %s\n", filepath.Join(chartpath, "charts"))
59 return nil
60 }
61
62 d.printDependencies(chartpath, out, c)
63 fmt.Fprintln(out)
64 d.printMissing(chartpath, out, c.Metadata.Dependencies)
65 return nil
66 }
67
68
69 func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string {
70 filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
71
72
73
74
75
76
77
78
79 switch archives, err := filepath.Glob(filepath.Join(chartpath, "charts", filename)); {
80 case err != nil:
81 return "bad pattern"
82 case len(archives) > 1:
83
84 found := []string{}
85 for _, arc := range archives {
86
87 filename = strings.TrimSuffix(filepath.Base(arc), ".tgz")
88 maybeVersion := strings.TrimPrefix(filename, fmt.Sprintf("%s-", dep.Name))
89
90 if _, err := semver.StrictNewVersion(maybeVersion); err == nil {
91
92
93 found = append(found, arc)
94 }
95 }
96
97 if l := len(found); l == 1 {
98
99 if r := statArchiveForStatus(found[0], dep); r != "" {
100 return r
101 }
102
103
104 } else if l > 1 {
105 return "too many matches"
106 }
107
108
109
110
111 case len(archives) == 1:
112 archive := archives[0]
113 if r := statArchiveForStatus(archive, dep); r != "" {
114 return r
115 }
116
117 }
118
119
120 var depChart *chart.Chart
121 for _, item := range parent.Dependencies() {
122 if item.Name() == dep.Name {
123 depChart = item
124 }
125 }
126
127 if depChart == nil {
128 return "missing"
129 }
130
131 if depChart.Metadata.Version != dep.Version {
132 constraint, err := semver.NewConstraint(dep.Version)
133 if err != nil {
134 return "invalid version"
135 }
136
137 v, err := semver.NewVersion(depChart.Metadata.Version)
138 if err != nil {
139 return "invalid version"
140 }
141
142 if !constraint.Check(v) {
143 return "wrong version"
144 }
145 }
146
147 return "unpacked"
148 }
149
150
151
152
153
154 func statArchiveForStatus(archive string, dep *chart.Dependency) string {
155 if _, err := os.Stat(archive); err == nil {
156 c, err := loader.Load(archive)
157 if err != nil {
158 return "corrupt"
159 }
160 if c.Name() != dep.Name {
161 return "misnamed"
162 }
163
164 if c.Metadata.Version != dep.Version {
165 constraint, err := semver.NewConstraint(dep.Version)
166 if err != nil {
167 return "invalid version"
168 }
169
170 v, err := semver.NewVersion(c.Metadata.Version)
171 if err != nil {
172 return "invalid version"
173 }
174
175 if !constraint.Check(v) {
176 return "wrong version"
177 }
178 }
179 return "ok"
180 }
181 return ""
182 }
183
184
185 func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) {
186 table := uitable.New()
187 table.MaxColWidth = d.ColumnWidth
188 table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
189 for _, row := range c.Metadata.Dependencies {
190 table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row, c))
191 }
192 fmt.Fprintln(out, table)
193 }
194
195
196
197 func (d *Dependency) printMissing(chartpath string, out io.Writer, reqs []*chart.Dependency) {
198 folder := filepath.Join(chartpath, "charts/*")
199 files, err := filepath.Glob(folder)
200 if err != nil {
201 fmt.Fprintln(out, err)
202 return
203 }
204
205 for _, f := range files {
206 fi, err := os.Stat(f)
207 if err != nil {
208 fmt.Fprintf(out, "Warning: %s\n", err)
209 }
210
211 if !fi.IsDir() && filepath.Ext(f) != ".tgz" {
212 continue
213 }
214 c, err := loader.Load(f)
215 if err != nil {
216 fmt.Fprintf(out, "WARNING: %q is not a chart.\n", f)
217 continue
218 }
219 found := false
220 for _, d := range reqs {
221 if d.Name == c.Name() {
222 found = true
223 break
224 }
225 }
226 if !found {
227 fmt.Fprintf(out, "WARNING: %q is not in Chart.yaml.\n", f)
228 }
229 }
230 }
231
View as plain text