forked from helm/helm-classic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lint.go
170 lines (133 loc) · 4.87 KB
/
lint.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package action
import (
"io"
"os"
"path/filepath"
"regexp"
"github.com/google/go-github/github"
"github.com/helm/helm/log"
"github.com/helm/helm/manifest"
"github.com/helm/helm/util"
"github.com/helm/helm/validation"
)
const (
// Owner is default Helm repository owner or organization.
Owner = "helm"
// Project is the default Charts repository name.
Project = "charts"
// Maximum length of Metadata.name allowed by kubernetes
MaxMetadataNameLength = 24
)
// RepoService is a GitHub client instance.
var RepoService GHRepoService
// GHRepoService is a restricted interface to GitHub client operations.
type GHRepoService interface {
DownloadContents(string, string, string, *github.RepositoryContentGetOptions) (io.ReadCloser, error)
}
// LintAll vlaidates all charts are well-formed
//
// - homedir is the home directory for the user
func LintAll(homedir string) {
md := util.WorkspaceChartDirectory(homedir, "*")
chartPaths, err := filepath.Glob(md)
if err != nil {
log.Warn("Could not find any charts in %q: %s", md, err)
}
if len(chartPaths) == 0 {
log.Warn("Could not find any charts in %q", md)
} else {
for _, chartPath := range chartPaths {
Lint(chartPath)
}
}
}
// Lint validates that a chart is well-formed
//
// - chartPath path to chart directory
func Lint(chartPath string) {
cv := new(validation.ChartValidation)
chartPresenceValidation := cv.AddError("Chart found at "+chartPath, func(path string, v *validation.Validation) bool {
stat, err := os.Stat(chartPath)
cv.Path = chartPath
return err == nil && stat.Mode().IsDir()
})
chartYamlPresenceValidation := chartPresenceValidation.AddError("Chart.yaml is present", func(path string, v *validation.Validation) bool {
stat, err := os.Stat(v.ChartYamlPath())
return err == nil && stat.Mode().IsRegular()
})
chartYamlValidation := chartYamlPresenceValidation.AddError("Chart.yaml is valid yaml", func(path string, v *validation.Validation) bool {
chartfile, err := v.Chartfile()
if err == nil {
cv.Chartfile = chartfile
}
return err == nil
})
chartYamlNameValidation := chartYamlValidation.AddError("Chart.yaml has a name field", func(path string, v *validation.Validation) bool {
return cv.Chartfile.Name != ""
})
chartYamlNameValidation.AddError("Name declared in Chart.yaml is the same as directory name.", func(path string, v *validation.Validation) bool {
return cv.Chartfile.Name == cv.ChartName()
})
chartYamlValidation.AddError("Chart.yaml has a version field", func(path string, v *validation.Validation) bool {
return cv.Chartfile.Version != ""
})
chartYamlValidation.AddWarning("Chart.yaml has a description field", func(path string, v *validation.Validation) bool {
return cv.Chartfile.Description != ""
})
chartYamlValidation.AddWarning("Chart.yaml has a maintainers field", func(path string, v *validation.Validation) bool {
return cv.Chartfile.Maintainers != nil
})
chartPresenceValidation.AddWarning("README.md is present and not empty", func(path string, v *validation.Validation) bool {
readmePath := filepath.Join(path, "README.md")
stat, err := os.Stat(readmePath)
return err == nil && stat.Mode().IsRegular() && stat.Size() > 0
})
manifestsValidation := chartPresenceValidation.AddError("Manifests directory is present", func(path string, v *validation.Validation) bool {
stat, err := os.Stat(v.ChartManifestsPath())
return err == nil && stat.Mode().IsDir()
})
manifestsParsingValidation := manifestsValidation.AddError("Manifests are valid yaml", func(path string, v *validation.Validation) bool {
manifests, err := manifest.ParseDir(cv.Path)
if err == nil {
cv.Manifests = manifests
}
return err == nil && cv.Manifests != nil
})
manifestsParsingValidation.AddWarning("Manifests have correct and valid metadata", func(path string, v *validation.Validation) bool {
success := true
validKinds := InstallOrder
for _, m := range cv.Manifests {
meta, _ := m.VersionedObject.Meta()
if meta.Name == "" || len(meta.Name) > MaxMetadataNameLength {
success = false
}
if match, _ := regexp.MatchString(`[a-z]([-a-z0-9]*[a-z0-9])?`, meta.Name); !match {
success = false
}
val, ok := meta.Labels["heritage"]
if !ok || (val != "helm") {
success = false
}
kind := meta.Kind
validManifestKind := false
for _, validKind := range validKinds {
if kind == validKind {
validManifestKind = true
}
}
if validManifestKind == false {
success = false
}
}
return success
})
if cv.Valid() {
log.Info("Chart [%s] has passed all necessary checks", cv.ChartName())
} else {
if cv.ErrorCount > 0 {
log.Err("Chart [%s] has failed some necessary checks. Check out the error and warning messages listed.", cv.ChartName())
} else {
log.Warn("Chart [%s] has passed all necessary checks but failed some checks as well. Proceed with caution. Check out the warnings listed.", cv.ChartName())
}
}
}