pull/31169/merge
Thiago Perrotta 3 weeks ago committed by GitHub
commit 983488f17d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -160,7 +160,11 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
if yamlStruct != nil { if yamlStruct != nil {
// NOTE: set to warnings to allow users to support out-of-date kubernetes // NOTE: set to warnings to allow users to support out-of-date kubernetes
// Refs https://github.com/helm/helm/issues/8596 // Refs https://github.com/helm/helm/issues/8596
// Skip metadata name validation for List resources as they don't require meaningful names
// Refs https://github.com/helm/helm/issues/13192
if !isListResource(yamlStruct, renderedContent) {
linter.RunLinterRule(support.WarningSev, fpath, validateMetadataName(yamlStruct)) linter.RunLinterRule(support.WarningSev, fpath, validateMetadataName(yamlStruct))
}
linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct, kubeVersion)) linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct, kubeVersion))
linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(yamlStruct, renderedContent)) linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(yamlStruct, renderedContent))
@ -235,6 +239,33 @@ func validateYamlContent(err error) error {
return nil return nil
} }
// isListResource returns true if the resource is a Kubernetes List type
// (e.g. ConfigMapList, SecretList). It checks both that the Kind ends with
// "List" and that the resource has an "items" field that is an array.
// This avoids incorrectly treating custom resources that happen to end with
// "List" but aren't actually list containers.
func isListResource(obj *k8sYamlStruct, manifest string) bool {
if !strings.HasSuffix(obj.Kind, "List") {
return false
}
// For the special case of Kind == "List", we can be sure it's a list
if obj.Kind == "List" {
return true
}
// For other kinds ending with "List", verify they have an items field
var listStruct struct {
Items []interface{} `json:"items"`
}
if err := yaml.Unmarshal([]byte(manifest), &listStruct); err != nil {
return false
}
// Check if the items field exists and is an array (even if empty)
return listStruct.Items != nil
}
// validateMetadataName uses the correct validation function for the object // validateMetadataName uses the correct validation function for the object
// Kind, or if not set, defaults to the standard definition of a subdomain in // Kind, or if not set, defaults to the standard definition of a subdomain in
// DNS (RFC 1123), used by most resources. // DNS (RFC 1123), used by most resources.

@ -438,3 +438,98 @@ items:
t.Fatalf("List objects keep annotations should pass. got: %s", err) t.Fatalf("List objects keep annotations should pass. got: %s", err)
} }
} }
func TestIsListResource(t *testing.T) {
tests := []struct {
kind string
manifest string
expected bool
}{
{"ConfigMap", `apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
key: value`, false},
{"ConfigMapList", `apiVersion: v1
kind: ConfigMapList
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: test1
- apiVersion: v1
kind: ConfigMap
metadata:
name: test2`, true},
{"Secret", `apiVersion: v1
kind: Secret
metadata:
name: test
data:
key: value`, false},
{"SecretList", `apiVersion: v1
kind: SecretList
items:
- apiVersion: v1
kind: Secret
metadata:
name: test1
- apiVersion: v1
kind: Secret
metadata:
name: test2`, true},
{"List", `apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: test`, true},
{"", `apiVersion: v1
kind: ""
metadata:
name: test`, false},
{"SomethingListExtra", `apiVersion: v1
kind: SomethingListExtra
metadata:
name: test`, false},
// Test case for a custom resource ending in "List" but without items field (should be false)
{"AccessList", `apiVersion: example.com/v1
kind: AccessList
metadata:
name: test
spec:
rules: []`, false},
}
for _, test := range tests {
t.Run(test.kind, func(t *testing.T) {
obj := &k8sYamlStruct{Kind: test.kind}
result := isListResource(obj, test.manifest)
if result != test.expected {
t.Errorf("isListResource(%q) = %v, expected %v", test.kind, result, test.expected)
}
})
}
}
func TestConfigMapListLinting(t *testing.T) {
linter := support.Linter{ChartDir: "./testdata/configmaplist-chart"}
Templates(&linter, values, namespace, strict)
// The ConfigMapList should not generate any lint warnings about metadata.name
for _, msg := range linter.Messages {
if strings.Contains(msg.Err.Error(), "object name does not conform to Kubernetes naming requirements") {
t.Errorf("ConfigMapList should not generate metadata name validation errors, but got: %v", msg.Err)
}
}
// Should have no errors or warnings
if len(linter.Messages) > 0 {
t.Errorf("Expected no lint messages for ConfigMapList, got %d messages:", len(linter.Messages))
for i, msg := range linter.Messages {
t.Logf("Message %d: %s", i, msg)
}
}
}

@ -0,0 +1,6 @@
apiVersion: v2
name: configmaplist-chart
description: Test chart with ConfigMapList
type: application
version: "0.1.0"
appVersion: "1.0"

@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-single-config
namespace: {{ .Release.Namespace }}
data:
setting: "value"

@ -0,0 +1,21 @@
apiVersion: v1
kind: ConfigMapList
metadata:
# ConfigMapList objects don't require meaningful names
# This should not trigger lint warnings
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-database-config
namespace: {{ .Release.Namespace }}
data:
host: {{ .Values.configs.database.host }}
port: "{{ .Values.configs.database.port }}"
- apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-cache-config
namespace: {{ .Release.Namespace }}
data:
redis-url: {{ .Values.configs.cache.redis }}

@ -0,0 +1,7 @@
# Default values for configmaplist-chart
configs:
database:
host: localhost
port: 5432
cache:
redis: redis://localhost:6379
Loading…
Cancel
Save