Commit 7e317e82 authored by Matt Butcher's avatar Matt Butcher
Browse files

Merge pull request #63 from technosophos/feat/helm-lint

feat(helm): add a very basic lint command
parents 7142189c cf9c8ebe
main Release add-codeql dependabot/go_modules/github.com/docker/distribution-2.8.2incompatible dependabot/go_modules/github.com/lib/pq-1.10.9 dependabot/go_modules/github.com/rubenv/sql-migrate-1.4.0 dependabot/go_modules/golang.org/x/crypto-0.9.0 dependabot/go_modules/golang.org/x/term-0.8.0 dependabot/go_modules/k8s.io/klog/v2-2.100.1 dev-v2 feat-v3/event-emitter-lua kube-update-test release-2.0 release-2.1 release-2.10 release-2.11 release-2.12 release-2.13 release-2.14 release-2.15 release-2.16 release-2.17 release-2.2 release-2.3 release-2.4 release-2.5 release-2.6 release-2.7 release-2.8 release-2.9 release-3.0 release-3.1 release-3.10 release-3.11 release-3.12 release-3.2 release-3.3 release-3.4 release-3.5 release-3.6 release-3.6.1 release-3.6.2 release-3.7 release-3.8 release-3.9 release-v3.0.0-beta.4 v3.12.0 v3.12.0-rc.1 v3.12.0-dev.1 v3.11.3 v3.11.2 v3.11.1 v3.11.0 v3.11.0-rc.2 v3.11.0-rc.1 v3.10.3 v3.10.2 v3.10.1 v3.10.0 v3.10.0-rc.1 v3.9.4 v3.9.3 v3.9.2 v3.9.1 v3.9.0 v3.9.0-rc.1 v3.8.2 v3.8.1 v3.8.0 v3.8.0-rc.2 v3.8.0-rc.1 v3.7.2 v3.7.1 v3.7.0 v3.7.0-rc.3 v3.7.0-rc.2 v3.7.0-rc.1 v3.6.3 v3.6.2 v3.6.1 v3.6.0 v3.6.0-rc.1 v3.5.4 v3.5.3 v3.5.2 v3.5.1 v3.5.0 v3.5.0-rc.2 v3.5.0-rc.1 v3.4.2 v3.4.1 v3.4.0 v3.4.0-rc.1 v3.3.4 v3.3.3 v3.3.2 v3.3.1 v3.3.0 v3.3.0-rc.2 v3.3.0-rc.1 v3.2.4 v3.2.3 v3.2.2 v3.2.1 v3.2.0 v3.2.0-rc.1 v3.1.3 v3.1.2 v3.1.1 v3.1.0 v3.1.0-rc.3 v3.1.0-rc.2 v3.1.0-rc.1 v3.0.3 v3.0.2 v3.0.1 v3.0.0 v3.0.0-rc.4 v3.0.0-rc.3 v3.0.0-rc.2 v3.0.0-rc.1 v3.0.0-beta.5 v3.0.0-beta.4 v3.0.0-beta.3 v3.0.0-beta.2 v3.0.0-beta.1 v3.0.0-alpha.2 v3.0.0-alpha.1 v2.17.0 v2.17.0-rc.1 v2.16.12 v2.16.11 v2.16.10 v2.16.9 v2.16.8 v2.16.7 v2.16.6 v2.16.5 v2.16.4 v2.16.3 v2.16.2 v2.16.1 v2.16.0 v2.16.0-rc.2 v2.16.0-rc.1 v2.15.2 v2.15.1 v2.15.0 v2.15.0-rc.2 v2.15.0-rc.1 v2.14.3 v2.14.2 v2.14.1 v2.14.0 v2.14.0-rc.2 v2.14.0-rc.1 v2.13.1 v2.13.1-rc.1 v2.13.0 v2.13.0-rc.2 v2.13.0-rc.1 v2.12.3 v2.12.2 v2.12.1 v2.12.0 v2.12.0-rc.2 v2.12.0-rc.1 v2.11.0 v2.11.0-rc.4 v2.11.0-rc.3 v2.11.0-rc.2 v2.11.0-rc.1 v2.10.0 v2.10.0-rc.3 v2.10.0-rc.2 v2.10.0-rc.1 v2.9.1 v2.9.0 v2.9.0-rc5 v2.9.0-rc4 v2.9.0-rc3 v2.9.0-rc2 v2.9.0-rc1 v2.8.2 v2.8.2-rc1 v2.8.1 v2.8.0 v2.8.0-rc.1 v2.7.2 v2.7.1 v2.7.0 v2.7.0-rc1 v2.6.2 v2.6.1 v2.6.0 v2.5.1 v2.5.0 v2.4.2 v2.4.1 v2.4.0 v2.3.1 v2.3.0 v2.2.3 v2.2.2 v2.2.1 v2.2.0 v2.1.3 v2.1.2 v2.1.1 v2.1.0 v2.0.2 v2.0.1 v2.0.0 v2.0.0-rc.2 v2.0.0-rc.1 v2.0.0-beta.2 v2.0.0-beta.1 v2.0.0-alpha.5 v2.0.0-alpha.4 v2.0.0-alpha.3 v2.0.0-alpha.2 v2.0.0-alpha.1
No related merge requests found
Showing with 263 additions and 0 deletions
+263 -0
package main
import (
"fmt"
"github.com/deis/tiller/pkg/lint"
"github.com/spf13/cobra"
)
var longLintHelp = `
This command takes a path to a chart and runs a series of tests to verify that
the chart is well-formed.
If the linter encounters things that will cause the chart to fail installation,
it will emit [ERROR] messages. If it encounters issues that break with convention
or recommendation, it will emit [WARNING] messages.
`
var lintCommand = &cobra.Command{
Use: "lint [flags] PATH",
Short: "Examines a chart for possible issues",
Long: longLintHelp,
Run: lintCmd,
}
func init() {
RootCommand.AddCommand(lintCommand)
}
func lintCmd(cmd *cobra.Command, args []string) {
path := "."
if len(args) > 0 {
path = args[0]
}
issues := lint.All(path)
for _, i := range issues {
fmt.Printf("%s\n", i)
}
}
package lint
import (
"os"
"path/filepath"
chartutil "github.com/deis/tiller/pkg/chart"
)
func Chartfile(basepath string) (m []Message) {
m = []Message{}
path := filepath.Join(basepath, "Chart.yaml")
if fi, err := os.Stat(path); err != nil {
m = append(m, Message{Severity: ErrorSev, Text: "No Chart.yaml file"})
return
} else if fi.IsDir() {
m = append(m, Message{Severity: ErrorSev, Text: "Chart.yaml is a directory."})
return
}
cf, err := chartutil.LoadChartfile(path)
if err != nil {
m = append(m, Message{
Severity: ErrorSev,
Text: err.Error(),
})
return
}
if cf.Name == "" {
m = append(m, Message{
Severity: ErrorSev,
Text: "Chart.yaml: 'name' is required",
})
}
if cf.Version == "" || cf.Version == "0.0.0" {
m = append(m, Message{
Severity: ErrorSev,
Text: "Chart.yaml: 'version' is required, and must be greater than 0.0.0",
})
}
return
}
package lint
import (
"testing"
)
const badchartfile = "testdata/badchartfile"
func TestChartfile(t *testing.T) {
msgs := Chartfile(badchartfile)
if len(msgs) != 2 {
t.Errorf("Expected 2 errors, got %d", len(msgs))
}
if msgs[0].Text != "Chart.yaml: 'name' is required" {
t.Errorf("Unexpected message 0: %s", msgs[0].Text)
}
if msgs[1].Text != "Chart.yaml: 'version' is required, and must be greater than 0.0.0" {
t.Errorf("Unexpected message 1: %s", msgs[1].Text)
}
}
/*Package lint contains tools for linting charts.
Linting is the process of testing charts for errors or warnings regarding
formatting, compilation, or standards compliance.
*/
package lint
package lint
func All(basedir string) []Message {
out := Chartfile(basedir)
out = append(out, Templates(basedir)...)
return out
}
package lint
import "fmt"
type Severity int
const (
UnknownSev = iota
WarningSev
ErrorSev
)
var sev = []string{"INFO", "WARNING", "ERROR"}
type Message struct {
// Severity is one of the *Sev constants
Severity int
// Text contains the message text
Text string
}
// String prints a string representation of this Message.
//
// Implements fmt.Stringer.
func (m Message) String() string {
return fmt.Sprintf("[%s] %s", sev[m.Severity], m.Text)
}
package lint
import (
"fmt"
"testing"
)
var _ fmt.Stringer = Message{}
func TestMessage(t *testing.T) {
m := Message{ErrorSev, "Foo"}
if m.String() != "[ERROR] Foo" {
t.Errorf("Unexpected output: %s", m.String())
}
m = Message{WarningSev, "Bar"}
if m.String() != "[WARNING] Bar" {
t.Errorf("Unexpected output: %s", m.String())
}
}
package lint
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"text/template"
"github.com/Masterminds/sprig"
)
func Templates(basepath string) (messages []Message) {
messages = []Message{}
path := filepath.Join(basepath, "templates")
if fi, err := os.Stat(path); err != nil {
messages = append(messages, Message{Severity: WarningSev, Text: "No templates"})
return
} else if !fi.IsDir() {
messages = append(messages, Message{Severity: ErrorSev, Text: "'templates' is not a directory"})
return
}
tpl := template.New("tpl").Funcs(sprig.TxtFuncMap())
err := filepath.Walk(basepath, func(name string, fi os.FileInfo, e error) error {
// If an error is returned, we fail. Non-fatal errors should just be
// added directly to messages.
if e != nil {
return e
}
if fi.IsDir() {
return nil
}
data, err := ioutil.ReadFile(name)
if err != nil {
messages = append(messages, Message{
Severity: ErrorSev,
Text: fmt.Sprintf("cannot read %s: %s", name, err),
})
return nil
}
// An error rendering a file should emit a warning.
newtpl, err := tpl.Parse(string(data))
if err != nil {
messages = append(messages, Message{
Severity: ErrorSev,
Text: fmt.Sprintf("error processing %s: %s", name, err),
})
return nil
}
tpl = newtpl
return nil
})
if err != nil {
messages = append(messages, Message{Severity: ErrorSev, Text: err.Error()})
}
return
}
package lint
import (
"strings"
"testing"
)
const templateTestBasedir = "./testdata/albatross"
func TestTemplate(t *testing.T) {
res := Templates(templateTestBasedir)
if len(res) != 1 {
t.Fatalf("Expected one error, got %d", len(res))
}
if !strings.Contains(res[0].Text, "deliberateSyntaxError") {
t.Errorf("Unexpected error: %s", res[0])
}
}
name: albatross
description: testing chart
version: 199.44.12345-Alpha.1+cafe009
metadata:
name: {{.name | default "foo" | title}}
{{ deliberateSyntaxError }}
name = "mariner"
description: A Helm chart for Kubernetes
version: 0.0.0
home: ""
# Default values for badchartfile.
# This is a TOML-formatted file. https://github.com/toml-lang/toml
# Declare name/value pairs to be passed into your templates.
# name = "value"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment