diff --git a/cmd/cli/kubectl-kyverno/policy/load.go b/cmd/cli/kubectl-kyverno/policy/load.go index 7edd8b2fda..79669c5689 100644 --- a/cmd/cli/kubectl-kyverno/policy/load.go +++ b/cmd/cli/kubectl-kyverno/policy/load.go @@ -11,6 +11,7 @@ import ( "github.com/go-git/go-billy/v5" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2" kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/data" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/experimental" @@ -39,9 +40,11 @@ var ( vapV1Beta1 = v1beta1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicy") vapBidningV1alpha1 = v1alpha1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicyBinding") vapBidningV1beta1 = v1beta1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicyBinding") + exceptionV2beta1 = schema.GroupVersion(kyvernov2beta1.GroupVersion).WithKind("PolicyException") + exceptionV2 = schema.GroupVersion(kyvernov2.GroupVersion).WithKind("PolicyException") LegacyLoader = yamlutils.GetPolicy KubectlValidateLoader = kubectlValidateLoader - defaultLoader = func(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) { + defaultLoader = func(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) { if experimental.UseKubectlValidate() { return KubectlValidateLoader(bytes) } else { @@ -50,174 +53,189 @@ var ( } ) -type loader = func([]byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) +type loader = func([]byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) -func Load(fs billy.Filesystem, resourcePath string, paths ...string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) { +func Load(fs billy.Filesystem, resourcePath string, paths ...string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) { return LoadWithLoader(nil, fs, resourcePath, paths...) } -func LoadWithLoader(loader loader, fs billy.Filesystem, resourcePath string, paths ...string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) { +func LoadWithLoader(loader loader, fs billy.Filesystem, resourcePath string, paths ...string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) { if loader == nil { loader = defaultLoader } var pols []kyvernov1.PolicyInterface var vaps []v1alpha1.ValidatingAdmissionPolicy var vapBindings []v1alpha1.ValidatingAdmissionPolicyBinding + var polex []kyvernov2beta1.PolicyException for _, path := range paths { if source.IsStdin(path) { - p, v, b, err := stdinLoad(loader) + p, v, b, pe, err := stdinLoad(loader) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } pols = append(pols, p...) vaps = append(vaps, v...) vapBindings = append(vapBindings, b...) + polex = append(polex, pe...) } else if fs != nil { - p, v, b, err := gitLoad(loader, fs, filepath.Join(resourcePath, path)) + p, v, b, pe, err := gitLoad(loader, fs, filepath.Join(resourcePath, path)) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } pols = append(pols, p...) vaps = append(vaps, v...) vapBindings = append(vapBindings, b...) + polex = append(polex, pe...) } else if source.IsHttp(path) { - p, v, b, err := httpLoad(loader, path) + p, v, b, pe, err := httpLoad(loader, path) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } pols = append(pols, p...) vaps = append(vaps, v...) vapBindings = append(vapBindings, b...) + polex = append(polex, pe...) } else { - p, v, b, err := fsLoad(loader, path) + p, v, b, pe, err := fsLoad(loader, path) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } pols = append(pols, p...) vaps = append(vaps, v...) vapBindings = append(vapBindings, b...) + polex = append(polex, pe...) } } - return pols, vaps, vapBindings, nil + return pols, vaps, vapBindings, polex, nil } -func kubectlValidateLoader(content []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) { +func kubectlValidateLoader(content []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) { documents, err := extyaml.SplitDocuments(content) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } var policies []kyvernov1.PolicyInterface var vaps []v1alpha1.ValidatingAdmissionPolicy var vapBindings []v1alpha1.ValidatingAdmissionPolicyBinding + var exceptions []kyvernov2beta1.PolicyException for _, document := range documents { gvk, untyped, err := factory.Load(document) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } switch gvk { case policyV1, policyV2: typed, err := convert.To[kyvernov1.Policy](untyped) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } policies = append(policies, typed) case clusterPolicyV1, clusterPolicyV2: typed, err := convert.To[kyvernov1.ClusterPolicy](untyped) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } policies = append(policies, typed) case vapV1alpha1, vapV1Beta1: typed, err := convert.To[v1alpha1.ValidatingAdmissionPolicy](untyped) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } vaps = append(vaps, *typed) case vapBidningV1alpha1, vapBidningV1beta1: typed, err := convert.To[v1alpha1.ValidatingAdmissionPolicyBinding](untyped) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } vapBindings = append(vapBindings, *typed) + case exceptionV2beta1, exceptionV2: + typed, err := convert.To[kyvernov2beta1.PolicyException](untyped) + if err != nil { + return nil, nil, nil, nil, err + } + exceptions = append(exceptions, *typed) default: - return nil, nil, nil, fmt.Errorf("policy type not supported %s", gvk) + return nil, nil, nil, nil, fmt.Errorf("policy type not supported %s", gvk) } } - return policies, vaps, vapBindings, nil + return policies, vaps, vapBindings, exceptions, err } -func fsLoad(loader loader, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) { +func fsLoad(loader loader, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) { var pols []kyvernov1.PolicyInterface var vaps []v1alpha1.ValidatingAdmissionPolicy var vapBindings []v1alpha1.ValidatingAdmissionPolicyBinding + var polex []kyvernov2beta1.PolicyException fi, err := os.Stat(filepath.Clean(path)) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } if fi.IsDir() { files, err := os.ReadDir(path) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } for _, file := range files { - p, v, b, err := fsLoad(loader, filepath.Join(path, file.Name())) + p, v, b, pe, err := fsLoad(loader, filepath.Join(path, file.Name())) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } pols = append(pols, p...) vaps = append(vaps, v...) vapBindings = append(vapBindings, b...) + polex = append(polex, pe...) } } else if git.IsYaml(fi) { fileBytes, err := os.ReadFile(filepath.Clean(path)) // #nosec G304 if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } - p, v, b, err := loader(fileBytes) + p, v, b, pe, err := loader(fileBytes) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } pols = append(pols, p...) vaps = append(vaps, v...) vapBindings = append(vapBindings, b...) + polex = append(polex, pe...) } - return pols, vaps, vapBindings, nil + return pols, vaps, vapBindings, polex, nil } -func httpLoad(loader loader, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) { +func httpLoad(loader loader, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) { // We accept here that a random URL might be called based on user provided input. req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, path, nil) if err != nil { - return nil, nil, nil, fmt.Errorf("failed to process %v: %v", path, err) + return nil, nil, nil, nil, fmt.Errorf("failed to process %v: %v", path, err) } resp, err := http.DefaultClient.Do(req) if err != nil { - return nil, nil, nil, fmt.Errorf("failed to process %v: %v", path, err) + return nil, nil, nil, nil, fmt.Errorf("failed to process %v: %v", path, err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, nil, nil, fmt.Errorf("failed to process %v: %v", path, err) + return nil, nil, nil, nil, fmt.Errorf("failed to process %v: %v", path, err) } fileBytes, err := io.ReadAll(resp.Body) if err != nil { - return nil, nil, nil, fmt.Errorf("failed to process %v: %v", path, err) + return nil, nil, nil, nil, fmt.Errorf("failed to process %v: %v", path, err) } return loader(fileBytes) } -func gitLoad(loader loader, fs billy.Filesystem, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) { +func gitLoad(loader loader, fs billy.Filesystem, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) { file, err := fs.Open(path) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } fileBytes, err := io.ReadAll(file) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } return loader(fileBytes) } -func stdinLoad(loader loader) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) { +func stdinLoad(loader loader) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, []kyvernov2beta1.PolicyException, error) { policyStr := "" scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { diff --git a/cmd/cli/kubectl-kyverno/policy/load_test.go b/cmd/cli/kubectl-kyverno/policy/load_test.go index cf67d8b11c..ebaa17507e 100644 --- a/cmd/cli/kubectl-kyverno/policy/load_test.go +++ b/cmd/cli/kubectl-kyverno/policy/load_test.go @@ -31,7 +31,7 @@ func TestLoad(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, _, _, err := Load(tt.fs, tt.resourcePath, tt.paths...) + _, _, _, _, err := Load(tt.fs, tt.resourcePath, tt.paths...) if (err != nil) != tt.wantErr { t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr) return @@ -87,7 +87,7 @@ func TestLoadWithKubectlValidate(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - policies, vaps, _, err := LoadWithLoader(KubectlValidateLoader, tt.fs, tt.resourcePath, tt.paths...) + policies, vaps, _, _, err := LoadWithLoader(KubectlValidateLoader, tt.fs, tt.resourcePath, tt.paths...) if (err != nil) != tt.wantErr { t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/cmd/cli/kubectl-kyverno/policy/variables_test.go b/cmd/cli/kubectl-kyverno/policy/variables_test.go index c80a6712f7..4bf31e9657 100644 --- a/cmd/cli/kubectl-kyverno/policy/variables_test.go +++ b/cmd/cli/kubectl-kyverno/policy/variables_test.go @@ -11,7 +11,7 @@ import ( func TestExtractVariables(t *testing.T) { loadPolicy := func(path string) kyvernov1.PolicyInterface { t.Helper() - policies, _, _, err := Load(nil, "", path) + policies, _, _, _, err := Load(nil, "", path) assert.NoError(t, err) assert.Equal(t, len(policies), 1) return policies[0]