mirror of https://github.com/helm/helm
This checks that the `crds/` dir only contains YAML files that define K8s resources with `kind: CustomResourceDefinition`. Checking that the YAML files are not templates will be done in a separate commit. Signed-off-by: Zach Burgess <zachburg@google.com>pull/31015/head
parent
968ebc3a15
commit
21b9aa3d94
@ -0,0 +1,96 @@
|
||||
/*
|
||||
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 rules
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
|
||||
"helm.sh/helm/v4/pkg/chart/v2/loader"
|
||||
"helm.sh/helm/v4/pkg/lint/support"
|
||||
)
|
||||
|
||||
// Crds lints the CRDs in the Linter.
|
||||
func Crds(linter *support.Linter) {
|
||||
fpath := "crds/"
|
||||
crdsPath := filepath.Join(linter.ChartDir, fpath)
|
||||
|
||||
crdsDirExist := linter.RunLinterRule(support.WarningSev, fpath, validateCrdsDir(crdsPath))
|
||||
|
||||
// crds directory is optional
|
||||
if !crdsDirExist {
|
||||
return
|
||||
}
|
||||
|
||||
// Load chart and parse CRDs
|
||||
chart, err := loader.Load(linter.ChartDir)
|
||||
|
||||
chartLoaded := linter.RunLinterRule(support.ErrorSev, fpath, err)
|
||||
|
||||
if !chartLoaded {
|
||||
return
|
||||
}
|
||||
|
||||
/* Iterate over all the CRDs to check:
|
||||
- It is a YAML file
|
||||
- The kind is CustomResourceDefinition
|
||||
*/
|
||||
for _, crd := range chart.CRDObjects() {
|
||||
fileName := crd.Name
|
||||
fpath = fileName
|
||||
|
||||
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(crd.File.Data), 4096)
|
||||
for {
|
||||
var yamlStruct *K8sYamlStruct
|
||||
|
||||
err := decoder.Decode(&yamlStruct)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
// If YAML linting fails here, it will always fail in the next block as well, so we should return here.
|
||||
if !linter.RunLinterRule(support.ErrorSev, fpath, validateYamlContent(err)) {
|
||||
return
|
||||
}
|
||||
|
||||
linter.RunLinterRule(support.ErrorSev, fpath, validateCrdKind(yamlStruct))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validation functions
|
||||
func validateCrdsDir(crdsPath string) error {
|
||||
if fi, err := os.Stat(crdsPath); err == nil {
|
||||
if !fi.IsDir() {
|
||||
return errors.New("not a directory")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCrdKind(obj *K8sYamlStruct) error {
|
||||
if obj.Kind != "CustomResourceDefinition" {
|
||||
return fmt.Errorf("object kind is not 'CustomResourceDefinition'")
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 rules
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v4/pkg/lint/support"
|
||||
)
|
||||
|
||||
const crdsTestBasedir = "./testdata/withcrd"
|
||||
const invalidCrdsDir = "./testdata/invalidcrdsdir"
|
||||
|
||||
func TestCrdsDir(t *testing.T) {
|
||||
linter := support.Linter{ChartDir: crdsTestBasedir}
|
||||
Crds(&linter)
|
||||
res := linter.Messages
|
||||
|
||||
if len(res) > 0 {
|
||||
t.Fatalf("Expected no errors, got %d, %v", len(res), res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidCrdsDir(t *testing.T) {
|
||||
linter := support.Linter{ChartDir: invalidCrdsDir}
|
||||
Crds(&linter)
|
||||
res := linter.Messages
|
||||
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("Expected one error, got %d, %v", len(res), res)
|
||||
}
|
||||
|
||||
if !strings.Contains(res[0].Err.Error(), "not a directory") {
|
||||
t.Errorf("Unexpected error: %s", res[0])
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
description: A Helm chart for Kubernetes
|
||||
version: 0.1.0
|
||||
name: badcrdfile
|
||||
type: application
|
||||
icon: http://riverrun.io
|
@ -0,0 +1,2 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: NotACustomResourceDefinition
|
@ -0,0 +1 @@
|
||||
# Default values for badcrdfile.
|
@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
description: A Helm chart for Kubernetes
|
||||
version: 0.1.0
|
||||
name: invalidcrdsdir
|
||||
type: application
|
||||
icon: http://riverrun.io
|
@ -0,0 +1 @@
|
||||
# Default values for badcrdfile.
|
@ -0,0 +1,5 @@
|
||||
apiVersion: v1
|
||||
name: withcrd
|
||||
description: testing chart with a CRD
|
||||
version: 199.44.12345-Alpha.1+cafe009
|
||||
icon: http://riverrun.io
|
@ -0,0 +1,19 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: tests.test.io
|
||||
spec:
|
||||
group: test.io
|
||||
names:
|
||||
kind: Test
|
||||
listKind: TestList
|
||||
plural: tests
|
||||
singular: test
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name : v1alpha2
|
||||
served: true
|
||||
storage: true
|
||||
- name : v1alpha1
|
||||
served: true
|
||||
storage: false
|
Loading…
Reference in new issue