mirror of https://github.com/helm/helm
See HIP-0019 proposal at helm/community: https://github.com/helm/community/pull/353 Co-authored-by: Danilo Patrucco <danilo.patrucco@gmail.com> Signed-off-by: Daniel J. Pritchett <dpritchett@radiusmethod.com>pull/13257/head
parent
c86e0d3ad9
commit
f93c2554ad
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
//Package ignore
|
||||
/*
|
||||
Package ignore contains tools for linting charts.
|
||||
|
||||
Linting is the process of testing charts for errors or warnings regarding
|
||||
formatting, compilation, or standards compliance.
|
||||
*/
|
||||
package ignore // import "helm.sh/helm/v3/pkg/lint/ignore"
|
@ -0,0 +1,62 @@
|
||||
package ignore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"helm.sh/helm/v3/pkg/lint/support"
|
||||
"log"
|
||||
)
|
||||
|
||||
// DefaultIgnoreFileName is the name of the lint ignore file
|
||||
const DefaultIgnoreFileName = ".helmlintignore"
|
||||
|
||||
var debugFn func(format string, v ...interface{})
|
||||
|
||||
type Ignorer struct {
|
||||
ChartPath string
|
||||
Matchers []MatchesErrors
|
||||
}
|
||||
|
||||
func NewIgnorer(chartPath string, lintIgnorePath string, debugLogFn func(string, ...interface{})) (*Ignorer, error) {
|
||||
debugFn = debugLogFn
|
||||
matchers, err := LoadFromFilePath(chartPath, lintIgnorePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Ignorer{ChartPath: chartPath, Matchers: matchers}, nil
|
||||
}
|
||||
|
||||
// FilterMessages Verify what messages can be kept in the output, using also the error as a verification
|
||||
func (i *Ignorer) FilterMessages(messages []support.Message) []support.Message {
|
||||
out := make([]support.Message, 0, len(messages))
|
||||
for _, msg := range messages {
|
||||
if i.ShouldKeepMessage(msg) {
|
||||
out = append(out, msg)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (i *Ignorer) ShouldKeepMessage(msg support.Message) bool {
|
||||
return i.ShouldKeepError(msg.Err)
|
||||
}
|
||||
|
||||
// ShouldKeepError is used to verify if the error associated with the message need to be kept, or it can be ignored, called by FilterMessages and in the pkg/action/lint.go Run main function
|
||||
func (i *Ignorer) ShouldKeepError(err error) bool {
|
||||
// if any of our Matchers match the rule, we can discard it
|
||||
for _, rule := range i.Matchers {
|
||||
if rule.Match(err) {
|
||||
debug("lint ignore rule matched this error, we should suppress it.", "errText", err.Error(), rule.LogAttrs())
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// if we can't find a reason to discard it, we keep it
|
||||
debug("no lint ignore rules matched this error, we should NOT suppress it.", "errText", err.Error())
|
||||
return true
|
||||
}
|
||||
|
||||
var defaultDebugFn = func(format string, v ...interface{}) {
|
||||
format = fmt.Sprintf("[debug] %s\n", format)
|
||||
_ = log.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package ignore
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const pathlessRulePrefix = "error_lint_ignore="
|
||||
|
||||
type MatchesErrors interface {
|
||||
Match(error) bool
|
||||
LogAttrs() slog.Attr
|
||||
}
|
||||
|
||||
type BadTemplateRule struct {
|
||||
RuleText string
|
||||
MessageText string
|
||||
BadTemplatePath string
|
||||
}
|
||||
|
||||
type PathlessRule struct {
|
||||
RuleText string
|
||||
MessageText string
|
||||
}
|
||||
|
||||
// Match errors that have no file path in their body with ignorer rules.
|
||||
// An examples of errors with no file path in their body is chart metadata errors `chart metadata is missing these dependencies`
|
||||
func (pr PathlessRule) Match(err error) bool {
|
||||
errText := err.Error()
|
||||
matchableParts := strings.SplitN(pr.MessageText, ":", 2)
|
||||
matchablePrefix := strings.TrimSpace(matchableParts[0])
|
||||
|
||||
if match, _ := filepath.Match(pr.MessageText, errText); match {
|
||||
return true
|
||||
}
|
||||
if matched, _ := filepath.Match(matchablePrefix, errText); matched {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// LogAttrs Used for troubleshooting and gathering data
|
||||
func (pr PathlessRule) LogAttrs() slog.Attr {
|
||||
return slog.Group("BadTemplateRule",
|
||||
slog.String("rule_text", pr.RuleText),
|
||||
slog.String("value", pr.MessageText),
|
||||
)
|
||||
}
|
||||
|
||||
// LogAttrs Used for troubleshooting and gathering data
|
||||
func (btr BadTemplateRule) LogAttrs() slog.Attr {
|
||||
return slog.Group("BadTemplateRule",
|
||||
slog.String("rule_text", btr.RuleText),
|
||||
slog.String("key", btr.BadTemplatePath),
|
||||
slog.String("value", btr.MessageText),
|
||||
)
|
||||
}
|
||||
|
||||
// Match errors that have a file path in their body with ignorer rules.
|
||||
func (btr BadTemplateRule) Match(err error) bool {
|
||||
errText := err.Error()
|
||||
pathToOffendingFile, err := pathToOffendingFile(errText)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
cleanRulePath := filepath.Clean(btr.BadTemplatePath)
|
||||
|
||||
if strings.Contains(pathToOffendingFile, cleanRulePath) {
|
||||
if strings.Contains(errText, btr.MessageText) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
package ignore
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func LoadFromFilePath(chartPath, ignoreFilePath string) ([]MatchesErrors, error) {
|
||||
if ignoreFilePath == "" {
|
||||
ignoreFilePath = filepath.Join(chartPath, DefaultIgnoreFileName)
|
||||
debug("\nNo helm lint ignore filepath specified, will try and use the following default: %s\n", ignoreFilePath)
|
||||
}
|
||||
|
||||
// attempt to load ignore patterns from ignoreFilePath.
|
||||
// if none are found, return an empty ignorer so the program can keep running.
|
||||
debug("\nTrying to load helm lint ignore file at %s\n", ignoreFilePath)
|
||||
file, err := os.Open(ignoreFilePath)
|
||||
if err != nil {
|
||||
debug("failed to open helm lint ignore file: %s", ignoreFilePath)
|
||||
return []MatchesErrors{}, nil
|
||||
}
|
||||
defer func() {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
debug("failed to close helm lint ignore file: %s", ignoreFilePath)
|
||||
}
|
||||
}()
|
||||
|
||||
matchers := LoadFromReader(file)
|
||||
return matchers, nil
|
||||
}
|
||||
|
||||
// debug provides [pkg/lint/ignore] with a runtime-overridable logging function
|
||||
// intended to match the behavior of the top level debug() method from package main.
|
||||
//
|
||||
// When no debugFn is set for the package at runtime then debug will fall back to
|
||||
// defaultDebugFn.
|
||||
func debug(format string, args ...interface{}) {
|
||||
if debugFn == nil {
|
||||
defaultDebugFn(format, args...)
|
||||
} else {
|
||||
debugFn(format, args...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: figure out & fix or remove
|
||||
func pathToOffendingFile(errText string) (string, error) {
|
||||
delimiter := ":"
|
||||
// splits into N parts delimited by colons
|
||||
parts := strings.Split(errText, delimiter)
|
||||
// if 3 or more parts, return the second part, after trimming its spaces
|
||||
if len(parts) > 2 {
|
||||
return strings.TrimSpace(parts[1]), nil
|
||||
}
|
||||
// if fewer than 3 parts, return empty string
|
||||
return "", fmt.Errorf("fewer than three [%s]-delimited parts found, no path here: %s", delimiter, errText)
|
||||
}
|
||||
|
||||
func LoadFromReader(rdr io.Reader) []MatchesErrors {
|
||||
matchers := make([]MatchesErrors, 0)
|
||||
|
||||
scanner := bufio.NewScanner(rdr)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, pathlessRulePrefix) {
|
||||
matchers = append(matchers, buildPathlessRule(line, pathlessRulePrefix))
|
||||
} else {
|
||||
matchers = append(matchers, buildBadTemplateRule(line))
|
||||
}
|
||||
}
|
||||
|
||||
return matchers
|
||||
}
|
||||
|
||||
func buildPathlessRule(line string, pathlessRulePrefix string) PathlessRule {
|
||||
// handle chart-level errors
|
||||
// Drop 'error_lint_ignore=' prefix from rule before saving it
|
||||
const numSplits = 2
|
||||
tokens := strings.SplitN(line[len(pathlessRulePrefix):], pathlessRulePrefix, numSplits)
|
||||
if len(tokens) == numSplits {
|
||||
// TODO: find an example for this one - not sure we still use it
|
||||
messageText, _ := tokens[0], tokens[1]
|
||||
return PathlessRule{RuleText: line, MessageText: messageText}
|
||||
} else {
|
||||
messageText := tokens[0]
|
||||
return PathlessRule{RuleText: line, MessageText: messageText}
|
||||
}
|
||||
}
|
||||
|
||||
func buildBadTemplateRule(line string) BadTemplateRule {
|
||||
const noMessageText = ""
|
||||
const separator = " "
|
||||
const numSplits = 2
|
||||
|
||||
// handle chart yaml file errors in specific template files
|
||||
parts := strings.SplitN(line, separator, numSplits)
|
||||
if len(parts) == numSplits {
|
||||
messagePath, messageText := parts[0], parts[1]
|
||||
return BadTemplateRule{RuleText: line, BadTemplatePath: messagePath, MessageText: messageText}
|
||||
} else {
|
||||
messagePath := parts[0]
|
||||
return BadTemplateRule{RuleText: line, BadTemplatePath: messagePath, MessageText: noMessageText}
|
||||
}
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
package ignore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"helm.sh/helm/v3/pkg/lint/support"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRule_ShouldKeepMessage(t *testing.T) {
|
||||
type testCase struct {
|
||||
Scenario string
|
||||
RuleText string
|
||||
Ignorables []support.Message
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
Scenario: "subchart template not defined",
|
||||
RuleText: "gitlab/charts/webservice/templates/tests/tests.yaml <{{template \"fullname\" .}}>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: gitlab/charts/webservice/templates/tests/tests.yaml:5:20: executing \"gitlab/charts/webservice/templates/tests/tests.yaml\" at <{{template \"fullname\" .}}>: template \"fullname\" not defined"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "subchart template include template not found",
|
||||
RuleText: "gitaly/templates/statefulset.yml <include \"gitlab.gitaly.includeInternalResources\" $>\n",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: gitaly/templates/statefulset.yml:1:11: executing \"gitaly/templates/statefulset.yml\" at <include \"gitlab.gitaly.includeInternalResources\" $>: error calling include: template: no template \"gitlab.gitaly.includeInternalResources\" associated with template \"gotpl\""),
|
||||
}},
|
||||
},
|
||||
{
|
||||
Scenario: "subchart template evaluation has a nil pointer",
|
||||
RuleText: "gitlab-exporter/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>\n",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: gitlab-exporter/templates/serviceaccount.yaml:1:57: executing \"gitlab-exporter/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "webservice path only",
|
||||
RuleText: "webservice/templates/tests/tests.yaml",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: webservice/templates/tests/tests.yaml:8:8: executing \"webservice/templates/tests/tests.yaml\" at <include \"gitlab.standardLabels\" .>: error calling include: template: no template \"gitlab.standardLabels\" associated with template \"gotpl\""),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "geo-logcursor path only",
|
||||
RuleText: "geo-logcursor/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: geo-logcursor/templates/serviceaccount.yaml:1:57: executing \"geo-logcursor/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "webservice path only",
|
||||
RuleText: "webservice/templates/service.yaml <include \"fullname\" .>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: gitlab/charts/gitlab/charts/webservice/templates/service.yaml:14:11: executing \"gitlab/charts/gitlab/charts/webservice/templates/service.yaml\" at <include \"fullname\" .>: error calling include: template: gitlab/templates/_helpers.tpl:14:27: executing \"fullname\" at <.Chart.Name>: nil pointer evaluating interface {}.Name"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "certmanager-issuer path only",
|
||||
RuleText: "certmanager-issuer/templates/rbac-config.yaml <.Values.global.ingress>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: certmanager-issuer/templates/rbac-config.yaml:1:67: executing \"certmanager-issuer/templates/rbac-config.yaml\" at <.Values.global.ingress>: nil pointer evaluating interface {}.ingress"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "gitlab-pages path only",
|
||||
RuleText: "gitlab-pages/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: gitlab-pages/templates/serviceaccount.yaml:1:57: executing \"gitlab-pages/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "gitlab-shell path only",
|
||||
RuleText: "gitlab-shell/templates/traefik-tcp-ingressroute.yaml <.Values.global.ingress.provider>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: gitlab-shell/templates/traefik-tcp-ingressroute.yaml:2:17: executing \"gitlab-shell/templates/traefik-tcp-ingressroute.yaml\" at <.Values.global.ingress.provider>: nil pointer evaluating interface {}.provider"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "kas path only",
|
||||
RuleText: "kas/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: kas/templates/serviceaccount.yaml:1:57: executing \"kas/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "kas path only",
|
||||
RuleText: "kas/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: kas/templates/serviceaccount.yaml:1:57: executing \"kas/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "mailroom path only",
|
||||
RuleText: "mailroom/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: mailroom/templates/serviceaccount.yaml:1:57: executing \"mailroom/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "migrations path only",
|
||||
RuleText: "migrations/templates/job.yaml <include (print $.Template.BasePath \"/_serviceaccountspec.yaml\") .>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: migrations/templates/job.yaml:2:3: executing \"migrations/templates/job.yaml\" at <include (print $.Template.BasePath \"/_serviceaccountspec.yaml\") .>: error calling include: template: migrations/templates/_serviceaccountspec.yaml:1:57: executing \"migrations/templates/_serviceaccountspec.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "praefect path only",
|
||||
RuleText: "praefect/templates/statefulset.yaml <.Values.global.image>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: praefect/templates/statefulset.yaml:1:38: executing \"praefect/templates/statefulset.yaml\" at <.Values.global.image>: nil pointer evaluating interface {}.image"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "sidekiq path only",
|
||||
RuleText: "sidekiq/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: sidekiq/templates/serviceaccount.yaml:1:57: executing \"sidekiq/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "spamcheck path only",
|
||||
RuleText: "spamcheck/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: spamcheck/templates/serviceaccount.yaml:1:57: executing \"spamcheck/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.serviceAccount"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "toolbox path only",
|
||||
RuleText: "toolbox/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: toolbox/templates/serviceaccount.yaml:1:57: executing \"toolbox/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "minio path only",
|
||||
RuleText: "minio/templates/pdb.yaml <{{template \"gitlab.pdb.apiVersion\" $pdbCfg}}>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: minio/templates/pdb.yaml:3:24: executing \"minio/templates/pdb.yaml\" at <{{template \"gitlab.pdb.apiVersion\" $pdbCfg}}>: template \"gitlab.pdb.apiVersion\" not defined"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "nginx-ingress path only",
|
||||
RuleText: "nginx-ingress/templates/admission-webhooks/job-patch/serviceaccount.yaml <.Values.admissionWebhooks.serviceAccount.automountServiceAccountToken>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: nginx-ingress/templates/admission-webhooks/job-patch/serviceaccount.yaml:13:40: executing \"nginx-ingress/templates/admission-webhooks/job-patch/serviceaccount.yaml\" at <.Values.admissionWebhooks.serviceAccount.automountServiceAccountToken>: nil pointer evaluating interface {}.serviceAccount"),
|
||||
}},
|
||||
}, {
|
||||
Scenario: "registry path only",
|
||||
RuleText: "registry/templates/serviceaccount.yaml <.Values.global.serviceAccount.enabled>",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "templates/",
|
||||
Err: fmt.Errorf("template: registry/templates/serviceaccount.yaml:1:57: executing \"registry/templates/serviceaccount.yaml\" at <.Values.global.serviceAccount.enabled>: nil pointer evaluating interface {}.enabled"),
|
||||
}},
|
||||
},
|
||||
|
||||
{
|
||||
Scenario: "subchart metadata missing dependencies",
|
||||
RuleText: "error_lint_ignore=chart metadata is missing these dependencies**",
|
||||
Ignorables: []support.Message{
|
||||
{
|
||||
Path: "gitlab/chart/charts/gitlab",
|
||||
Err: fmt.Errorf("chart metadata is missing these dependencies: sidekiq,spamcheck,gitaly,gitlab-shell,kas,mailroom,migrations,toolbox,geo-logcursor,gitlab-exporter,webservice"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Scenario: "subchart icon is recommended",
|
||||
RuleText: "error_lint_ignore=icon is recommended",
|
||||
Ignorables: []support.Message{{
|
||||
Path: "Chart.yaml",
|
||||
Err: fmt.Errorf("icon is recommended"),
|
||||
}},
|
||||
},
|
||||
{
|
||||
Scenario: "subchart values file does not exist",
|
||||
RuleText: "error_lint_ignore=file does not exist",
|
||||
Ignorables: []support.Message{
|
||||
{Path: "values.yaml", Err: fmt.Errorf("file does not exist")},
|
||||
},
|
||||
},
|
||||
{
|
||||
Scenario: "Chart.yaml missing apiVersion",
|
||||
RuleText: "error_lint_ignore=apiVersion is required. The value must be either \"v1\" or \"v2\"",
|
||||
Ignorables: []support.Message{
|
||||
{
|
||||
Severity: support.ErrorSev,
|
||||
Path: "Chart.yaml",
|
||||
Err: fmt.Errorf("apiVersion is required. The value must be either \"v1\" or \"v2\""),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Scenario: "values.yaml does not exist",
|
||||
RuleText: "error_lint_ignore=file does not exist",
|
||||
Ignorables: []support.Message{
|
||||
{
|
||||
Severity: support.InfoSev,
|
||||
Path: "values.yaml",
|
||||
Err: fmt.Errorf("file does not exist"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Scenario: "missing dependencies in chart directory",
|
||||
RuleText: "error_lint_ignore=chart directory is missing these dependencies: mariadb",
|
||||
Ignorables: []support.Message{
|
||||
{
|
||||
Severity: support.WarningSev,
|
||||
Path: "/Users/daniel/radius/bb/helm/pkg/action/testdata/charts/chart-missing-deps-but-ignorable",
|
||||
Err: fmt.Errorf("chart directory is missing these dependencies: mariadb"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.Scenario, func(t *testing.T) {
|
||||
matchers := LoadFromReader(strings.NewReader(testCase.RuleText))
|
||||
if len(matchers) == 0 {
|
||||
t.Fatal("Expected a match, got none.", testCase.Scenario)
|
||||
}
|
||||
assert.Equal(t, 1, len(matchers))
|
||||
matcher := matchers[0]
|
||||
|
||||
for _, ignorableMessage := range testCase.Ignorables {
|
||||
got := matcher.Match(ignorableMessage.Err)
|
||||
assert.True(t, got, testCase.Scenario)
|
||||
}
|
||||
|
||||
keepableMessage := support.NewMessage(support.ErrorSev, "wow/", fmt.Errorf("incredible: something just happened"))
|
||||
got := matcher.Match(keepableMessage.Err)
|
||||
assert.False(t, got, testCase.Scenario)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in new issue