diff --git a/cmd/cli/kubectl-kyverno/commands/apply/command.go b/cmd/cli/kubectl-kyverno/commands/apply/command.go index e00ec54153..14cbda6ed7 100644 --- a/cmd/cli/kubectl-kyverno/commands/apply/command.go +++ b/cmd/cli/kubectl-kyverno/commands/apply/command.go @@ -28,7 +28,6 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" - "github.com/kyverno/kyverno/pkg/openapi" gitutils "github.com/kyverno/kyverno/pkg/utils/git" policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy" "github.com/spf13/cobra" @@ -142,10 +141,6 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul if err != nil { return nil, nil, skipInvalidPolicies, nil, fmt.Errorf("failed to decode yaml (%w)", err) } - openApiManager, err := openapi.NewManager(log.Log) - if err != nil { - return nil, nil, skipInvalidPolicies, nil, fmt.Errorf("failed to initialize openAPIController (%w)", err) - } rc, resources1, skipInvalidPolicies, responses1, err, dClient := c.initStoreAndClusterClient(skipInvalidPolicies) if err != nil { return rc, resources1, skipInvalidPolicies, responses1, err @@ -171,7 +166,6 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul variables, policies, resources, - openApiManager, &skipInvalidPolicies, dClient, userInfo, @@ -228,7 +222,6 @@ func (c *ApplyCommandConfig) applyPolicytoResource( vars *variables.Variables, policies []kyvernov1.PolicyInterface, resources []*unstructured.Unstructured, - openApiManager openapi.Manager, skipInvalidPolicies *SkippedInvalidPolicies, dClient dclient.Interface, userInfo *v1beta1.RequestInfo, @@ -241,7 +234,7 @@ func (c *ApplyCommandConfig) applyPolicytoResource( var validPolicies []kyvernov1.PolicyInterface for _, pol := range policies { // TODO we should return this info to the caller - _, err := policyvalidation.Validate(pol, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName())) + _, err := policyvalidation.Validate(pol, nil, nil, true, config.KyvernoUserName(config.KyvernoServiceAccountName())) if err != nil { log.Log.Error(err, "policy validation error") if strings.HasPrefix(err.Error(), "variable 'element.name'") { diff --git a/cmd/cli/kubectl-kyverno/commands/oci/push/options.go b/cmd/cli/kubectl-kyverno/commands/oci/push/options.go index f1491891dd..0d626ff3fa 100644 --- a/cmd/cli/kubectl-kyverno/commands/oci/push/options.go +++ b/cmd/cli/kubectl-kyverno/commands/oci/push/options.go @@ -14,10 +14,8 @@ import ( "github.com/google/go-containerregistry/pkg/v1/static" "github.com/google/go-containerregistry/pkg/v1/types" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/oci/internal" - clilog "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy" "github.com/kyverno/kyverno/pkg/config" - "github.com/kyverno/kyverno/pkg/openapi" policyutils "github.com/kyverno/kyverno/pkg/utils/policy" policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy" ) @@ -41,12 +39,8 @@ func (o options) execute(ctx context.Context, dir string, keychain authn.Keychai if err != nil { return fmt.Errorf("unable to read policy file or directory %s (%w)", dir, err) } - openApiManager, err := openapi.NewManager(clilog.Log) - if err != nil { - return fmt.Errorf("creating openapi manager: %v", err) - } for _, policy := range policies { - if _, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName())); err != nil { + if _, err := policyvalidation.Validate(policy, nil, nil, true, config.KyvernoUserName(config.KyvernoServiceAccountName())); err != nil { return fmt.Errorf("validating policy %s: %v", policy.GetName(), err) } } diff --git a/cmd/cli/kubectl-kyverno/commands/test/command.go b/cmd/cli/kubectl-kyverno/commands/test/command.go index c00ad26f11..84725bdbb0 100644 --- a/cmd/cli/kubectl-kyverno/commands/test/command.go +++ b/cmd/cli/kubectl-kyverno/commands/test/command.go @@ -8,14 +8,12 @@ import ( "github.com/go-git/go-billy/v5" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/v1alpha1" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command" - "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/color" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/table" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/report" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/store" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/filter" engineapi "github.com/kyverno/kyverno/pkg/engine/api" - "github.com/kyverno/kyverno/pkg/openapi" "github.com/spf13/cobra" "k8s.io/client-go/tools/cache" ) @@ -75,11 +73,6 @@ func testCommandExecute( fmt.Fprintln(out, " Error:", e) } } - // init openapi manager - openApiManager, err := openapi.NewManager(log.Log) - if err != nil { - return fmt.Errorf("unable to create open api controller, %w", err) - } // load tests tests, err := loadTests(dirPath, fileName, gitBranch) if err != nil { @@ -122,7 +115,7 @@ func testCommandExecute( continue } resourcePath := filepath.Dir(test.Path) - responses, err := runTest(out, openApiManager, test, false) + responses, err := runTest(out, test, false) if err != nil { return fmt.Errorf("failed to run test (%w)", err) } diff --git a/cmd/cli/kubectl-kyverno/commands/test/test.go b/cmd/cli/kubectl-kyverno/commands/test/test.go index 1628395df5..8693b8d24c 100644 --- a/cmd/cli/kubectl-kyverno/commands/test/test.go +++ b/cmd/cli/kubectl-kyverno/commands/test/test.go @@ -22,12 +22,11 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" - "github.com/kyverno/kyverno/pkg/openapi" policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func runTest(out io.Writer, openApiManager openapi.Manager, testCase test.TestCase, auditWarn bool) ([]engineapi.EngineResponse, error) { +func runTest(out io.Writer, testCase test.TestCase, auditWarn bool) ([]engineapi.EngineResponse, error) { // don't process test case with errors if testCase.Err != nil { return nil, testCase.Err @@ -116,7 +115,7 @@ func runTest(out io.Writer, openApiManager openapi.Manager, testCase test.TestCa var validPolicies []kyvernov1.PolicyInterface for _, pol := range policies { // TODO we should return this info to the caller - _, err := policyvalidation.Validate(pol, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName())) + _, err := policyvalidation.Validate(pol, nil, nil, true, config.KyvernoUserName(config.KyvernoServiceAccountName())) if err != nil { log.Log.Error(err, "skipping invalid policy", "name", pol.GetName()) continue diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index 174e3d3ce7..aaed0eb466 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -20,7 +20,6 @@ import ( genericloggingcontroller "github.com/kyverno/kyverno/pkg/controllers/generic/logging" genericwebhookcontroller "github.com/kyverno/kyverno/pkg/controllers/generic/webhook" policymetricscontroller "github.com/kyverno/kyverno/pkg/controllers/metrics/policy" - openapicontroller "github.com/kyverno/kyverno/pkg/controllers/openapi" policycachecontroller "github.com/kyverno/kyverno/pkg/controllers/policycache" vapcontroller "github.com/kyverno/kyverno/pkg/controllers/validatingadmissionpolicy-generate" webhookcontroller "github.com/kyverno/kyverno/pkg/controllers/webhook" @@ -29,7 +28,6 @@ import ( "github.com/kyverno/kyverno/pkg/informers" "github.com/kyverno/kyverno/pkg/leaderelection" "github.com/kyverno/kyverno/pkg/logging" - "github.com/kyverno/kyverno/pkg/openapi" "github.com/kyverno/kyverno/pkg/policycache" "github.com/kyverno/kyverno/pkg/tls" "github.com/kyverno/kyverno/pkg/toggle" @@ -82,7 +80,6 @@ func createNonLeaderControllers( dynamicClient dclient.Interface, configuration config.Configuration, policyCache policycache.Cache, - manager openapi.Manager, ) ([]internal.Controller, func(context.Context) error) { policyCacheController := policycachecontroller.NewController( dynamicClient, @@ -90,13 +87,8 @@ func createNonLeaderControllers( kyvernoInformer.Kyverno().V1().ClusterPolicies(), kyvernoInformer.Kyverno().V1().Policies(), ) - openApiController := openapicontroller.NewController( - dynamicClient, - manager, - ) return []internal.Controller{ internal.NewController(policycachecontroller.ControllerName, policyCacheController, policycachecontroller.Workers), - internal.NewController(openapicontroller.ControllerName, openApiController, openapicontroller.Workers), }, func(ctx context.Context) error { if err := policyCacheController.WarmUp(); err != nil { @@ -294,11 +286,6 @@ func main() { kubeInformer := kubeinformers.NewSharedInformerFactory(setup.KubeClient, resyncPeriod) kubeKyvernoInformer := kubeinformers.NewSharedInformerFactoryWithOptions(setup.KubeClient, resyncPeriod, kubeinformers.WithNamespace(config.KyvernoNamespace())) kyvernoInformer := kyvernoinformer.NewSharedInformerFactory(setup.KyvernoClient, resyncPeriod) - openApiManager, err := openapi.NewManager(setup.Logger.WithName("openapi")) - if err != nil { - setup.Logger.Error(err, "Failed to create openapi manager") - os.Exit(1) - } var wg sync.WaitGroup certRenewer := tls.NewCertRenewer( setup.KubeClient.CoreV1().Secrets(config.KyvernoNamespace()), @@ -375,7 +362,6 @@ func main() { setup.KyvernoDynamicClient, setup.Configuration, policyCache, - openApiManager, ) // start informers and wait for cache sync if !internal.StartInformersAndWaitForCacheSync(signalCtx, setup.Logger, kyvernoInformer, kubeInformer, kubeKyvernoInformer) { @@ -473,7 +459,6 @@ func main() { ) policyHandlers := webhookspolicy.NewHandlers( setup.KyvernoDynamicClient, - openApiManager, backgroundServiceAccountName, ) resourceHandlers := webhooksresource.NewHandlers( @@ -489,7 +474,6 @@ func main() { kyvernoInformer.Kyverno().V1().Policies(), urgen, eventGenerator, - openApiManager, admissionReports, backgroundServiceAccountName, setup.Jp, diff --git a/go.mod b/go.mod index aea3b52fc4..a9be73e3d8 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,6 @@ require ( github.com/onsi/gomega v1.27.10 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0-rc5 - github.com/orcaman/concurrent-map/v2 v2.0.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.16.0 github.com/robfig/cron v1.2.0 @@ -70,7 +69,6 @@ require ( google.golang.org/grpc v1.58.2 gopkg.in/inf.v0 v0.9.1 gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible k8s.io/api v0.28.2 k8s.io/apiextensions-apiserver v0.28.1 @@ -80,7 +78,6 @@ require ( k8s.io/client-go v0.28.2 k8s.io/klog/v2 v2.100.1 k8s.io/kube-aggregator v0.28.2 - k8s.io/kube-openapi v0.0.0-20230816210353-14e408962443 k8s.io/pod-security-admission v0.28.2 k8s.io/utils v0.0.0-20230726121419-3b25d923346b sigs.k8s.io/controller-runtime v0.16.2 @@ -399,8 +396,10 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect k8s.io/component-base v0.28.2 // indirect + k8s.io/kube-openapi v0.0.0-20230816210353-14e408962443 // indirect k8s.io/kubectl v0.28.2 // indirect oras.land/oras-go/v2 v2.3.0 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.4 // indirect diff --git a/go.sum b/go.sum index 28f2ea14be..57a78a978d 100644 --- a/go.sum +++ b/go.sum @@ -1249,8 +1249,6 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c= -github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM= github.com/outcaste-io/ristretto v0.2.1/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0= github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= diff --git a/pkg/controllers/openapi/controller.go b/pkg/controllers/openapi/controller.go deleted file mode 100644 index 8469698be0..0000000000 --- a/pkg/controllers/openapi/controller.go +++ /dev/null @@ -1,150 +0,0 @@ -package openapi - -import ( - "context" - "fmt" - "strings" - "sync" - "time" - - "github.com/kyverno/kyverno/pkg/clients/dclient" - "github.com/kyverno/kyverno/pkg/controllers" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtimeSchema "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/discovery" -) - -const ( - // Workers is the number of workers for this controller - Workers = 1 - ControllerName = "openapi-controller" -) - -type Controller interface { - controllers.Controller - CheckSync(context.Context) -} - -type controller struct { - client dclient.Interface - manager Manager -} - -const ( - skipErrorMsg = "Got empty response for" -) - -// NewController ... -func NewController(client dclient.Interface, mgr Manager) Controller { - if mgr == nil { - panic(fmt.Errorf("nil manager sent into crd sync")) - } - return &controller{ - manager: mgr, - client: client, - } -} - -func (c *controller) Run(ctx context.Context, workers int) { - if err := c.updateInClusterKindToAPIVersions(); err != nil { - logger.Error(err, "failed to update in-cluster api versions") - } - newDoc, err := c.client.Discovery().OpenAPISchema() - if err != nil { - logger.Error(err, "cannot get OpenAPI schema") - } - err = c.manager.UseOpenAPIDocument(newDoc) - if err != nil { - logger.Error(err, "Could not set custom OpenAPI document") - } - // Sync CRD before kyverno starts - c.sync() - var wg sync.WaitGroup - for i := 0; i < workers; i++ { - wg.Add(1) - go func() { - defer wg.Done() - wait.UntilWithContext(ctx, c.CheckSync, 15*time.Second) - }() - } - <-ctx.Done() -} - -func (c *controller) sync() { - c.manager.Lock() - defer c.manager.Unlock() - c.client.Discovery().CachedDiscoveryInterface().Invalidate() - crds, err := c.client.GetDynamicInterface().Resource(runtimeSchema.GroupVersionResource{ - Group: "apiextensions.k8s.io", - Version: "v1", - Resource: "customresourcedefinitions", - }).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - logger.Error(err, "could not fetch crd's from server") - return - } - - c.manager.DeleteCRDFromPreviousSync() - - for _, crd := range crds.Items { - c.manager.ParseCRD(crd) - } - - if err := c.updateInClusterKindToAPIVersions(); err != nil { - logger.Error(err, "sync failed, unable to update in-cluster api versions") - } - - newDoc, err := c.client.Discovery().OpenAPISchema() - if err != nil { - logger.Error(err, "cannot get OpenAPI schema") - } - - err = c.manager.UseOpenAPIDocument(newDoc) - if err != nil { - logger.Error(err, "Could not set custom OpenAPI document") - } -} - -func (c *controller) updateInClusterKindToAPIVersions() error { - overrideRuntimeErrorHandler() - _, apiResourceLists, err := discovery.ServerGroupsAndResources(c.client.Discovery().CachedDiscoveryInterface()) - if err != nil { - if discovery.IsGroupDiscoveryFailedError(err) { - err := err.(*discovery.ErrGroupDiscoveryFailed) - for gv, err := range err.Groups { - logger.Error(err, "failed to list api resources", "group", gv) - } - } else if !strings.Contains(err.Error(), skipErrorMsg) { - return err - } - } - preferredAPIResourcesLists, err := discovery.ServerPreferredResources(c.client.Discovery().CachedDiscoveryInterface()) - if err != nil { - if discovery.IsGroupDiscoveryFailedError(err) { - err := err.(*discovery.ErrGroupDiscoveryFailed) - for gv, err := range err.Groups { - logger.Error(err, "failed to list api resources", "group", gv) - } - } else if !strings.Contains(err.Error(), skipErrorMsg) { - return err - } - } - c.manager.UpdateKindToAPIVersions(apiResourceLists, preferredAPIResourcesLists) - return nil -} - -func (c *controller) CheckSync(ctx context.Context) { - crds, err := c.client.GetDynamicInterface().Resource(runtimeSchema.GroupVersionResource{ - Group: "apiextensions.k8s.io", - Version: "v1", - Resource: "customresourcedefinitions", - }).List(ctx, metav1.ListOptions{}) - if err != nil { - logger.Error(err, "could not fetch crd's from server") - return - } - if len(c.manager.GetCrdList()) != len(crds.Items) { - c.sync() - } -} diff --git a/pkg/controllers/openapi/log.go b/pkg/controllers/openapi/log.go deleted file mode 100644 index 4a3a3fa700..0000000000 --- a/pkg/controllers/openapi/log.go +++ /dev/null @@ -1,5 +0,0 @@ -package openapi - -import "github.com/kyverno/kyverno/pkg/logging" - -var logger = logging.ControllerLogger(ControllerName) diff --git a/pkg/controllers/openapi/manager.go b/pkg/controllers/openapi/manager.go deleted file mode 100644 index 656e8144a0..0000000000 --- a/pkg/controllers/openapi/manager.go +++ /dev/null @@ -1,17 +0,0 @@ -package openapi - -import ( - openapiv2 "github.com/google/gnostic-models/openapiv2" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -type Manager interface { - Lock() - Unlock() - UseOpenAPIDocument(*openapiv2.Document) error - DeleteCRDFromPreviousSync() - ParseCRD(unstructured.Unstructured) - UpdateKindToAPIVersions([]*metav1.APIResourceList, []*metav1.APIResourceList) - GetCrdList() []string -} diff --git a/pkg/controllers/openapi/utils.go b/pkg/controllers/openapi/utils.go deleted file mode 100644 index 43c7a861d7..0000000000 --- a/pkg/controllers/openapi/utils.go +++ /dev/null @@ -1,17 +0,0 @@ -package openapi - -import "k8s.io/apimachinery/pkg/util/runtime" - -func overrideRuntimeErrorHandler() { - if len(runtime.ErrorHandlers) > 0 { - runtime.ErrorHandlers[0] = func(err error) { - logger.V(6).Info("runtime error", "msg", err.Error()) - } - } else { - runtime.ErrorHandlers = []func(err error){ - func(err error) { - logger.V(6).Info("runtime error", "msg", err.Error()) - }, - } - } -} diff --git a/pkg/openapi/definitions.go b/pkg/openapi/definitions.go deleted file mode 100644 index 7d8838bed5..0000000000 --- a/pkg/openapi/definitions.go +++ /dev/null @@ -1,28 +0,0 @@ -package openapi - -// crdDefinitionPrior represents CRDs version prior to 1.16 -var crdDefinitionPrior struct { - Spec struct { - Names struct { - Kind string `json:"kind"` - } `json:"names"` - Validation struct { - OpenAPIV3Schema interface{} `json:"openAPIV3Schema"` - } `json:"validation"` - } `json:"spec"` -} - -// crdDefinitionNew represents CRDs version 1.16+ -var crdDefinitionNew struct { - Spec struct { - Names struct { - Kind string `json:"kind"` - } `json:"names"` - Versions []struct { - Schema struct { - OpenAPIV3Schema interface{} `json:"openAPIV3Schema"` - } `json:"schema"` - Storage bool `json:"storage"` - } `json:"versions"` - } `json:"spec"` -} diff --git a/pkg/openapi/fake.go b/pkg/openapi/fake.go deleted file mode 100644 index e6d2fc8bc0..0000000000 --- a/pkg/openapi/fake.go +++ /dev/null @@ -1,20 +0,0 @@ -package openapi - -import ( - kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -func NewFake() ValidateInterface { - return &fakeValidation{} -} - -type fakeValidation struct{} - -func (f *fakeValidation) ValidateResource(resource unstructured.Unstructured, apiVersion, kind string) error { - return nil -} - -func (f *fakeValidation) ValidatePolicyMutation(kyvernov1.PolicyInterface) error { - return nil -} diff --git a/pkg/openapi/manager.go b/pkg/openapi/manager.go deleted file mode 100644 index d2b01b043f..0000000000 --- a/pkg/openapi/manager.go +++ /dev/null @@ -1,395 +0,0 @@ -package openapi - -import ( - "encoding/json" - "fmt" - "strings" - "sync" - - "github.com/go-logr/logr" - "github.com/google/gnostic-models/compiler" - openapiv2 "github.com/google/gnostic-models/openapiv2" - kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" - "github.com/kyverno/kyverno/pkg/autogen" - openapicontroller "github.com/kyverno/kyverno/pkg/controllers/openapi" - "github.com/kyverno/kyverno/pkg/engine" - cmap "github.com/orcaman/concurrent-map/v2" - "gopkg.in/yaml.v3" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/kube-openapi/pkg/util/proto" - "k8s.io/kube-openapi/pkg/util/proto/validation" -) - -type ValidateInterface interface { - ValidateResource(unstructured.Unstructured, string, string) error - ValidatePolicyMutation(kyvernov1.PolicyInterface) error -} - -type Manager interface { - ValidateInterface - openapicontroller.Manager -} - -type manager struct { - // definitions holds the map of {definitionName: *openapiv2.Schema} - definitions cmap.ConcurrentMap[string, *openapiv2.Schema] - - // kindToDefinitionName holds the map of {(group/version/)kind: definitionName} - // i.e. with k8s 1.20.2 - // - Ingress: io.k8s.api.networking.v1.Ingress (preferred version) - // - networking.k8s.io/v1/Ingress: io.k8s.api.networking.v1.Ingress - // - networking.k8s.io/v1beta1/Ingress: io.k8s.api.networking.v1beta1.Ingress - // - extension/v1beta1/Ingress: io.k8s.api.extensions.v1beta1.Ingress - gvkToDefinitionName cmap.ConcurrentMap[string, string] - - crdList []string - models proto.Models - - // kindToAPIVersions stores the Kind and all its available apiVersions, {kind: apiVersions} - kindToAPIVersions cmap.ConcurrentMap[string, apiVersions] - - logger logr.Logger - lock sync.Mutex -} - -// apiVersions stores all available gvks for a kind, a gvk is "/" separated string -type apiVersions struct { - serverPreferredGVK string - gvks []string -} - -// NewManager initializes a new instance of openapi schema manager -func NewManager(logger logr.Logger) (*manager, error) { - mgr := &manager{ - definitions: cmap.New[*openapiv2.Schema](), - gvkToDefinitionName: cmap.New[string](), - kindToAPIVersions: cmap.New[apiVersions](), - logger: logger, - } - - apiResourceLists, preferredAPIResourcesLists, err := getAPIResourceLists() - if err != nil { - return nil, err - } - - mgr.UpdateKindToAPIVersions(apiResourceLists, preferredAPIResourcesLists) - - defaultDoc, err := getSchemaDocument() - if err != nil { - return nil, err - } - - err = mgr.UseOpenAPIDocument(defaultDoc) - if err != nil { - return nil, err - } - - return mgr, nil -} - -func (o *manager) Lock() { - o.lock.Lock() -} - -func (o *manager) Unlock() { - o.lock.Unlock() -} - -// ValidateResource ... -func (o *manager) ValidateResource(patchedResource unstructured.Unstructured, apiVersion, kind string) error { - o.Lock() - defer o.Unlock() - return o.validateResource(patchedResource, apiVersion, kind) -} - -// ValidatePolicyMutation ... -func (o *manager) ValidatePolicyMutation(policy kyvernov1.PolicyInterface) error { - o.Lock() - defer o.Unlock() - kindToRules := make(map[string][]kyvernov1.Rule) - for _, rule := range autogen.ComputeRules(policy) { - if rule.HasMutate() { - if rule.IsMutateExisting() { - for _, target := range rule.Mutation.Targets { - kindToRules[target.Kind] = append(kindToRules[target.Kind], rule) - } - } else { - for _, kind := range rule.MatchResources.Kinds { - kindToRules[kind] = append(kindToRules[kind], rule) - } - for _, resourceFilter := range rule.MatchResources.Any { - for _, kind := range resourceFilter.Kinds { - kindToRules[kind] = append(kindToRules[kind], rule) - } - } - for _, resourceFilter := range rule.MatchResources.All { - for _, kind := range resourceFilter.Kinds { - kindToRules[kind] = append(kindToRules[kind], rule) - } - } - } - } - } - - for kind, rules := range kindToRules { - if kind == "CustomResourceDefinition" { - continue - } - newPolicy := policy.CreateDeepCopy() - spec := newPolicy.GetSpec() - spec.SetRules(rules) - k, ok := o.gvkToDefinitionName.Get(kind) - if !ok { - continue - } - d, ok := o.definitions.Get(k) - if !ok { - continue - } - resource, _ := o.generateEmptyResource(d).(map[string]interface{}) - if len(resource) == 0 { - o.logger.V(2).Info("unable to validate resource. OpenApi definition not found", "kind", kind) - return nil - } - - newResource := unstructured.Unstructured{Object: resource} - newResource.SetKind(kind) - - patchedResource, err := engine.ForceMutate(nil, o.logger, newPolicy, newResource) - if err != nil { - return err - } - - if kind != "*" { - err = o.validateResource(*patchedResource.DeepCopy(), "", kind) - if err != nil { - return fmt.Errorf("mutate result violates resource schema: %w", err) - } - } - } - - return nil -} - -// ValidateResource ... -func (o *manager) validateResource(patchedResource unstructured.Unstructured, apiVersion, kind string) error { - var err error - - gvk := kind - if apiVersion != "" { - gvk = apiVersion + "/" + kind - } - - kind, _ = o.gvkToDefinitionName.Get(gvk) - schema := o.models.LookupModel(kind) - if schema == nil { - // Check if kind is a CRD - schema, err = o.getCRDSchema(kind) - if err != nil || schema == nil { - return fmt.Errorf("pre-validation: couldn't find model %s, err: %v", kind, err) - } - delete(patchedResource.Object, "kind") - } - - if errs := validation.ValidateModel(patchedResource.UnstructuredContent(), schema, kind); len(errs) > 0 { - var errorMessages []string - for i := range errs { - errorMessages = append(errorMessages, errs[i].Error()) - } - - return fmt.Errorf(strings.Join(errorMessages, "\n\n")) - } - - return nil -} - -func (o *manager) UseOpenAPIDocument(doc *openapiv2.Document) error { - for _, definition := range doc.GetDefinitions().AdditionalProperties { - definitionName := definition.GetName() - - o.definitions.Set(definitionName, definition.GetValue()) - - gvk, preferredGVK, err := o.getGVKByDefinitionName(definitionName) - if err != nil { - o.logger.V(5).Info("unable to cache OpenAPISchema", "definitionName", definitionName, "reason", err.Error()) - continue - } - - if preferredGVK { - paths := strings.Split(definitionName, ".") - kind := paths[len(paths)-1] - o.gvkToDefinitionName.Set(kind, definitionName) - } - - if gvk != "" { - o.gvkToDefinitionName.Set(gvk, definitionName) - } - } - - var err error - o.models, err = proto.NewOpenAPIData(doc) - if err != nil { - return err - } - - return nil -} - -func (o *manager) getGVKByDefinitionName(definitionName string) (gvk string, preferredGVK bool, err error) { - paths := strings.Split(definitionName, ".") - kind := paths[len(paths)-1] - versions, ok := o.kindToAPIVersions.Get(kind) - if !ok { - // the kind here is the sub-resource of a K8s Kind, i.e. CronJobStatus - // such cases are skipped in schema validation - return - } - - if matchGVK(definitionName, versions.serverPreferredGVK) { - preferredGVK = true - } - - for _, gvk := range versions.gvks { - if matchGVK(definitionName, gvk) { - return gvk, preferredGVK, nil - } - } - - return "", preferredGVK, fmt.Errorf("gvk not found by the given definition name %s, %v", definitionName, versions.gvks) -} - -func (c *manager) GetCrdList() []string { - return c.crdList -} - -// UpdateKindToAPIVersions sets kindToAPIVersions with static manifests -func (c *manager) UpdateKindToAPIVersions(apiResourceLists, preferredAPIResourcesLists []*metav1.APIResourceList) { - tempKindToAPIVersions := getAllAPIVersions(apiResourceLists) - tempKindToAPIVersions = setPreferredVersions(tempKindToAPIVersions, preferredAPIResourcesLists) - - c.kindToAPIVersions = cmap.New[apiVersions]() - for key, value := range tempKindToAPIVersions { - c.kindToAPIVersions.Set(key, value) - } -} - -// For crd, we do not store definition in document -func (o *manager) getCRDSchema(kind string) (proto.Schema, error) { - if kind == "" { - return nil, fmt.Errorf("invalid kind") - } - - path := proto.NewPath(kind) - definition, _ := o.definitions.Get(kind) - if definition == nil { - return nil, fmt.Errorf("could not find definition") - } - - // This was added so crd's can access - // normal definitions from existing schema such as - // `metadata` - this maybe a breaking change. - // Removing this may cause policy validate to stop working - existingDefinitions, _ := o.models.(*proto.Definitions) - - return (existingDefinitions).ParseSchema(definition, &path) -} - -func (o *manager) generateEmptyResource(kindSchema *openapiv2.Schema) interface{} { - types := kindSchema.GetType().GetValue() - - if kindSchema.GetXRef() != "" { - d, _ := o.definitions.Get(strings.TrimPrefix(kindSchema.GetXRef(), "#/definitions/")) - return o.generateEmptyResource(d) - } - - if len(types) != 1 { - if len(kindSchema.GetProperties().GetAdditionalProperties()) > 0 { - types = []string{"object"} - } else { - return nil - } - } - - switch types[0] { - case "object": - return getObjectValue(kindSchema, o) - case "array": - return getArrayValue(kindSchema, o) - case "string": - return getStringValue(kindSchema) - case "integer": - return getNumericValue(kindSchema) - case "number": - return getNumericValue(kindSchema) - case "boolean": - return getBoolValue(kindSchema) - } - - o.logger.V(2).Info("unknown type", types[0]) - return nil -} - -func (o *manager) DeleteCRDFromPreviousSync() { - for _, crd := range o.crdList { - o.gvkToDefinitionName.Remove(crd) - o.definitions.Remove(crd) - } - - o.crdList = make([]string, 0) -} - -// ParseCRD loads CRD to the cache -func (o *manager) ParseCRD(crd unstructured.Unstructured) { - var err error - - crdRaw, _ := json.Marshal(crd.Object) - _ = json.Unmarshal(crdRaw, &crdDefinitionPrior) - - openV3schema := crdDefinitionPrior.Spec.Validation.OpenAPIV3Schema - crdName := crdDefinitionPrior.Spec.Names.Kind - - if openV3schema == nil { - _ = json.Unmarshal(crdRaw, &crdDefinitionNew) - for _, crdVersion := range crdDefinitionNew.Spec.Versions { - if crdVersion.Storage { - openV3schema = crdVersion.Schema.OpenAPIV3Schema - crdName = crdDefinitionNew.Spec.Names.Kind - break - } - } - } - - if openV3schema == nil { - o.logger.V(4).Info("skip adding schema, CRD has no properties", "name", crdName) - return - } - - schemaRaw, _ := json.Marshal(openV3schema) - if len(schemaRaw) < 1 { - o.logger.V(4).Info("failed to parse crd schema", "name", crdName) - return - } - - schemaRaw, err = addingDefaultFieldsToSchema(crdName, schemaRaw) - if err != nil { - o.logger.Error(err, "failed to parse crd schema", "name", crdName) - return - } - - var schema yaml.Node - _ = yaml.Unmarshal(schemaRaw, &schema) - - parsedSchema, err := openapiv2.NewSchema(&schema, compiler.NewContext("schema", &schema, nil)) - if err != nil { - v3valueFound := isOpenV3Error(err) - if !v3valueFound { - o.logger.Error(err, "failed to parse crd schema", "name", crdName) - } - return - } - - o.crdList = append(o.crdList, crdName) - o.gvkToDefinitionName.Set(crdName, crdName) - o.definitions.Set(crdName, parsedSchema) -} diff --git a/pkg/openapi/manager_test.go b/pkg/openapi/manager_test.go deleted file mode 100644 index 50f8534023..0000000000 --- a/pkg/openapi/manager_test.go +++ /dev/null @@ -1,215 +0,0 @@ -package openapi - -import ( - "encoding/json" - "testing" - - "github.com/go-logr/logr" - v1 "github.com/kyverno/kyverno/api/kyverno/v1" - "gotest.tools/assert" -) - -func Test_ValidateMutationPolicy(t *testing.T) { - - tcs := []struct { - description string - policy []byte - mustSucceed bool - }{ - { - description: "Policy with mutating imagePullPolicy Overlay", - policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"set-image-pull-policy-2"},"spec":{"rules":[{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod"]}},"mutate":{"patchStrategicMerge":{"spec":{"containers":[{"(name)":"*","imagePullPolicy":"Always"}]}}}}]}}`), - mustSucceed: true, - }, - { - description: "Policy with mutating imagePullPolicy Overlay, type of value is different (does not throw error since all numbers are also strings according to swagger)", - policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"set-image-pull-policy-2"},"spec":{"rules":[{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod"]}},"mutate":{"patchStrategicMerge":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":80}]}}}}]}}`), - mustSucceed: true, - }, - { - description: "Policy with patches", - policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"policy-endpoints"},"spec":{"rules":[{"name":"pEP","match":{"resources":{"kinds":["Endpoints"],"selector":{"matchLabels":{"label":"test"}}}},"mutate":{"patches": "[{\"path\":\"/subsets/0/ports/0/port\",\"op\":\"replace\",\"value\":9663},{\"path\":\"/metadata/labels/isMutated\",\"op\":\"add\",\"value\":\"true\"}]}}]" }}`), - mustSucceed: true, - }, - { - description: "Dealing with nested variables", - policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"add-ns-access-controls","annotations":{"policies.kyverno.io/category":"Workload Isolation","policies.kyverno.io/description":"Create roles and role bindings for a new namespace"}},"spec":{"background":false,"rules":[{"name":"add-sa-annotation","match":{"resources":{"kinds":["Namespace"]}},"mutate":{"overlay":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName-{{something}}}}"}}}}},{"name":"generate-owner-role","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"ClusterRole","name":"ns-owner-{{request.object.metadata.name{{something}}}}-{{request.userInfo.username}}","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"rules":[{"apiGroups":[""],"resources":["namespaces"],"verbs":["delete"],"resourceNames":["{{request.object.metadata.name}}"]}]}}},{"name":"generate-owner-role-binding","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"ClusterRoleBinding","name":"ns-owner-{{request.object.metadata.name}}-{{request.userInfo.username}}-binding","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"ns-owner-{{request.object.metadata.name}}-{{request.userInfo.username}}"},"subjects":[{"kind":"ServiceAccount","name":"{{serviceAccountName}}","namespace":"{{serviceAccountNamespace}}"}]}}},{"name":"generate-admin-role-binding","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"RoleBinding","name":"ns-admin-{{request.object.metadata.name}}-{{request.userInfo.username}}-binding","namespace":"{{request.object.metadata.name}}","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"admin"},"subjects":[{"kind":"ServiceAccount","name":"{{serviceAccountName}}","namespace":"{{serviceAccountNamespace}}"}]}}}]}}`), - mustSucceed: true, - }, - { - description: "Policy with patchesJson6902 and added element at the beginning of a list", - policy: []byte(`{"apiVersion": "kyverno.io/v1","kind": "ClusterPolicy","metadata": {"name": "pe"},"spec": {"rules": [{"name": "pe","match": {"resources": {"kinds": ["Endpoints"]}},"mutate": {"patchesJson6902": "- path: \"/subsets/0/addresses/0\"\n op: add\n value: {\"ip\":\"123\"}\n- path: \"/subsets/1/addresses/0\"\n op: add\n value: {\"ip\":\"123\"}"}}]}}`), - mustSucceed: true, - }, - { - description: "Policy with patchesJson6902 and added element at the end of a list", - policy: []byte(`{"apiVersion": "kyverno.io/v1","kind": "ClusterPolicy","metadata": {"name": "pe"},"spec": {"rules": [{"name": "pe","match": {"resources": {"kinds": ["Endpoints"]}},"mutate": {"patchesJson6902": "- path: \"/subsets/0/addresses/-\"\n op: add\n value: {\"ip\":\"123\"}\n- path: \"/subsets/1/addresses/-\"\n op: add\n value: {\"ip\":\"123\"}"}}]}}`), - mustSucceed: true, - }, - { - description: "Invalid policy with patchStrategicMerge and new match schema(any)", - policy: []byte(`{"apiVersion":"kyverno.io\/v1","kind":"ClusterPolicy","metadata":{"name":"mutate-pod"},"spec":{"rules":[{"name":"mutate-pod","match":{"any":[{"resources":{"kinds":["Pod"]}}]},"mutate":{"patchStrategicMerge":{"spec":{"pod":"incorrect"}}}}]}}`), - mustSucceed: false, - }, - { - description: "Invalid policy with patchStrategicMerge and new match schema(all)", - policy: []byte(`{"apiVersion":"kyverno.io\/v1","kind":"ClusterPolicy","metadata":{"name":"mutate-pod"},"spec":{"rules":[{"name":"mutate-pod","match":{"all":[{"resources":{"kinds":["Pod"]}}]},"mutate":{"patchStrategicMerge":{"spec":{"pod":"incorrect"}}}}]}}`), - mustSucceed: false, - }, - { - description: "Valid policy with patchStrategicMerge and new match schema(any)", - policy: []byte(`{"apiVersion":"kyverno.io\/v1","kind":"ClusterPolicy","metadata":{"name":"set-image-pull-policy"},"spec":{"rules":[{"name":"set-image-pull-policy","match":{"any":[{"resources":{"kinds":["Pod"]}}]},"mutate":{"patchStrategicMerge":{"spec":{"containers":[{"(image)":"*:latest","imagePullPolicy":"IfNotPresent"}]}}}}]}}`), - mustSucceed: true, - }, - { - description: "Valid policy with patchStrategicMerge and new match schema(all)", - policy: []byte(`{"apiVersion":"kyverno.io\/v1","kind":"ClusterPolicy","metadata":{"name":"set-image-pull-policy"},"spec":{"rules":[{"name":"set-image-pull-policy","match":{"all":[{"resources":{"kinds":["Pod"]}}]},"mutate":{"patchStrategicMerge":{"spec":{"containers":[{"(image)":"*:latest","imagePullPolicy":"IfNotPresent"}]}}}}]}}`), - mustSucceed: true, - }, - { - description: "Policy with nested foreach and patchesJson6902", - policy: []byte(`{"apiVersion":"kyverno.io/v2beta1","kind":"ClusterPolicy","metadata":{"name":"replace-image-registry"},"spec":{"background":false,"validationFailureAction":"Enforce","rules":[{"name":"replace-dns-suffix","match":{"any":[{"resources":{"kinds":["Ingress"]}}]},"mutate":{"foreach":[{"list":"request.object.spec.tls","foreach":[{"list":"element.hosts","patchesJson6902":"- path: \"/spec/tls/{{elementIndex0}}/hosts/{{elementIndex1}}\"\n op: replace\n value: \"{{replace_all('{{element}}', '.foo.com', '.newfoo.com')}}\""}]}]}}]}}`), - mustSucceed: true, - }, - } - - o, _ := NewManager(logr.Discard()) - - for i, tc := range tcs { - t.Run(tc.description, func(t *testing.T) { - policy := v1.ClusterPolicy{} - _ = json.Unmarshal(tc.policy, &policy) - var errMessage string - err := o.ValidatePolicyMutation(&policy) - if err != nil { - errMessage = err.Error() - } - if tc.mustSucceed { - assert.NilError(t, err, "\nTestcase [%v] failed: Expected no error, Got error: %v", i+1, errMessage) - } else { - assert.Assert(t, err != nil, "\nTestcase [%v] failed: Expected error to have occurred", i+1) - } - }) - } - -} - -func Test_addDefaultFieldsToSchema(t *testing.T) { - addingDefaultFieldsToSchema("", []byte(`null`)) - addingDefaultFieldsToSchema("", nil) -} - -func Test_matchGVK(t *testing.T) { - testCases := []struct { - definitionName string - gvk string - match bool - }{ - - { - "io.k8s.api.networking.v1.Ingress", - "networking.k8s.io/v1/Ingress", - true, - }, - { - "io.k8s.api.extensions.v1beta1.Ingress", - "extensions/v1beta1/Ingress", - true, - }, - { - "io.crossplane.gcp.iam.v1.ServiceAccount", - "v1/ServiceAccount", - false, - }, - { - "io.k8s.api.core.v1.Secret", - "v1/Secret", - true, - }, - { - "io.wgpolicyk8s.v1alpha1.PolicyReport", - "wgpolicyk8s.io/v1alpha1/PolicyReport", - true, - }, - { - "io.k8s.api.rbac.v1.RoleBinding", - "rbac.authorization.k8s.io/v1/RoleBinding", - true, - }, - { - "io.k8s.api.rbac.v1beta1.ClusterRoleBinding", - "rbac.authorization.k8s.io/v1beta1/ClusterRoleBinding", - true, - }, - { - "io.crossplane.gcp.iam.v1alpha1.ServiceAccount", - "iam.gcp.crossplane.io/v1alpha1/ServiceAccount", - true, - }, - { - "io.crossplane.gcp.iam.v1alpha1.ServiceAccount", - "v1/ServiceAccount", - false, - }, - { - "v1.ServiceAccount", - "iam.gcp.crossplane.io/v1alpha1/ServiceAccount", - false, - }, - { - "io.k8s.api.rbac.v1.Role", - "rbac.authorization.k8s.io/v1/Role", - true, - }, - { - "io.k8s.api.rbac.v1.ClusterRole", - "rbac.authorization.k8s.io/v1/ClusterRole", - true, - }, - { - "io.k8s.api.flowcontrol.v1beta1.FlowSchema", - "flowcontrol.apiserver.k8s.io/v1beta1/FlowSchema", - true, - }, - { - "io.k8s.api.policy.v1beta1.Eviction", - "v1/Eviction", - false, - }, - { - "io.k8s.api.rbac.v1beta1.ClusterRole", - "rbac.authorization.k8s.io/v1beta1/ClusterRole", - true, - }, - { - "io.k8s.api.policy.v1.Eviction", - "v1/Eviction", - false, - }, - } - - for i, test := range testCases { - t.Run(test.definitionName, func(t *testing.T) { - res := matchGVK(test.definitionName, test.gvk) - assert.Equal(t, res, test.match, "test #%d failed", i) - }) - } -} - -// this test covers all supported Ingress -func Test_Ingress(t *testing.T) { - o, err := NewManager(logr.Discard()) - assert.NilError(t, err) - - versions, ok := o.kindToAPIVersions.Get("Ingress") - assert.Equal(t, true, ok) - - assert.Equal(t, versions.serverPreferredGVK, "networking.k8s.io/v1/Ingress") - assert.Equal(t, len(versions.gvks), 1) - - definitionName, _ := o.gvkToDefinitionName.Get("Ingress") - assert.Equal(t, definitionName, "io.k8s.api.networking.v1.Ingress") - - definitionName, _ = o.gvkToDefinitionName.Get("networking.k8s.io/v1/Ingress") - assert.Equal(t, definitionName, "io.k8s.api.networking.v1.Ingress") -} diff --git a/pkg/openapi/utils.go b/pkg/openapi/utils.go deleted file mode 100644 index bce9a458f2..0000000000 --- a/pkg/openapi/utils.go +++ /dev/null @@ -1,292 +0,0 @@ -package openapi - -import ( - "encoding/json" - "fmt" - "slices" - "strconv" - "strings" - "sync" - - "github.com/google/gnostic-models/compiler" - openapiv2 "github.com/google/gnostic-models/openapiv2" - "github.com/kyverno/kyverno/data" - "github.com/kyverno/kyverno/pkg/logging" - "gopkg.in/yaml.v3" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func parseGVK(str string) (group, apiVersion, kind string) { - if strings.Count(str, "/") == 0 { - return "", "", str - } - splitString := strings.Split(str, "/") - if strings.Count(str, "/") == 1 { - return "", splitString[0], splitString[1] - } - return splitString[0], splitString[1], splitString[2] -} - -func groupMatches(gvkMap map[string]bool, group, kind string) bool { - if group == "" { - ok := gvkMap["core"] - if ok { - return true - } - } else { - elements := strings.Split(group, ".") - ok := gvkMap[elements[0]] - if ok { - return true - } - } - return false -} - -// matchGVK is a helper function that checks if the given GVK matches the definition name -func matchGVK(definitionName, gvk string) bool { - paths := strings.Split(definitionName, ".") - - gvkMap := make(map[string]bool) - for _, p := range paths { - gvkMap[p] = true - } - - group, version, kind := parseGVK(gvk) - - ok := gvkMap[kind] - if !ok { - return false - } - ok = gvkMap[version] - if !ok { - return false - } - - if !groupMatches(gvkMap, group, kind) { - return false - } - - return true -} - -func getSchemaDocument() (*openapiv2.Document, error) { - var spec yaml.Node - err := yaml.Unmarshal([]byte(data.SwaggerDoc), &spec) - if err != nil { - return nil, err - } - - root := spec.Content[0] - return openapiv2.NewDocument(root, compiler.NewContext("$root", root, nil)) -} - -func getArrayValue(kindSchema *openapiv2.Schema, o *manager) interface{} { - var array []interface{} - for _, schema := range kindSchema.GetItems().GetSchema() { - array = append(array, o.generateEmptyResource(schema)) - } - - return array -} - -func getObjectValue(kindSchema *openapiv2.Schema, o *manager) interface{} { - props := make(map[string]interface{}) - properties := kindSchema.GetProperties().GetAdditionalProperties() - if len(properties) == 0 { - return props - } - - var wg sync.WaitGroup - var mutex sync.Mutex - wg.Add(len(properties)) - for _, property := range properties { - go func(property *openapiv2.NamedSchema) { - prop := o.generateEmptyResource(property.GetValue()) - mutex.Lock() - props[property.GetName()] = prop - mutex.Unlock() - wg.Done() - }(property) - } - wg.Wait() - return props -} - -func getBoolValue(kindSchema *openapiv2.Schema) bool { - if d := kindSchema.GetDefault(); d != nil { - v := getAnyValue(d) - return string(v) == "true" - } - - if e := kindSchema.GetExample(); e != nil { - v := getAnyValue(e) - return string(v) == "true" - } - - return false -} - -func getNumericValue(kindSchema *openapiv2.Schema) int64 { - if d := kindSchema.GetDefault(); d != nil { - v := getAnyValue(d) - val, _ := strconv.Atoi(string(v)) - return int64(val) - } - - if e := kindSchema.GetExample(); e != nil { - v := getAnyValue(e) - val, _ := strconv.Atoi(string(v)) - return int64(val) - } - - return int64(0) -} - -func getStringValue(kindSchema *openapiv2.Schema) string { - if d := kindSchema.GetDefault(); d != nil { - v := getAnyValue(d) - return string(v) - } - - if e := kindSchema.GetExample(); e != nil { - v := getAnyValue(e) - return string(v) - } - - return "" -} - -func getAnyValue(any *openapiv2.Any) []byte { - if any != nil { - if val := any.GetValue(); val != nil { - return val.GetValue() - } - } - - return nil -} - -// getAllAPIVersions gets all available versions for a kind -// returns a map which stores all kinds with its versions -func getAllAPIVersions(apiResourceLists []*metav1.APIResourceList) map[string]apiVersions { - tempKindToAPIVersions := make(map[string]apiVersions) - - for _, apiResourceList := range apiResourceLists { - lastKind := "" - for _, apiResource := range apiResourceList.APIResources { - if apiResource.Kind == lastKind { - continue - } - - version, ok := tempKindToAPIVersions[apiResource.Kind] - if !ok { - tempKindToAPIVersions[apiResource.Kind] = apiVersions{} - } - - gvk := strings.Join([]string{apiResourceList.GroupVersion, apiResource.Kind}, "/") - version.gvks = append(version.gvks, gvk) - tempKindToAPIVersions[apiResource.Kind] = version - lastKind = apiResource.Kind - } - } - - return tempKindToAPIVersions -} - -// setPreferredVersions sets the serverPreferredGVK of the given apiVersions map -func setPreferredVersions(kindToAPIVersions map[string]apiVersions, preferredAPIResourcesLists []*metav1.APIResourceList) map[string]apiVersions { - tempKindToAPIVersionsCopied := copyKindToAPIVersions(kindToAPIVersions) - - for kind, versions := range tempKindToAPIVersionsCopied { - for _, preferredAPIResourcesList := range preferredAPIResourcesLists { - for _, resource := range preferredAPIResourcesList.APIResources { - preferredGV := preferredAPIResourcesList.GroupVersion - preferredGVK := preferredGV + "/" + resource.Kind - - if slices.Contains(versions.gvks, preferredGVK) { - v := kindToAPIVersions[kind] - - // if a Kind belongs to multiple groups, the first group/version - // returned from discovery docs is used as preferred version - // https://github.com/kubernetes/kubernetes/issues/94761#issuecomment-691982480 - if v.serverPreferredGVK != "" { - continue - } - - v.serverPreferredGVK = strings.Join([]string{preferredGV, kind}, "/") - kindToAPIVersions[kind] = v - } - } - } - } - - return kindToAPIVersions -} - -func copyKindToAPIVersions(old map[string]apiVersions) map[string]apiVersions { - new := make(map[string]apiVersions, len(old)) - for key, value := range old { - new[key] = value - } - return new -} - -func getAPIResourceLists() ([]*metav1.APIResourceList, []*metav1.APIResourceList, error) { - var apiResourceLists []*metav1.APIResourceList - err := json.Unmarshal([]byte(data.APIResourceLists), &apiResourceLists) - if err != nil { - return nil, nil, fmt.Errorf("unable to load apiResourceLists: %v", err) - } - - var preferredAPIResourcesLists []*metav1.APIResourceList - err = json.Unmarshal([]byte(data.APIResourceLists), &preferredAPIResourcesLists) - if err != nil { - return nil, nil, fmt.Errorf("unable to load preferredAPIResourcesLists: %v", err) - } - - return apiResourceLists, preferredAPIResourcesLists, nil -} - -func isOpenV3Error(err error) bool { - unsupportedValues := []string{"anyOf", "allOf", "not"} - v3valueFound := false - for _, value := range unsupportedValues { - if !strings.Contains(err.Error(), fmt.Sprintf("has invalid property: %s", value)) { - v3valueFound = true - break - } - } - return v3valueFound -} - -// addingDefaultFieldsToSchema will add any default missing fields like apiVersion, metadata -func addingDefaultFieldsToSchema(crdName string, schemaRaw []byte) ([]byte, error) { - var schema struct { - Properties map[string]interface{} `json:"properties"` - } - _ = json.Unmarshal(schemaRaw, &schema) - - if len(schema.Properties) < 1 { - logging.V(6).Info("crd schema has no properties", "name", crdName) - return schemaRaw, nil - } - - if schema.Properties["apiVersion"] == nil { - apiVersionDefRaw := `{"description":"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources","type":"string"}` - apiVersionDef := make(map[string]interface{}) - _ = json.Unmarshal([]byte(apiVersionDefRaw), &apiVersionDef) - schema.Properties["apiVersion"] = apiVersionDef - } - - if schema.Properties["metadata"] == nil { - metadataDefRaw := `{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta","description":"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"}` - metadataDef := make(map[string]interface{}) - _ = json.Unmarshal([]byte(metadataDefRaw), &metadataDef) - schema.Properties["metadata"] = metadataDef - } - - schemaWithDefaultFields, _ := json.Marshal(schema) - - return schemaWithDefaultFields, nil -} diff --git a/pkg/validation/policy/fuzz_test.go b/pkg/validation/policy/fuzz_test.go index d9e76db875..014c6160a1 100644 --- a/pkg/validation/policy/fuzz_test.go +++ b/pkg/validation/policy/fuzz_test.go @@ -3,30 +3,17 @@ package policy import ( "testing" - "github.com/go-logr/logr" - kyverno "github.com/kyverno/kyverno/api/kyverno/v1" - "github.com/kyverno/kyverno/pkg/openapi" fuzz "github.com/AdaLogics/go-fuzz-headers" ) -var fuzzOpenApiManager openapi.Manager - -func init() { - var err error - fuzzOpenApiManager, err = openapi.NewManager(logr.Discard()) - if err != nil { - panic(err) - } -} - func FuzzValidatePolicy(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { ff := fuzz.NewConsumer(data) p := &kyverno.ClusterPolicy{} ff.GenerateStruct(p) - Validate(p, nil, nil, true, fuzzOpenApiManager, "admin") + Validate(p, nil, nil, true, "admin") }) } diff --git a/pkg/validation/policy/validate.go b/pkg/validation/policy/validate.go index b6c5f1483d..8d7a3e20ef 100644 --- a/pkg/validation/policy/validate.go +++ b/pkg/validation/policy/validate.go @@ -1,7 +1,6 @@ package policy import ( - "context" "encoding/json" "errors" "fmt" @@ -19,18 +18,15 @@ import ( kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/clients/dclient" - openapicontroller "github.com/kyverno/kyverno/pkg/controllers/openapi" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/engine/variables/regex" "github.com/kyverno/kyverno/pkg/logging" - "github.com/kyverno/kyverno/pkg/openapi" apiutils "github.com/kyverno/kyverno/pkg/utils/api" datautils "github.com/kyverno/kyverno/pkg/utils/data" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "github.com/kyverno/kyverno/pkg/utils/wildcard" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/yaml" @@ -120,14 +116,11 @@ func checkValidationFailureAction(spec *kyvernov1.Spec) []string { } // Validate checks the policy and rules declarations for required configurations -func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interface, mock bool, openApiManager openapi.Manager, username string) ([]string, error) { +func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interface, mock bool, username string) ([]string, error) { var warnings []string spec := policy.GetSpec() background := spec.BackgroundProcessingEnabled() mutateExistingOnPolicyUpdate := spec.GetMutateExistingOnPolicyUpdate() - if !mock { - openapicontroller.NewController(client, openApiManager).CheckSync(context.TODO()) - } warnings = append(warnings, checkValidationFailureAction(spec)...) var errs field.ErrorList @@ -145,11 +138,14 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf } } - var res []*metav1.APIResourceList - clusterResources := sets.New[string]() - if !mock { + getClusteredResources := func(invalidate bool) (sets.Set[string], error) { + clusterResources := sets.New[string]() // Get all the cluster type kind supported by cluster - res, err = discovery.ServerPreferredResources(client.Discovery().CachedDiscoveryInterface()) + d := client.Discovery().CachedDiscoveryInterface() + if invalidate { + d.Invalidate() + } + res, err := discovery.ServerPreferredResources(d) if err != nil { if discovery.IsGroupDiscoveryFailedError(err) { err := err.(*discovery.ErrGroupDiscoveryFailed) @@ -157,7 +153,7 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf logging.Error(err, "failed to list api resources", "group", gv) } } else { - return warnings, err + return nil, err } } for _, resList := range res { @@ -168,10 +164,29 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf } } } + return clusterResources, nil } + clusterResources := sets.New[string]() - if errs := policy.Validate(clusterResources); len(errs) != 0 { - return warnings, errs.ToAggregate() + // if not using a mock, we first try to validate and if it fails we retry with cache invalidation in between + if !mock { + clusterResources, err = getClusteredResources(false) + if err != nil { + return warnings, err + } + if errs := policy.Validate(clusterResources); len(errs) != 0 { + clusterResources, err = getClusteredResources(true) + if err != nil { + return warnings, err + } + if errs := policy.Validate(clusterResources); len(errs) != 0 { + return warnings, errs.ToAggregate() + } + } + } else { + if errs := policy.Validate(clusterResources); len(errs) != 0 { + return warnings, errs.ToAggregate() + } } if !policy.IsNamespaced() { @@ -364,11 +379,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf checkForDeprecatedFieldsInVerifyImages(rule, &warnings) } } - if !mock && (spec.SchemaValidation == nil || *spec.SchemaValidation) { - if err := openApiManager.ValidatePolicyMutation(policy); err != nil { - return warnings, fmt.Errorf("%s (you can bypass schema validation by setting `spec.schemaValidation: false`)", err) - } - } return warnings, nil } diff --git a/pkg/validation/policy/validate_test.go b/pkg/validation/policy/validate_test.go index aa288c81d8..3142814cca 100644 --- a/pkg/validation/policy/validate_test.go +++ b/pkg/validation/policy/validate_test.go @@ -1,3354 +1,3354 @@ package policy -import ( - "encoding/json" - "errors" - "fmt" - "testing" - - "github.com/go-logr/logr" - kyverno "github.com/kyverno/kyverno/api/kyverno/v1" - "github.com/kyverno/kyverno/pkg/openapi" - "gotest.tools/assert" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/validation/field" -) - -func Test_Validate_ResourceDescription_Empty(t *testing.T) { - var err error - rawResourcedescirption := []byte(`{}`) - - var rd kyverno.ResourceDescription - err = json.Unmarshal(rawResourcedescirption, &rd) - assert.NilError(t, err) - - _, err = validateMatchedResourceDescription(rd) - assert.Assert(t, err != nil) -} - -func Test_Validate_ResourceDescription_MatchedValid(t *testing.T) { - rawResourcedescirption := []byte(` - { - "kinds": [ - "Deployment" - ], - "selector": { - "matchLabels": { - "app.type": "prod" - } - } - }`) - - var rd kyverno.ResourceDescription - err := json.Unmarshal(rawResourcedescirption, &rd) - assert.NilError(t, err) - - _, err = validateMatchedResourceDescription(rd) - assert.NilError(t, err) -} - -func Test_Validate_DenyConditions_KeyRequestOperation_Empty(t *testing.T) { - denyConditions := []byte(`[]`) - - var dcs apiextensions.JSON - err := json.Unmarshal(denyConditions, &dcs) - assert.NilError(t, err) - - _, err = validateConditions(dcs, "conditions") - assert.NilError(t, err) - - _, err = validateConditions(dcs, "conditions") - assert.NilError(t, err) -} - -func Test_Validate_Preconditions_KeyRequestOperation_Empty(t *testing.T) { - preConditions := []byte(`[]`) - - var pcs apiextensions.JSON - err := json.Unmarshal(preConditions, &pcs) - assert.NilError(t, err) - - _, err = validateConditions(pcs, "preconditions") - assert.NilError(t, err) - - _, err = validateConditions(pcs, "preconditions") - assert.NilError(t, err) -} - -func Test_Validate_DenyConditionsValuesString_KeyRequestOperation_ExpectedValue(t *testing.T) { - denyConditions := []byte(` - [ - { - "key":"{{request.operation}}", - "operator":"Equals", - "value":"DELETE" - }, - { - "key":"{{request.operation}}", - "operator":"NotEquals", - "value":"CREATE" - }, - { - "key":"{{request.operation}}", - "operator":"NotEquals", - "value":"CONNECT" - }, - { - "key":"{{ request.operation }}", - "operator":"NotEquals", - "value":"UPDATE" - }, - { - "key":"{{lbServiceCount}}", - "operator":"Equals", - "value":"2" - } - ] - `) - - var dcs apiextensions.JSON - err := json.Unmarshal(denyConditions, &dcs) - assert.NilError(t, err) - - _, err = validateConditions(dcs, "conditions") - assert.NilError(t, err) - - _, err = validateConditions(dcs, "conditions") - assert.NilError(t, err) -} - -func Test_Validate_DenyConditionsValuesString_KeyRequestOperation_RightfullyTemplatizedValue(t *testing.T) { - denyConditions := []byte(` - [ - { - "key":"{{request.operation}}", - "operator":"Equals", - "value":"{{ \"ops-cm\".data.\"deny-ops\"}}" - }, - { - "key":"{{ request.operation }}", - "operator":"NotEquals", - "value":"UPDATE" - } - ] - `) - - var dcs apiextensions.JSON - err := json.Unmarshal(denyConditions, &dcs) - assert.NilError(t, err) - - _, err = validateConditions(dcs, "conditions") - assert.NilError(t, err) - - _, err = validateConditions(dcs, "conditions") - assert.NilError(t, err) -} - -func Test_Validate_DenyConditionsValuesString_KeyRequestOperation_WrongfullyTemplatizedValue(t *testing.T) { - denyConditions := []byte(` - [ - { - "key":"{{request.operation}}", - "operator":"Equals", - "value":"{{ \"ops-cm\".data.\"deny-ops\" }" - }, - { - "key":"{{ request.operation }}", - "operator":"NotEquals", - "value":"UPDATE" - } - ] - `) - - var dcs []kyverno.Condition - err := json.Unmarshal(denyConditions, &dcs) - assert.NilError(t, err) - - _, err = validateConditions(dcs, "conditions") - assert.Assert(t, err != nil) -} - -func Test_Validate_PreconditionsValuesString_KeyRequestOperation_UnknownValue(t *testing.T) { - preConditions := []byte(` - [ - { - "key":"{{request.operation}}", - "operator":"Equals", - "value":"foobar" - }, - { - "key": "{{request.operation}}", - "operator": "NotEquals", - "value": "CREATE" - } - ] - `) - - var pcs apiextensions.JSON - err := json.Unmarshal(preConditions, &pcs) - assert.NilError(t, err) - - _, err = validateConditions(pcs, "preconditions") - assert.Assert(t, err != nil) - - _, err = validateConditions(pcs, "preconditions") - assert.Assert(t, err != nil) -} - -func Test_Validate_DenyConditionsValuesList_KeyRequestOperation_ExpectedItem(t *testing.T) { - denyConditions := []byte(` - [ - { - "key":"{{request.operation}}", - "operator":"Equals", - "value": [ - "CREATE", - "DELETE", - "CONNECT" - ] - }, - { - "key":"{{request.operation}}", - "operator":"NotEquals", - "value": [ - "UPDATE" - ] - }, - { - "key": "{{lbServiceCount}}", - "operator": "Equals", - "value": "2" - } - ] - `) - - var dcs []kyverno.Condition - err := json.Unmarshal(denyConditions, &dcs) - assert.NilError(t, err) - - _, err = validateConditions(dcs, "conditions") - assert.NilError(t, err) -} - -func Test_Validate_PreconditionsValuesList_KeyRequestOperation_UnknownItem(t *testing.T) { - preConditions := []byte(` - [ - { - "key":"{{request.operation}}", - "operator":"Equals", - "value": [ - "foobar", - "CREATE" - ] - }, - { - "key":"{{request.operation}}", - "operator":"NotEquals", - "value": [ - "foobar" - ] - } - ] - `) - - var pcs apiextensions.JSON - err := json.Unmarshal(preConditions, &pcs) - assert.NilError(t, err) - - _, err = validateConditions(pcs, "preconditions") - assert.Assert(t, err != nil) - - _, err = validateConditions(pcs, "preconditions") - assert.Assert(t, err != nil) -} - -func Test_Validate_Policy(t *testing.T) { - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "container-security-context" - }, - "spec": { - "rules": [ - { - "name": "validate-runAsNonRoot", - "match": { - "resources": { - "kinds": [ - "Deployment" - ], - "selector": { - "matchLabels": { - "app.type": "prod" - } - } - } - }, - "validate": { - "message": "validate container security contexts", - "anyPattern": [ - { - "spec": { - "template": { - "spec": { - "^(containers)": [ - { - "securityContext": { - "runAsNonRoot": "true" - } - } - ] - } - } - } - } - ] - } - }, - { - "name": "validate-allowPrivilegeEscalation", - "match": { - "resources": { - "kinds": [ - "Deployment" - ], - "selector": { - "matchLabels": { - "app.type": "prod" - } - } - } - }, - "validate": { - "message": "validate container security contexts", - "pattern": { - "spec": { - "template": { - "spec": { - "^(containers)": [ - { - "securityContext": { - "allowPrivilegeEscalation": "false" - } - } - ] - } - } - } - } - } - } - ] - } - }`) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.NilError(t, err) -} - -func Test_Validate_ErrorFormat(t *testing.T) { - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "test-error-format" - }, - "spec": { - "rules": [ - { - "name": "image-pull-policy", - "match": { - "resources": { - "kinds": [ - "Deployment" - ], - "selector": { - "matchLabels": { - "app": "nginxlatest" - } - } - } - }, - "exclude": { - "resources": { - "selector": { - "app": "nginxlatest" - } - } - }, - "mutate": { - "patchStrategicMerge": { - "spec": { - "template": { - "spec": { - "containers": [ - { - "=(image)": "*latest", - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - } - } - }, - { - "name": "validate-user-privilege", - "match": { - "resources": { - "kinds": [], - "selector": { - "matchLabels": { - "app.type": "prod" - } - } - } - }, - "validate": { - "message": "validate container security contexts", - "anyPattern": [ - { - "spec": { - "template": { - "spec": { - "^(containers)": [ - { - "securityContext": { - "runAsNonRoot": "true" - } - } - ] - } - } - } - } - ] - } - }, - { - "name": "validate-user-privilege", - "match": { - "resources": { - "kinds": [ - "Deployment" - ], - "selector": { - "matchLabels": { - "app.type": "prod" - } - } - } - }, - "validate": { - "message": "validate container security contexts", - "pattern": { - "spec": { - "template": { - "spec": { - "containers": [ - { - "^(securityContext)": { - "allowPrivilegeEscalation": "false" - } - } - ] - } - } - } - } - } - }, - { - "name": "default-networkpolicy", - "match": { - "resources": { - "kinds": [ - "Namespace" - ], - "name": "devtest" - } - }, - "generate": { - "kind": "ConfigMap", - "name": "copied-cm", - "clone": { - "^(namespace)": "default", - "name": "game-config" - } - } - } - ] - } - } - `) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Assert(t, err != nil) -} - -func Test_BackGroundUserInfo_match_roles(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "match.roles", - "match": { - "roles": [ - "a", - "b" - ] - } - } - ] - } - } - `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = containsUserVariables(policy, nil) - assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/roles") -} - -func Test_BackGroundUserInfo_match_clusterRoles(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "match.clusterRoles", - "match": { - "clusterRoles": [ - "a", - "b" - ] - } - } - ] - } - } - `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = containsUserVariables(policy, nil) - assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/clusterRoles") -} - -func Test_BackGroundUserInfo_match_subjects(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "match.subjects", - "match": { - "subjects": [ - { - "Name": "a" - }, - { - "Name": "b" - } - ] - } - } - ] - } - } `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = containsUserVariables(policy, nil) - assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/subjects") -} - -func Test_BackGroundUserInfo_mutate_patchStrategicMerge1(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "mutate.overlay1", - "mutate": { - "patchStrategicMerge": { - "var1": "{{request.userInfo}}" - } - } - } - ] - } - } - `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = ValidateVariables(policy, true) - assert.Assert(t, err != nil) -} - -func Test_Context_Variable_Substitution(t *testing.T) { - var err error - rawPolicy := []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "check-images" - }, - "spec": { - "validationFailureAction": "Enforce", - "webhookTimeoutSeconds": 30, - "rules": [ - { - "name": "call-aws-signer-extension", - "match": { - "any": [ - { - "resources": { - "namespaces": [ - "test-notation" - ], - "kinds": [ - "Pod" - ] - } - } - ] - }, - "context": [ - { - "name": "response", - "apiCall": { - "method": "POST", - "data": [ - { - "key": "imagesInfo", - "value": "{{ images }}" - } - ], - "service": { - "url": "https://svc.kyverno-notation-aws/checkimages", - "caBundle": "-----BEGIN CERTIFICATE-----\nMIICizCCAjGgAwIBAgIRAIUEJcm7TtwJEtRtsI2yUcMwCgYIKoZIzj0EAwIwGzEZ\nMBcGA1UEAxMQbXktc2VsZnNpZ25lZC1jYTAeFw0yMzA1MTAwNTI5MzBaFw0yMzA4\nMDgwNTI5MzBaMDExEDAOBgNVBAoTB25pcm1hdGExHTAbBgNVBAMTFGt5dmVybm8t\nbm90YXRpb24tYXdzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrSr\nVXUbuQbF4rhh0/jqDE6agtXqS9jko6vHTEZUF2Y9f0LdSycEdCocIKZmPerWER7l\nVUmMFPQLSGOZrCIM22L9+EXDyL7q2PN3koDxKOyqVOod8j3hKdRL+KIiZuUeD4zD\ncos+AFxA1XAM/220JKfPSUpBL0DAP299Baqjs/Ae5wU5wT4qZVa1I3pcV2uicPvE\nRSZO3ZT+y1nYBWtTTzzXP3f9ou8IHweCl57Sk16mbFFZ+TrCSekewYchzn88z7lq\nL+56LtBUjcJozypLGEWM+kc4S5wBNYUaFPGiCHIrdQ5ScmfnY7mDvO8u47E+xw13\nbz7NUAlT73rBqBv6hQIDAQABo3UwczAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB\nBQUHAwIwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRXZIp2KalD6pjRfPua2kFn\nMBuJJTAjBgNVHREEHDAaghhzdmMua3l2ZXJuby1ub3RhdGlvbi1hd3MwCgYIKoZI\nzj0EAwIDSAAwRQIhAKob5SV/N56VqP8VPdHqCAULRj92qhWwW3hb7fzaGxnHAiBP\n3c8K2Vrxx2KRsjnWwn1vUMz7UyM2Tmib1C4YM3f+xg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIBdjCCAR2gAwIBAgIRAP1VDXD3R744lE7t/I5MK44wCgYIKoZIzj0EAwIwGzEZ\nMBcGA1UEAxMQbXktc2VsZnNpZ25lZC1jYTAeFw0yMzA1MTAwNTI5MjVaFw0yMzA4\nMDgwNTI5MjVaMBsxGTAXBgNVBAMTEG15LXNlbGZzaWduZWQtY2EwWTATBgcqhkjO\nPQIBBggqhkjOPQMBBwNCAAQrFCRBF8PjKPcT/lrXmyP474fNuhlhGFAlLaoTSUuP\nS3VK2O7hWrlJ/AhCccY8EPBi/DdFEaCB2+hTo00clmvfo0IwQDAOBgNVHQ8BAf8E\nBAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUV2SKdimpQ+qY0Xz7mtpB\nZzAbiSUwCgYIKoZIzj0EAwIDRwAwRAIgU3O7Qnk9PGCV4aXgZAXp0h4Iz2O7XUnP\nUfv4SgD7neECIHLb+BDvRFPJ77FpfIYxBO70AHB7Kp0nWKCqyv3FK4aT\n-----END CERTIFICATE-----" - } - } - } - ], - "validate": { - "message": "not allowed", - "deny": { - "conditions": { - "all": [ - { - "key": "{{ response.verified }}", - "operator": "EQUALS", - "value": false - } - ] - } - } - } - } - ] - } -}`) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = ValidateVariables(policy, true) - assert.NilError(t, err) -} - -func Test_BackGroundUserInfo_mutate_patchStrategicMerge2(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "mutate.overlay2", - "mutate": { - "patchStrategicMerge": { - "var1": "{{request.userInfo.userName}}" - } - } - } - ] - } - } - `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = ValidateVariables(policy, true) - assert.Assert(t, err != nil) -} - -func Test_BackGroundUserInfo_validate_pattern(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "validate-patch-strategic-merge", - "validate": { - "pattern": { - "var1": "{{request.userInfo}}" - } - } - } - ] - } - } - `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = ValidateVariables(policy, true) - assert.Assert(t, err != nil, err) -} - -func Test_BackGroundUserInfo_validate_anyPattern(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "validate.anyPattern", - "validate": { - "anyPattern": [ - { - "var1": "temp" - }, - { - "var1": "{{request.userInfo}}" - } - ] - } - } - ] - } - } `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = ValidateVariables(policy, true) - assert.Assert(t, err != nil) -} - -func Test_BackGroundUserInfo_validate_anyPattern_multiple_var(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "validate.anyPattern", - "validate": { - "anyPattern": [ - { - "var1": "temp" - }, - { - "var1": "{{request.userInfo}}-{{temp}}" - } - ] - } - } - ] - } - } `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = ValidateVariables(policy, true) - assert.Assert(t, err != nil) -} - -func Test_BackGroundUserInfo_validate_anyPattern_serviceAccount(t *testing.T) { - var err error - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "disallow-root-user" - }, - "spec": { - "rules": [ - { - "name": "validate.anyPattern", - "validate": { - "anyPattern": [ - { - "var1": "temp" - }, - { - "var1": "{{serviceAccountName}}" - } - ] - } - } - ] - } - } `) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - err = ValidateVariables(policy, true) - assert.Assert(t, err != nil) -} - -func Test_ruleOnlyDealsWithResourceMetaData(t *testing.T) { - testcases := []struct { - description string - rule []byte - expectedOutput bool - }{ - { - description: "Test mutate patchStrategicMerge - pass", - rule: []byte(`{"name":"testPatches1","mutate":{"patchStrategicMerge":{"metadata":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}`), - expectedOutput: true, - }, - { - description: "Test mutate patchStrategicMerge - fail", - rule: []byte(`{"name":"testPatches2","mutate":{"patchStrategicMerge":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}`), - expectedOutput: false, - }, - { - description: "Test mutate patch - pass", - rule: []byte(`{"name":"testPatches3","mutate":{"patchesJson6902": "[{\"path\":\"/metadata/labels/isMutated\",\"op\":\"add\",\"value\":\"true\"},{\"path\":\"/metadata/labels/app\",\"op\":\"replace\",\"value\":\"nginx_is_mutated\"}]"}}`), - expectedOutput: true, - }, - { - description: "Test mutate patch - fail", - rule: []byte(`{"name":"testPatches4","mutate":{"patchesJson6902": "[{\"path\":\"/spec/labels/isMutated\",\"op\":\"add\",\"value\":\"true\"},{\"path\":\"/metadata/labels/app\",\"op\":\"replace\",\"value\":\"nginx_is_mutated\"}]" }}`), - expectedOutput: false, - }, - { - description: "Test validate - pass", - rule: []byte(`{"name":"testValidate1","validate":{"message":"CPU and memory resource requests and limits are required","pattern":{"metadata":{"containers":[{"(name)":"*","ports":[{"containerPort":80}]}]}}}}`), - expectedOutput: true, - }, - { - description: "Test validate - fail", - rule: []byte(`{"name":"testValidate2","validate":{"message":"CPU and memory resource requests and limits are required","pattern":{"spec":{"containers":[{"(name)":"*","ports":[{"containerPort":80}]}]}}}}`), - expectedOutput: false, - }, - { - description: "Test validate any pattern - pass", - rule: []byte(`{"name":"testValidateAnyPattern1","validate":{"message":"Volumes white list","anyPattern":[{"metadata":{"volumes":[{"hostPath":"*"}]}},{"metadata":{"volumes":[{"emptyDir":"*"}]}},{"metadata":{"volumes":[{"configMap":"*"}]}}]}}`), - expectedOutput: true, - }, - { - description: "Test validate any pattern - fail", - rule: []byte(`{"name":"testValidateAnyPattern2","validate":{"message":"Volumes white list","anyPattern":[{"spec":{"volumes":[{"hostPath":"*"}]}},{"metadata":{"volumes":[{"emptyDir":"*"}]}},{"metadata":{"volumes":[{"configMap":"*"}]}}]}}`), - expectedOutput: false, - }, - } - - for i, testcase := range testcases { - var rule kyverno.Rule - _ = json.Unmarshal(testcase.rule, &rule) - output := ruleOnlyDealsWithResourceMetaData(rule) - if output != testcase.expectedOutput { - t.Errorf("Testcase [%d] (%s) failed", i+1, testcase.description) - } - } -} - -func Test_Validate_Kind(t *testing.T) { - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "policy-to-monitor-root-user-access" - }, - "spec": { - "validationFailureAction": "audit", - "rules": [ - { - "name": "monitor-annotation-for-root-user-access", - "match": { - "resources": { - "selector": { - "matchLabels": { - "AllowRootUserAccess": "true" - } - } - } - }, - "validate": { - "message": "Label provisioner.wg.net/cloudprovider is required", - "pattern": { - "metadata": { - "labels": { - "provisioner.wg.net/cloudprovider": "*" - } - } - } - } - } - ] - } - } - `) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Assert(t, err != nil) -} - -func Test_Validate_Any_Kind(t *testing.T) { - rawPolicy := []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "policy-to-monitor-root-user-access" - }, - "spec": { - "validationFailureAction": "audit", - "rules": [ - { - "name": "monitor-annotation-for-root-user-access", - "match": { - "any": [ - { - "resources": { - "selector": { - "matchLabels": { - "AllowRootUserAccess": "true" - } - } - } - } - ] - }, - "validate": { - "message": "Label provisioner.wg.net/cloudprovider is required", - "pattern": { - "metadata": { - "labels": { - "provisioner.wg.net/cloudprovider": "*" - } - } - } - } - } - ] - } - }`) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Assert(t, err != nil) -} - -func Test_Validate_ApiCall(t *testing.T) { - testCases := []struct { - resource kyverno.ContextEntry - expectedResult interface{} - }{ - { - resource: kyverno.ContextEntry{ - APICall: &kyverno.APICall{ - URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies", - JMESPath: "", - }, - }, - expectedResult: nil, - }, - { - resource: kyverno.ContextEntry{ - APICall: &kyverno.APICall{ - URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies", - JMESPath: "items[", - }, - }, - expectedResult: "failed to parse JMESPath items[: SyntaxError: Expected tStar, received: tEOF", - }, - { - resource: kyverno.ContextEntry{ - APICall: &kyverno.APICall{ - URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies", - JMESPath: "items[{{request.namespace}}", - }, - }, - expectedResult: nil, - }, - } - - for _, testCase := range testCases { - err := validateAPICall(testCase.resource) - - if err == nil { - assert.Equal(t, err, testCase.expectedResult) - } else { - assert.Equal(t, err.Error(), testCase.expectedResult) - } - } -} - -func Test_Wildcards_Kind(t *testing.T) { - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "require-labels" - }, - "spec": { - "validationFailureAction": "enforce", - "rules": [ - { - "name": "check-for-labels", - "match": { - "resources": { - "kinds": [ - "*" - ] - } - }, - "validate": { - "message": "label 'app.kubernetes.io/name' is required", - "pattern": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "?*" - } - } - } - } - } - ] - } - } - `) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Assert(t, err != nil) -} - -func Test_Namespced_Policy(t *testing.T) { - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "Policy", - "metadata": { - "name": "evil-policy-match-foreign-pods", - "namespace": "customer-foo" - }, - "spec": { - "validationFailureAction": "enforce", - "background": false, - "rules": [ - { - "name": "evil-validation", - "match": { - "resources": { - "kinds": [ - "Pod" - ], - "namespaces": [ - "customer-bar" - ] - } - }, - "validate": { - "message": "Mua ah ah ... you've been pwned by customer-foo", - "pattern": { - "metadata": { - "annotations": { - "pwned-by-customer-foo": "true" - } - } - } - } - } - ] - } - } - `) - - var policy *kyverno.Policy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Assert(t, err != nil) -} - -func Test_patchesJson6902_Policy(t *testing.T) { - rawPolicy := []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "set-max-surge-yaml-to-json" - }, - "spec": { - "background": false, - "schemaValidation": false, - "rules": [ - { - "name": "set-max-surge", - "context": [ - { - "name": "source", - "configMap": { - "name": "source-yaml-to-json", - "namespace": "default" - } - } - ], - "match": { - "resources": { - "kinds": [ - "Deployment" - ] - } - }, - "mutate": { - "patchesJson6902": "- op: replace\n path: /spec/strategy\n value: {{ source.data.strategy }}" - } - } - ] - } -} - `) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.NilError(t, err) -} - -func Test_deny_exec(t *testing.T) { - var err error - rawPolicy := []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "deny-exec-to-pod" - }, - "spec": { - "validationFailureAction": "enforce", - "background": false, - "schemaValidation": false, - "rules": [ - { - "name": "deny-pod-exec", - "match": { - "resources": { - "kinds": [ - "PodExecOptions" - ] - } - }, - "preconditions": { - "all": [ - { - "key": "{{ request.operation }}", - "operator": "Equals", - "value": "CONNECT" - } - ] - }, - "validate": { - "message": "Containers can't be exec'd into in production.", - "deny": {} - } - } - ] - } - }`) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.NilError(t, err) -} - -func Test_SignatureAlgorithm(t *testing.T) { - testcases := []struct { - description string - policy []byte - expectedOutput bool - }{ - { - description: "Test empty signature algorithm - pass", - policy: []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "check-empty-signature-algorithm" - }, - "spec": { - "rules": [ - { - "match": { - "resources": { - "kinds": [ - "Pod" - ] - } - }, - "verifyImages": [ - { - "imageReferences": [ - "ghcr.io/kyverno/test-verify-image:*" - ], - "attestors": [ - { - "count": 1, - "entries": [ - { - "keys": { - "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----" - } - } - ] - } - ] - } - ] - } - ] - } - }`), - expectedOutput: true, - }, - { - description: "Test invalid signature algorithm - fail", - policy: []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "check-invalid-signature-algorithm" - }, - "spec": { - "rules": [ - { - "match": { - "resources": { - "kinds": [ - "Pod" - ] - } - }, - "verifyImages": [ - { - "imageReferences": [ - "ghcr.io/kyverno/test-verify-image:*" - ], - "attestors": [ - { - "count": 1, - "entries": [ - { - "keys": { - "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----", - "signatureAlgorithm": "sha123" - } - } - ] - } - ] - } - ] - } - ] - } - }`), - expectedOutput: false, - }, - { - description: "Test invalid signature algorithm - fail", - policy: []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "check-valid-signature-algorithm" - }, - "spec": { - "rules": [ - { - "match": { - "resources": { - "kinds": [ - "Pod" - ] - } - }, - "verifyImages": [ - { - "imageReferences": [ - "ghcr.io/kyverno/test-verify-image:*" - ], - "attestors": [ - { - "count": 1, - "entries": [ - { - "keys": { - "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----", - "signatureAlgorithm": "sha256" - } - } - ] - } - ] - } - ] - } - ] - } - }`), - expectedOutput: true, - }, - } - for _, testcase := range testcases { - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(testcase.policy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - if testcase.expectedOutput { - assert.NilError(t, err) - } else { - assert.ErrorContains(t, err, "Invalid signature algorithm provided") - } - } -} - -func Test_existing_resource_policy(t *testing.T) { - var err error - rawPolicy := []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "np-test-1" - }, - "spec": { - "validationFailureAction": "audit", - "rules": [ - { - "name": "no-LoadBalancer", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "networking.k8s.io/v1/NetworkPolicy" - ] - } - } - ] - }, - "validate": { - "message": "np-test", - "pattern": { - "metadata": { - "name": "?*" - } - } - } - } - ] - } - }`) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.NilError(t, err) -} - -func Test_PodControllerAutoGenExclusion_All_Controllers_Policy(t *testing.T) { - rawPolicy := []byte(` -{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "add-all-pod-controller-annotations", - "annotations": { - "pod-policies.kyverno.io/autogen-controllers": "DaemonSet,Job,CronJob,Deployment,StatefulSet" - } - }, - "spec": { - "validationFailureAction": "Enforce", - "background": false, - "rules": [ - { - "name": "validate-livenessProbe-readinessProbe", - "match": { - "resources": { - "kinds": [ - "Pod" - ] - } - }, - "validate": { - "message": "Liveness and readiness probes are required.", - "pattern": { - "spec": { - "containers": [ - { - "livenessProbe": { - "periodSeconds": ">0" - }, - "readinessProbe": { - "periodSeconds": ">0" - } - } - ] - } - } - } - } - ] - } - } -`) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - res, err := Validate(policy, nil, nil, true, openApiManager, "admin") - assert.NilError(t, err) - assert.Assert(t, res == nil) -} - -func Test_PodControllerAutoGenExclusion_Not_All_Controllers_Policy(t *testing.T) { - rawPolicy := []byte(` -{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "add-not-all-pod-controller-annotations", - "annotations": { - "pod-policies.kyverno.io/autogen-controllers": "DaemonSet,Job,CronJob,Deployment" - } - }, - "spec": { - "validationFailureAction": "Enforce", - "background": false, - "rules": [ - { - "name": "validate-livenessProbe-readinessProbe", - "match": { - "resources": { - "kinds": [ - "Pod" - ] - } - }, - "validate": { - "message": "Liveness and readiness probes are required.", - "pattern": { - "spec": { - "containers": [ - { - "livenessProbe": { - "periodSeconds": ">0" - }, - "readinessProbe": { - "periodSeconds": ">0" - } - } - ] - } - } - } - } - ] - } - } -`) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - warnings, err := Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Assert(t, warnings != nil) - assert.NilError(t, err) -} - -func Test_PodControllerAutoGenExclusion_None_Policy(t *testing.T) { - rawPolicy := []byte(` -{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "add-none-pod-controller-annotations", - "annotations": { - "pod-policies.kyverno.io/autogen-controllers": "none" - } - }, - "spec": { - "validationFailureAction": "Enforce", - "background": false, - "rules": [ - { - "name": "validate-livenessProbe-readinessProbe", - "match": { - "resources": { - "kinds": [ - "Pod" - ] - } - }, - "validate": { - "message": "Liveness and readiness probes are required.", - "pattern": { - "spec": { - "containers": [ - { - "livenessProbe": { - "periodSeconds": ">0" - }, - "readinessProbe": { - "periodSeconds": ">0" - } - } - ] - } - } - } - } - ] - } - } -`) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - warnings, err := Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Assert(t, warnings == nil) - assert.NilError(t, err) -} - -func Test_ValidateJSON6902(t *testing.T) { - var patch string = `- path: "/metadata/labels/img" - op: addition - value: "nginx"` - err := validateJSONPatch(patch, 0) - assert.Error(t, err, "unexpected kind: spec.rules[0]: addition") - - patch = `- path: "/metadata/labels/img" - op: add - value: "nginx"` - err = validateJSONPatch(patch, 0) - assert.NilError(t, err) - - patch = `- path: "/metadata/labels/img" - op: add - value: "nginx"` - err = validateJSONPatch(patch, 0) - assert.NilError(t, err) -} - -func Test_ValidateNamespace(t *testing.T) { - testcases := []struct { - description string - spec *kyverno.Spec - expectedError error - }{ - { - description: "tc1", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("conflicting namespaces found in path: spec.validationFailureActionOverrides[1].namespaces: default"), - }, - { - description: "tc2", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Mutation: kyverno.Mutation{ - RawPatchStrategicMerge: &apiextv1.JSON{Raw: []byte(`"metadata": {"labels": {"app-name": "{{request.object.metadata.name}}"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("conflicting namespaces found in path: spec.validationFailureActionOverrides[1].namespaces: default"), - }, - { - description: "tc3", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default*", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern 'default*' matches with namespace 'default'"), - }, - { - description: "tc4", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "*", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '*' matches with namespace 'default'"), - }, - { - description: "tc5", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "?*", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '?*' matches with namespace 'default'"), - }, - { - description: "tc6", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default?", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default1", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern 'default?' matches with namespace 'default1'"), - }, - { - description: "tc7", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default*", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "?*", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '?*' matches with namespace 'test'"), - }, - { - description: "tc8", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "*", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "?*", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '?*' conflicts with the pattern '*'"), - }, - { - description: "tc9", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default*", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default", - "test*", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern 'test*' matches with namespace 'test'"), - }, - { - description: "tc10", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "*efault", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '*efault' matches with namespace 'default'"), - }, - { - description: "tc11", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default-*", - "test", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - }, - { - description: "tc12", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default*?", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default", - "test*", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - }, - { - description: "tc13", - spec: &kyverno.Spec{ - ValidationFailureAction: "Enforce", - ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ - { - Action: "Enforce", - Namespaces: []string{ - "default?", - }, - }, - { - Action: "Audit", - Namespaces: []string{ - "default", - }, - }, - }, - Rules: []kyverno.Rule{ - { - Name: "require-labels", - MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, - Validation: kyverno.Validation{ - Message: "label 'app.kubernetes.io/name' is required", - RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, - }, - }, - }, - }, - }, - } - - for _, tc := range testcases { - t.Run(tc.description, func(t *testing.T) { - err := validateNamespaces(tc.spec, field.NewPath("spec").Child("validationFailureActionOverrides")) - if tc.expectedError != nil { - assert.Error(t, err, tc.expectedError.Error()) - } else { - assert.NilError(t, err) - } - }) - } -} - -func testResourceList() []*metav1.APIResourceList { - return []*metav1.APIResourceList{ - { - GroupVersion: "v1", - APIResources: []metav1.APIResource{ - {Name: "pods", Namespaced: true, Kind: "Pod"}, - {Name: "services", Namespaced: true, Kind: "Service"}, - {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"}, - {Name: "componentstatuses", Namespaced: false, Kind: "ComponentStatus"}, - {Name: "nodes", Namespaced: false, Kind: "Node"}, - {Name: "secrets", Namespaced: true, Kind: "Secret"}, - {Name: "configmaps", Namespaced: true, Kind: "ConfigMap"}, - {Name: "namespacedtype", Namespaced: true, Kind: "NamespacedType"}, - {Name: "namespaces", Namespaced: false, Kind: "Namespace"}, - {Name: "resourcequotas", Namespaced: true, Kind: "ResourceQuota"}, - }, - }, - { - GroupVersion: "apps/v1", - APIResources: []metav1.APIResource{ - {Name: "deployments", Namespaced: true, Kind: "Deployment"}, - {Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"}, - }, - }, - { - GroupVersion: "storage.k8s.io/v1", - APIResources: []metav1.APIResource{ - {Name: "storageclasses", Namespaced: false, Kind: "StorageClass"}, - }, - }, - } -} - -func Test_Any_wildcard_policy(t *testing.T) { - var err error - rawPolicy := []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "verify-image" - }, - "spec": { - "validationFailureAction": "enforce", - "background": false, - "rules": [ - { - "name": "verify-image", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "*" - ] - } - } - ] - }, - "verifyImages": [ - { - "imageReferences": [ - "ghcr.io/kyverno/test-verify-image:*" - ], - "mutateDigest": true, - "attestors": [ - { - "entries": [ - { - "keys": { - "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY----- \n" - } - } - ] - } - ] - } - ] - } - ] - } - }`) - var policy *kyverno.ClusterPolicy - err = json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, err = Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Assert(t, err != nil) -} - -func Test_Validate_RuleImageExtractorsJMESPath(t *testing.T) { - rawPolicy := []byte(`{ - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "jmes-path-and-mutate-digest" - }, - "spec": { - "rules": [ - { - "match": { - "resources": { - "kinds": [ - "CRD" - ] - } - }, - "imageExtractors": { - "CRD": [ - { - "path": "/path/to/image/prefixed/with/scheme", - "jmesPath": "trim_prefix(@, 'docker://')" - } - ] - }, - "verifyImages": [ - { - "mutateDigest": true, - "attestors": [ - { - "count": 1, - "entries": [ - { - "keys": { - "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----" - } - } - ] - } - ] - } - ] - } - ] - } - }`) - - var policy *kyverno.ClusterPolicy - err := json.Unmarshal(rawPolicy, &policy) - assert.NilError(t, err) - - expectedErr := fmt.Errorf("path: spec.rules[0]: jmespath may not be used in an image extractor when mutating digests with verify images") - - openApiManager, _ := openapi.NewManager(logr.Discard()) - _, actualErr := Validate(policy, nil, nil, true, openApiManager, "admin") - assert.Equal(t, expectedErr.Error(), actualErr.Error()) -} - -func Test_ImmutableGenerateFields(t *testing.T) { - tests := []struct { - name string - oldPolicy []byte - newPolicy []byte - expectedErr bool - }{ - { - name: "update-apiVersion", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "apps/v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-kind", - oldPolicy: []byte(` -{ - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } -}`), - newPolicy: []byte(` -{ - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Configmap", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } -}`), - expectedErr: true, - }, - { - name: "update-namespace", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.labels.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-name", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "new-name", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-sync-flag", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": false, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - expectedErr: false, - }, - { - name: "update-clone-name", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "modifed-name" - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-clone-namespace", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "default", - "name": "regcred" - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "modifed-namespace", - "name": "regcred" - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-clone-namespace-unset-new", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "namespace": "prod", - "name": "regcred" - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v2beta1", - "kind": "ClusterPolicy", - "metadata": { - "name": "cpol-clone-sync-modify-source" - }, - "spec": { - "rules": [ - { - "name": "cpol-clone-sync-modify-source-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "apiVersion": "v1", - "kind": "Secret", - "name": "regcred", - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "clone": { - "name": "regcred" - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-cloneList-kinds", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "default", - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ] - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "default", - "kinds": [ - "v1/Secret" - ] - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-cloneList-namespace", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ] - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "prod", - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ] - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-cloneList-selector", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "default", - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ], - "selector": { - "matchLabels": { - "allowedToBeCloned": "true" - } - } - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "default", - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ], - "selector": { - "matchLabels": { - "allowedToBeCloned": "false" - } - } - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-clone-List-selector-unset", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "default", - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ], - "selector": { - "matchLabels": { - "allowedToBeCloned": "true" - } - } - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "default", - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ] - } - } - } - ] - } - }`), - expectedErr: true, - }, - { - name: "update-cloneList-selector-nochange", - oldPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "default", - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ], - "selector": { - "matchLabels": { - "allowedToBeCloned": "true" - } - } - } - } - } - ] - } - }`), - newPolicy: []byte(` - { - "apiVersion": "kyverno.io/v1", - "kind": "ClusterPolicy", - "metadata": { - "name": "sync-with-multi-clone" - }, - "spec": { - "generateExisting": false, - "rules": [ - { - "name": "sync-secret", - "match": { - "any": [ - { - "resources": { - "kinds": [ - "Namespace" - ] - } - } - ] - }, - "generate": { - "namespace": "{{request.object.metadata.name}}", - "synchronize": true, - "cloneList": { - "namespace": "default", - "kinds": [ - "v1/Secret", - "v1/ConfigMap" - ], - "selector": { - "matchLabels": { - "allowedToBeCloned": "true" - } - } - } - } - } - ] - } - }`), - expectedErr: false, - }, - } - - for _, test := range tests { - var old, new *kyverno.Policy - err := json.Unmarshal(test.oldPolicy, &old) - assert.NilError(t, err) - err = json.Unmarshal(test.newPolicy, &new) - assert.NilError(t, err) - - err = immutableGenerateFields(new, old) - assert.Assert(t, (err != nil) == test.expectedErr, test.name, err) - } -} - -func Test_isMapStringString(t *testing.T) { - type args struct { - m map[string]interface{} - } - tests := []struct { - name string - args args - want bool - }{{ - name: "nil", - args: args{ - m: nil, - }, - want: true, - }, { - name: "empty", - args: args{ - m: map[string]interface{}{}, - }, - want: true, - }, { - name: "string values", - args: args{ - m: map[string]interface{}{ - "a": "b", - "c": "d", - }, - }, - want: true, - }, { - name: "int value", - args: args{ - m: map[string]interface{}{ - "a": "b", - "c": 123, - }, - }, - want: false, - }, { - name: "nil value", - args: args{ - m: map[string]interface{}{ - "a": "b", - "c": nil, - }, - }, - want: false, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := isMapStringString(tt.args.m); got != tt.want { - t.Errorf("checkLabelAnnotation() = %v, want %v", got, tt.want) - } - }) - } -} +// import ( +// "encoding/json" +// "errors" +// "fmt" +// "testing" + +// "github.com/go-logr/logr" +// kyverno "github.com/kyverno/kyverno/api/kyverno/v1" +// "github.com/kyverno/kyverno/pkg/openapi" +// "gotest.tools/assert" +// "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" +// apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +// "k8s.io/apimachinery/pkg/util/validation/field" +// ) + +// func Test_Validate_ResourceDescription_Empty(t *testing.T) { +// var err error +// rawResourcedescirption := []byte(`{}`) + +// var rd kyverno.ResourceDescription +// err = json.Unmarshal(rawResourcedescirption, &rd) +// assert.NilError(t, err) + +// _, err = validateMatchedResourceDescription(rd) +// assert.Assert(t, err != nil) +// } + +// func Test_Validate_ResourceDescription_MatchedValid(t *testing.T) { +// rawResourcedescirption := []byte(` +// { +// "kinds": [ +// "Deployment" +// ], +// "selector": { +// "matchLabels": { +// "app.type": "prod" +// } +// } +// }`) + +// var rd kyverno.ResourceDescription +// err := json.Unmarshal(rawResourcedescirption, &rd) +// assert.NilError(t, err) + +// _, err = validateMatchedResourceDescription(rd) +// assert.NilError(t, err) +// } + +// func Test_Validate_DenyConditions_KeyRequestOperation_Empty(t *testing.T) { +// denyConditions := []byte(`[]`) + +// var dcs apiextensions.JSON +// err := json.Unmarshal(denyConditions, &dcs) +// assert.NilError(t, err) + +// _, err = validateConditions(dcs, "conditions") +// assert.NilError(t, err) + +// _, err = validateConditions(dcs, "conditions") +// assert.NilError(t, err) +// } + +// func Test_Validate_Preconditions_KeyRequestOperation_Empty(t *testing.T) { +// preConditions := []byte(`[]`) + +// var pcs apiextensions.JSON +// err := json.Unmarshal(preConditions, &pcs) +// assert.NilError(t, err) + +// _, err = validateConditions(pcs, "preconditions") +// assert.NilError(t, err) + +// _, err = validateConditions(pcs, "preconditions") +// assert.NilError(t, err) +// } + +// func Test_Validate_DenyConditionsValuesString_KeyRequestOperation_ExpectedValue(t *testing.T) { +// denyConditions := []byte(` +// [ +// { +// "key":"{{request.operation}}", +// "operator":"Equals", +// "value":"DELETE" +// }, +// { +// "key":"{{request.operation}}", +// "operator":"NotEquals", +// "value":"CREATE" +// }, +// { +// "key":"{{request.operation}}", +// "operator":"NotEquals", +// "value":"CONNECT" +// }, +// { +// "key":"{{ request.operation }}", +// "operator":"NotEquals", +// "value":"UPDATE" +// }, +// { +// "key":"{{lbServiceCount}}", +// "operator":"Equals", +// "value":"2" +// } +// ] +// `) + +// var dcs apiextensions.JSON +// err := json.Unmarshal(denyConditions, &dcs) +// assert.NilError(t, err) + +// _, err = validateConditions(dcs, "conditions") +// assert.NilError(t, err) + +// _, err = validateConditions(dcs, "conditions") +// assert.NilError(t, err) +// } + +// func Test_Validate_DenyConditionsValuesString_KeyRequestOperation_RightfullyTemplatizedValue(t *testing.T) { +// denyConditions := []byte(` +// [ +// { +// "key":"{{request.operation}}", +// "operator":"Equals", +// "value":"{{ \"ops-cm\".data.\"deny-ops\"}}" +// }, +// { +// "key":"{{ request.operation }}", +// "operator":"NotEquals", +// "value":"UPDATE" +// } +// ] +// `) + +// var dcs apiextensions.JSON +// err := json.Unmarshal(denyConditions, &dcs) +// assert.NilError(t, err) + +// _, err = validateConditions(dcs, "conditions") +// assert.NilError(t, err) + +// _, err = validateConditions(dcs, "conditions") +// assert.NilError(t, err) +// } + +// func Test_Validate_DenyConditionsValuesString_KeyRequestOperation_WrongfullyTemplatizedValue(t *testing.T) { +// denyConditions := []byte(` +// [ +// { +// "key":"{{request.operation}}", +// "operator":"Equals", +// "value":"{{ \"ops-cm\".data.\"deny-ops\" }" +// }, +// { +// "key":"{{ request.operation }}", +// "operator":"NotEquals", +// "value":"UPDATE" +// } +// ] +// `) + +// var dcs []kyverno.Condition +// err := json.Unmarshal(denyConditions, &dcs) +// assert.NilError(t, err) + +// _, err = validateConditions(dcs, "conditions") +// assert.Assert(t, err != nil) +// } + +// func Test_Validate_PreconditionsValuesString_KeyRequestOperation_UnknownValue(t *testing.T) { +// preConditions := []byte(` +// [ +// { +// "key":"{{request.operation}}", +// "operator":"Equals", +// "value":"foobar" +// }, +// { +// "key": "{{request.operation}}", +// "operator": "NotEquals", +// "value": "CREATE" +// } +// ] +// `) + +// var pcs apiextensions.JSON +// err := json.Unmarshal(preConditions, &pcs) +// assert.NilError(t, err) + +// _, err = validateConditions(pcs, "preconditions") +// assert.Assert(t, err != nil) + +// _, err = validateConditions(pcs, "preconditions") +// assert.Assert(t, err != nil) +// } + +// func Test_Validate_DenyConditionsValuesList_KeyRequestOperation_ExpectedItem(t *testing.T) { +// denyConditions := []byte(` +// [ +// { +// "key":"{{request.operation}}", +// "operator":"Equals", +// "value": [ +// "CREATE", +// "DELETE", +// "CONNECT" +// ] +// }, +// { +// "key":"{{request.operation}}", +// "operator":"NotEquals", +// "value": [ +// "UPDATE" +// ] +// }, +// { +// "key": "{{lbServiceCount}}", +// "operator": "Equals", +// "value": "2" +// } +// ] +// `) + +// var dcs []kyverno.Condition +// err := json.Unmarshal(denyConditions, &dcs) +// assert.NilError(t, err) + +// _, err = validateConditions(dcs, "conditions") +// assert.NilError(t, err) +// } + +// func Test_Validate_PreconditionsValuesList_KeyRequestOperation_UnknownItem(t *testing.T) { +// preConditions := []byte(` +// [ +// { +// "key":"{{request.operation}}", +// "operator":"Equals", +// "value": [ +// "foobar", +// "CREATE" +// ] +// }, +// { +// "key":"{{request.operation}}", +// "operator":"NotEquals", +// "value": [ +// "foobar" +// ] +// } +// ] +// `) + +// var pcs apiextensions.JSON +// err := json.Unmarshal(preConditions, &pcs) +// assert.NilError(t, err) + +// _, err = validateConditions(pcs, "preconditions") +// assert.Assert(t, err != nil) + +// _, err = validateConditions(pcs, "preconditions") +// assert.Assert(t, err != nil) +// } + +// func Test_Validate_Policy(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "container-security-context" +// }, +// "spec": { +// "rules": [ +// { +// "name": "validate-runAsNonRoot", +// "match": { +// "resources": { +// "kinds": [ +// "Deployment" +// ], +// "selector": { +// "matchLabels": { +// "app.type": "prod" +// } +// } +// } +// }, +// "validate": { +// "message": "validate container security contexts", +// "anyPattern": [ +// { +// "spec": { +// "template": { +// "spec": { +// "^(containers)": [ +// { +// "securityContext": { +// "runAsNonRoot": "true" +// } +// } +// ] +// } +// } +// } +// } +// ] +// } +// }, +// { +// "name": "validate-allowPrivilegeEscalation", +// "match": { +// "resources": { +// "kinds": [ +// "Deployment" +// ], +// "selector": { +// "matchLabels": { +// "app.type": "prod" +// } +// } +// } +// }, +// "validate": { +// "message": "validate container security contexts", +// "pattern": { +// "spec": { +// "template": { +// "spec": { +// "^(containers)": [ +// { +// "securityContext": { +// "allowPrivilegeEscalation": "false" +// } +// } +// ] +// } +// } +// } +// } +// } +// } +// ] +// } +// }`) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.NilError(t, err) +// } + +// func Test_Validate_ErrorFormat(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "test-error-format" +// }, +// "spec": { +// "rules": [ +// { +// "name": "image-pull-policy", +// "match": { +// "resources": { +// "kinds": [ +// "Deployment" +// ], +// "selector": { +// "matchLabels": { +// "app": "nginxlatest" +// } +// } +// } +// }, +// "exclude": { +// "resources": { +// "selector": { +// "app": "nginxlatest" +// } +// } +// }, +// "mutate": { +// "patchStrategicMerge": { +// "spec": { +// "template": { +// "spec": { +// "containers": [ +// { +// "=(image)": "*latest", +// "imagePullPolicy": "IfNotPresent" +// } +// ] +// } +// } +// } +// } +// } +// }, +// { +// "name": "validate-user-privilege", +// "match": { +// "resources": { +// "kinds": [], +// "selector": { +// "matchLabels": { +// "app.type": "prod" +// } +// } +// } +// }, +// "validate": { +// "message": "validate container security contexts", +// "anyPattern": [ +// { +// "spec": { +// "template": { +// "spec": { +// "^(containers)": [ +// { +// "securityContext": { +// "runAsNonRoot": "true" +// } +// } +// ] +// } +// } +// } +// } +// ] +// } +// }, +// { +// "name": "validate-user-privilege", +// "match": { +// "resources": { +// "kinds": [ +// "Deployment" +// ], +// "selector": { +// "matchLabels": { +// "app.type": "prod" +// } +// } +// } +// }, +// "validate": { +// "message": "validate container security contexts", +// "pattern": { +// "spec": { +// "template": { +// "spec": { +// "containers": [ +// { +// "^(securityContext)": { +// "allowPrivilegeEscalation": "false" +// } +// } +// ] +// } +// } +// } +// } +// } +// }, +// { +// "name": "default-networkpolicy", +// "match": { +// "resources": { +// "kinds": [ +// "Namespace" +// ], +// "name": "devtest" +// } +// }, +// "generate": { +// "kind": "ConfigMap", +// "name": "copied-cm", +// "clone": { +// "^(namespace)": "default", +// "name": "game-config" +// } +// } +// } +// ] +// } +// } +// `) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Assert(t, err != nil) +// } + +// func Test_BackGroundUserInfo_match_roles(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "match.roles", +// "match": { +// "roles": [ +// "a", +// "b" +// ] +// } +// } +// ] +// } +// } +// `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = containsUserVariables(policy, nil) +// assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/roles") +// } + +// func Test_BackGroundUserInfo_match_clusterRoles(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "match.clusterRoles", +// "match": { +// "clusterRoles": [ +// "a", +// "b" +// ] +// } +// } +// ] +// } +// } +// `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = containsUserVariables(policy, nil) +// assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/clusterRoles") +// } + +// func Test_BackGroundUserInfo_match_subjects(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "match.subjects", +// "match": { +// "subjects": [ +// { +// "Name": "a" +// }, +// { +// "Name": "b" +// } +// ] +// } +// } +// ] +// } +// } `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = containsUserVariables(policy, nil) +// assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/subjects") +// } + +// func Test_BackGroundUserInfo_mutate_patchStrategicMerge1(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "mutate.overlay1", +// "mutate": { +// "patchStrategicMerge": { +// "var1": "{{request.userInfo}}" +// } +// } +// } +// ] +// } +// } +// `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = ValidateVariables(policy, true) +// assert.Assert(t, err != nil) +// } + +// func Test_Context_Variable_Substitution(t *testing.T) { +// var err error +// rawPolicy := []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "check-images" +// }, +// "spec": { +// "validationFailureAction": "Enforce", +// "webhookTimeoutSeconds": 30, +// "rules": [ +// { +// "name": "call-aws-signer-extension", +// "match": { +// "any": [ +// { +// "resources": { +// "namespaces": [ +// "test-notation" +// ], +// "kinds": [ +// "Pod" +// ] +// } +// } +// ] +// }, +// "context": [ +// { +// "name": "response", +// "apiCall": { +// "method": "POST", +// "data": [ +// { +// "key": "imagesInfo", +// "value": "{{ images }}" +// } +// ], +// "service": { +// "url": "https://svc.kyverno-notation-aws/checkimages", +// "caBundle": "-----BEGIN CERTIFICATE-----\nMIICizCCAjGgAwIBAgIRAIUEJcm7TtwJEtRtsI2yUcMwCgYIKoZIzj0EAwIwGzEZ\nMBcGA1UEAxMQbXktc2VsZnNpZ25lZC1jYTAeFw0yMzA1MTAwNTI5MzBaFw0yMzA4\nMDgwNTI5MzBaMDExEDAOBgNVBAoTB25pcm1hdGExHTAbBgNVBAMTFGt5dmVybm8t\nbm90YXRpb24tYXdzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrSr\nVXUbuQbF4rhh0/jqDE6agtXqS9jko6vHTEZUF2Y9f0LdSycEdCocIKZmPerWER7l\nVUmMFPQLSGOZrCIM22L9+EXDyL7q2PN3koDxKOyqVOod8j3hKdRL+KIiZuUeD4zD\ncos+AFxA1XAM/220JKfPSUpBL0DAP299Baqjs/Ae5wU5wT4qZVa1I3pcV2uicPvE\nRSZO3ZT+y1nYBWtTTzzXP3f9ou8IHweCl57Sk16mbFFZ+TrCSekewYchzn88z7lq\nL+56LtBUjcJozypLGEWM+kc4S5wBNYUaFPGiCHIrdQ5ScmfnY7mDvO8u47E+xw13\nbz7NUAlT73rBqBv6hQIDAQABo3UwczAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB\nBQUHAwIwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRXZIp2KalD6pjRfPua2kFn\nMBuJJTAjBgNVHREEHDAaghhzdmMua3l2ZXJuby1ub3RhdGlvbi1hd3MwCgYIKoZI\nzj0EAwIDSAAwRQIhAKob5SV/N56VqP8VPdHqCAULRj92qhWwW3hb7fzaGxnHAiBP\n3c8K2Vrxx2KRsjnWwn1vUMz7UyM2Tmib1C4YM3f+xg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIBdjCCAR2gAwIBAgIRAP1VDXD3R744lE7t/I5MK44wCgYIKoZIzj0EAwIwGzEZ\nMBcGA1UEAxMQbXktc2VsZnNpZ25lZC1jYTAeFw0yMzA1MTAwNTI5MjVaFw0yMzA4\nMDgwNTI5MjVaMBsxGTAXBgNVBAMTEG15LXNlbGZzaWduZWQtY2EwWTATBgcqhkjO\nPQIBBggqhkjOPQMBBwNCAAQrFCRBF8PjKPcT/lrXmyP474fNuhlhGFAlLaoTSUuP\nS3VK2O7hWrlJ/AhCccY8EPBi/DdFEaCB2+hTo00clmvfo0IwQDAOBgNVHQ8BAf8E\nBAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUV2SKdimpQ+qY0Xz7mtpB\nZzAbiSUwCgYIKoZIzj0EAwIDRwAwRAIgU3O7Qnk9PGCV4aXgZAXp0h4Iz2O7XUnP\nUfv4SgD7neECIHLb+BDvRFPJ77FpfIYxBO70AHB7Kp0nWKCqyv3FK4aT\n-----END CERTIFICATE-----" +// } +// } +// } +// ], +// "validate": { +// "message": "not allowed", +// "deny": { +// "conditions": { +// "all": [ +// { +// "key": "{{ response.verified }}", +// "operator": "EQUALS", +// "value": false +// } +// ] +// } +// } +// } +// } +// ] +// } +// }`) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = ValidateVariables(policy, true) +// assert.NilError(t, err) +// } + +// func Test_BackGroundUserInfo_mutate_patchStrategicMerge2(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "mutate.overlay2", +// "mutate": { +// "patchStrategicMerge": { +// "var1": "{{request.userInfo.userName}}" +// } +// } +// } +// ] +// } +// } +// `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = ValidateVariables(policy, true) +// assert.Assert(t, err != nil) +// } + +// func Test_BackGroundUserInfo_validate_pattern(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "validate-patch-strategic-merge", +// "validate": { +// "pattern": { +// "var1": "{{request.userInfo}}" +// } +// } +// } +// ] +// } +// } +// `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = ValidateVariables(policy, true) +// assert.Assert(t, err != nil, err) +// } + +// func Test_BackGroundUserInfo_validate_anyPattern(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "validate.anyPattern", +// "validate": { +// "anyPattern": [ +// { +// "var1": "temp" +// }, +// { +// "var1": "{{request.userInfo}}" +// } +// ] +// } +// } +// ] +// } +// } `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = ValidateVariables(policy, true) +// assert.Assert(t, err != nil) +// } + +// func Test_BackGroundUserInfo_validate_anyPattern_multiple_var(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "validate.anyPattern", +// "validate": { +// "anyPattern": [ +// { +// "var1": "temp" +// }, +// { +// "var1": "{{request.userInfo}}-{{temp}}" +// } +// ] +// } +// } +// ] +// } +// } `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = ValidateVariables(policy, true) +// assert.Assert(t, err != nil) +// } + +// func Test_BackGroundUserInfo_validate_anyPattern_serviceAccount(t *testing.T) { +// var err error +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "disallow-root-user" +// }, +// "spec": { +// "rules": [ +// { +// "name": "validate.anyPattern", +// "validate": { +// "anyPattern": [ +// { +// "var1": "temp" +// }, +// { +// "var1": "{{serviceAccountName}}" +// } +// ] +// } +// } +// ] +// } +// } `) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// err = ValidateVariables(policy, true) +// assert.Assert(t, err != nil) +// } + +// func Test_ruleOnlyDealsWithResourceMetaData(t *testing.T) { +// testcases := []struct { +// description string +// rule []byte +// expectedOutput bool +// }{ +// { +// description: "Test mutate patchStrategicMerge - pass", +// rule: []byte(`{"name":"testPatches1","mutate":{"patchStrategicMerge":{"metadata":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}`), +// expectedOutput: true, +// }, +// { +// description: "Test mutate patchStrategicMerge - fail", +// rule: []byte(`{"name":"testPatches2","mutate":{"patchStrategicMerge":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}`), +// expectedOutput: false, +// }, +// { +// description: "Test mutate patch - pass", +// rule: []byte(`{"name":"testPatches3","mutate":{"patchesJson6902": "[{\"path\":\"/metadata/labels/isMutated\",\"op\":\"add\",\"value\":\"true\"},{\"path\":\"/metadata/labels/app\",\"op\":\"replace\",\"value\":\"nginx_is_mutated\"}]"}}`), +// expectedOutput: true, +// }, +// { +// description: "Test mutate patch - fail", +// rule: []byte(`{"name":"testPatches4","mutate":{"patchesJson6902": "[{\"path\":\"/spec/labels/isMutated\",\"op\":\"add\",\"value\":\"true\"},{\"path\":\"/metadata/labels/app\",\"op\":\"replace\",\"value\":\"nginx_is_mutated\"}]" }}`), +// expectedOutput: false, +// }, +// { +// description: "Test validate - pass", +// rule: []byte(`{"name":"testValidate1","validate":{"message":"CPU and memory resource requests and limits are required","pattern":{"metadata":{"containers":[{"(name)":"*","ports":[{"containerPort":80}]}]}}}}`), +// expectedOutput: true, +// }, +// { +// description: "Test validate - fail", +// rule: []byte(`{"name":"testValidate2","validate":{"message":"CPU and memory resource requests and limits are required","pattern":{"spec":{"containers":[{"(name)":"*","ports":[{"containerPort":80}]}]}}}}`), +// expectedOutput: false, +// }, +// { +// description: "Test validate any pattern - pass", +// rule: []byte(`{"name":"testValidateAnyPattern1","validate":{"message":"Volumes white list","anyPattern":[{"metadata":{"volumes":[{"hostPath":"*"}]}},{"metadata":{"volumes":[{"emptyDir":"*"}]}},{"metadata":{"volumes":[{"configMap":"*"}]}}]}}`), +// expectedOutput: true, +// }, +// { +// description: "Test validate any pattern - fail", +// rule: []byte(`{"name":"testValidateAnyPattern2","validate":{"message":"Volumes white list","anyPattern":[{"spec":{"volumes":[{"hostPath":"*"}]}},{"metadata":{"volumes":[{"emptyDir":"*"}]}},{"metadata":{"volumes":[{"configMap":"*"}]}}]}}`), +// expectedOutput: false, +// }, +// } + +// for i, testcase := range testcases { +// var rule kyverno.Rule +// _ = json.Unmarshal(testcase.rule, &rule) +// output := ruleOnlyDealsWithResourceMetaData(rule) +// if output != testcase.expectedOutput { +// t.Errorf("Testcase [%d] (%s) failed", i+1, testcase.description) +// } +// } +// } + +// func Test_Validate_Kind(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "policy-to-monitor-root-user-access" +// }, +// "spec": { +// "validationFailureAction": "audit", +// "rules": [ +// { +// "name": "monitor-annotation-for-root-user-access", +// "match": { +// "resources": { +// "selector": { +// "matchLabels": { +// "AllowRootUserAccess": "true" +// } +// } +// } +// }, +// "validate": { +// "message": "Label provisioner.wg.net/cloudprovider is required", +// "pattern": { +// "metadata": { +// "labels": { +// "provisioner.wg.net/cloudprovider": "*" +// } +// } +// } +// } +// } +// ] +// } +// } +// `) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Assert(t, err != nil) +// } + +// func Test_Validate_Any_Kind(t *testing.T) { +// rawPolicy := []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "policy-to-monitor-root-user-access" +// }, +// "spec": { +// "validationFailureAction": "audit", +// "rules": [ +// { +// "name": "monitor-annotation-for-root-user-access", +// "match": { +// "any": [ +// { +// "resources": { +// "selector": { +// "matchLabels": { +// "AllowRootUserAccess": "true" +// } +// } +// } +// } +// ] +// }, +// "validate": { +// "message": "Label provisioner.wg.net/cloudprovider is required", +// "pattern": { +// "metadata": { +// "labels": { +// "provisioner.wg.net/cloudprovider": "*" +// } +// } +// } +// } +// } +// ] +// } +// }`) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Assert(t, err != nil) +// } + +// func Test_Validate_ApiCall(t *testing.T) { +// testCases := []struct { +// resource kyverno.ContextEntry +// expectedResult interface{} +// }{ +// { +// resource: kyverno.ContextEntry{ +// APICall: &kyverno.APICall{ +// URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies", +// JMESPath: "", +// }, +// }, +// expectedResult: nil, +// }, +// { +// resource: kyverno.ContextEntry{ +// APICall: &kyverno.APICall{ +// URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies", +// JMESPath: "items[", +// }, +// }, +// expectedResult: "failed to parse JMESPath items[: SyntaxError: Expected tStar, received: tEOF", +// }, +// { +// resource: kyverno.ContextEntry{ +// APICall: &kyverno.APICall{ +// URLPath: "/apis/networking.k8s.io/v1/namespaces/{{request.namespace}}/networkpolicies", +// JMESPath: "items[{{request.namespace}}", +// }, +// }, +// expectedResult: nil, +// }, +// } + +// for _, testCase := range testCases { +// err := validateAPICall(testCase.resource) + +// if err == nil { +// assert.Equal(t, err, testCase.expectedResult) +// } else { +// assert.Equal(t, err.Error(), testCase.expectedResult) +// } +// } +// } + +// func Test_Wildcards_Kind(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "require-labels" +// }, +// "spec": { +// "validationFailureAction": "enforce", +// "rules": [ +// { +// "name": "check-for-labels", +// "match": { +// "resources": { +// "kinds": [ +// "*" +// ] +// } +// }, +// "validate": { +// "message": "label 'app.kubernetes.io/name' is required", +// "pattern": { +// "metadata": { +// "labels": { +// "app.kubernetes.io/name": "?*" +// } +// } +// } +// } +// } +// ] +// } +// } +// `) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Assert(t, err != nil) +// } + +// func Test_Namespced_Policy(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "Policy", +// "metadata": { +// "name": "evil-policy-match-foreign-pods", +// "namespace": "customer-foo" +// }, +// "spec": { +// "validationFailureAction": "enforce", +// "background": false, +// "rules": [ +// { +// "name": "evil-validation", +// "match": { +// "resources": { +// "kinds": [ +// "Pod" +// ], +// "namespaces": [ +// "customer-bar" +// ] +// } +// }, +// "validate": { +// "message": "Mua ah ah ... you've been pwned by customer-foo", +// "pattern": { +// "metadata": { +// "annotations": { +// "pwned-by-customer-foo": "true" +// } +// } +// } +// } +// } +// ] +// } +// } +// `) + +// var policy *kyverno.Policy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Assert(t, err != nil) +// } + +// func Test_patchesJson6902_Policy(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "set-max-surge-yaml-to-json" +// }, +// "spec": { +// "background": false, +// "schemaValidation": false, +// "rules": [ +// { +// "name": "set-max-surge", +// "context": [ +// { +// "name": "source", +// "configMap": { +// "name": "source-yaml-to-json", +// "namespace": "default" +// } +// } +// ], +// "match": { +// "resources": { +// "kinds": [ +// "Deployment" +// ] +// } +// }, +// "mutate": { +// "patchesJson6902": "- op: replace\n path: /spec/strategy\n value: {{ source.data.strategy }}" +// } +// } +// ] +// } +// } +// `) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.NilError(t, err) +// } + +// func Test_deny_exec(t *testing.T) { +// var err error +// rawPolicy := []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "deny-exec-to-pod" +// }, +// "spec": { +// "validationFailureAction": "enforce", +// "background": false, +// "schemaValidation": false, +// "rules": [ +// { +// "name": "deny-pod-exec", +// "match": { +// "resources": { +// "kinds": [ +// "PodExecOptions" +// ] +// } +// }, +// "preconditions": { +// "all": [ +// { +// "key": "{{ request.operation }}", +// "operator": "Equals", +// "value": "CONNECT" +// } +// ] +// }, +// "validate": { +// "message": "Containers can't be exec'd into in production.", +// "deny": {} +// } +// } +// ] +// } +// }`) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.NilError(t, err) +// } + +// func Test_SignatureAlgorithm(t *testing.T) { +// testcases := []struct { +// description string +// policy []byte +// expectedOutput bool +// }{ +// { +// description: "Test empty signature algorithm - pass", +// policy: []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "check-empty-signature-algorithm" +// }, +// "spec": { +// "rules": [ +// { +// "match": { +// "resources": { +// "kinds": [ +// "Pod" +// ] +// } +// }, +// "verifyImages": [ +// { +// "imageReferences": [ +// "ghcr.io/kyverno/test-verify-image:*" +// ], +// "attestors": [ +// { +// "count": 1, +// "entries": [ +// { +// "keys": { +// "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----" +// } +// } +// ] +// } +// ] +// } +// ] +// } +// ] +// } +// }`), +// expectedOutput: true, +// }, +// { +// description: "Test invalid signature algorithm - fail", +// policy: []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "check-invalid-signature-algorithm" +// }, +// "spec": { +// "rules": [ +// { +// "match": { +// "resources": { +// "kinds": [ +// "Pod" +// ] +// } +// }, +// "verifyImages": [ +// { +// "imageReferences": [ +// "ghcr.io/kyverno/test-verify-image:*" +// ], +// "attestors": [ +// { +// "count": 1, +// "entries": [ +// { +// "keys": { +// "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----", +// "signatureAlgorithm": "sha123" +// } +// } +// ] +// } +// ] +// } +// ] +// } +// ] +// } +// }`), +// expectedOutput: false, +// }, +// { +// description: "Test invalid signature algorithm - fail", +// policy: []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "check-valid-signature-algorithm" +// }, +// "spec": { +// "rules": [ +// { +// "match": { +// "resources": { +// "kinds": [ +// "Pod" +// ] +// } +// }, +// "verifyImages": [ +// { +// "imageReferences": [ +// "ghcr.io/kyverno/test-verify-image:*" +// ], +// "attestors": [ +// { +// "count": 1, +// "entries": [ +// { +// "keys": { +// "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----", +// "signatureAlgorithm": "sha256" +// } +// } +// ] +// } +// ] +// } +// ] +// } +// ] +// } +// }`), +// expectedOutput: true, +// }, +// } +// for _, testcase := range testcases { +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(testcase.policy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// if testcase.expectedOutput { +// assert.NilError(t, err) +// } else { +// assert.ErrorContains(t, err, "Invalid signature algorithm provided") +// } +// } +// } + +// func Test_existing_resource_policy(t *testing.T) { +// var err error +// rawPolicy := []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "np-test-1" +// }, +// "spec": { +// "validationFailureAction": "audit", +// "rules": [ +// { +// "name": "no-LoadBalancer", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "networking.k8s.io/v1/NetworkPolicy" +// ] +// } +// } +// ] +// }, +// "validate": { +// "message": "np-test", +// "pattern": { +// "metadata": { +// "name": "?*" +// } +// } +// } +// } +// ] +// } +// }`) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.NilError(t, err) +// } + +// func Test_PodControllerAutoGenExclusion_All_Controllers_Policy(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "add-all-pod-controller-annotations", +// "annotations": { +// "pod-policies.kyverno.io/autogen-controllers": "DaemonSet,Job,CronJob,Deployment,StatefulSet" +// } +// }, +// "spec": { +// "validationFailureAction": "Enforce", +// "background": false, +// "rules": [ +// { +// "name": "validate-livenessProbe-readinessProbe", +// "match": { +// "resources": { +// "kinds": [ +// "Pod" +// ] +// } +// }, +// "validate": { +// "message": "Liveness and readiness probes are required.", +// "pattern": { +// "spec": { +// "containers": [ +// { +// "livenessProbe": { +// "periodSeconds": ">0" +// }, +// "readinessProbe": { +// "periodSeconds": ">0" +// } +// } +// ] +// } +// } +// } +// } +// ] +// } +// } +// `) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// res, err := Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.NilError(t, err) +// assert.Assert(t, res == nil) +// } + +// func Test_PodControllerAutoGenExclusion_Not_All_Controllers_Policy(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "add-not-all-pod-controller-annotations", +// "annotations": { +// "pod-policies.kyverno.io/autogen-controllers": "DaemonSet,Job,CronJob,Deployment" +// } +// }, +// "spec": { +// "validationFailureAction": "Enforce", +// "background": false, +// "rules": [ +// { +// "name": "validate-livenessProbe-readinessProbe", +// "match": { +// "resources": { +// "kinds": [ +// "Pod" +// ] +// } +// }, +// "validate": { +// "message": "Liveness and readiness probes are required.", +// "pattern": { +// "spec": { +// "containers": [ +// { +// "livenessProbe": { +// "periodSeconds": ">0" +// }, +// "readinessProbe": { +// "periodSeconds": ">0" +// } +// } +// ] +// } +// } +// } +// } +// ] +// } +// } +// `) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// warnings, err := Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Assert(t, warnings != nil) +// assert.NilError(t, err) +// } + +// func Test_PodControllerAutoGenExclusion_None_Policy(t *testing.T) { +// rawPolicy := []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "add-none-pod-controller-annotations", +// "annotations": { +// "pod-policies.kyverno.io/autogen-controllers": "none" +// } +// }, +// "spec": { +// "validationFailureAction": "Enforce", +// "background": false, +// "rules": [ +// { +// "name": "validate-livenessProbe-readinessProbe", +// "match": { +// "resources": { +// "kinds": [ +// "Pod" +// ] +// } +// }, +// "validate": { +// "message": "Liveness and readiness probes are required.", +// "pattern": { +// "spec": { +// "containers": [ +// { +// "livenessProbe": { +// "periodSeconds": ">0" +// }, +// "readinessProbe": { +// "periodSeconds": ">0" +// } +// } +// ] +// } +// } +// } +// } +// ] +// } +// } +// `) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// warnings, err := Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Assert(t, warnings == nil) +// assert.NilError(t, err) +// } + +// func Test_ValidateJSON6902(t *testing.T) { +// var patch string = `- path: "/metadata/labels/img" +// op: addition +// value: "nginx"` +// err := validateJSONPatch(patch, 0) +// assert.Error(t, err, "unexpected kind: spec.rules[0]: addition") + +// patch = `- path: "/metadata/labels/img" +// op: add +// value: "nginx"` +// err = validateJSONPatch(patch, 0) +// assert.NilError(t, err) + +// patch = `- path: "/metadata/labels/img" +// op: add +// value: "nginx"` +// err = validateJSONPatch(patch, 0) +// assert.NilError(t, err) +// } + +// func Test_ValidateNamespace(t *testing.T) { +// testcases := []struct { +// description string +// spec *kyverno.Spec +// expectedError error +// }{ +// { +// description: "tc1", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("conflicting namespaces found in path: spec.validationFailureActionOverrides[1].namespaces: default"), +// }, +// { +// description: "tc2", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Mutation: kyverno.Mutation{ +// RawPatchStrategicMerge: &apiextv1.JSON{Raw: []byte(`"metadata": {"labels": {"app-name": "{{request.object.metadata.name}}"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("conflicting namespaces found in path: spec.validationFailureActionOverrides[1].namespaces: default"), +// }, +// { +// description: "tc3", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default*", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern 'default*' matches with namespace 'default'"), +// }, +// { +// description: "tc4", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "*", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '*' matches with namespace 'default'"), +// }, +// { +// description: "tc5", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "?*", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '?*' matches with namespace 'default'"), +// }, +// { +// description: "tc6", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default?", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default1", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern 'default?' matches with namespace 'default1'"), +// }, +// { +// description: "tc7", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default*", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "?*", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '?*' matches with namespace 'test'"), +// }, +// { +// description: "tc8", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "*", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "?*", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '?*' conflicts with the pattern '*'"), +// }, +// { +// description: "tc9", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default*", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default", +// "test*", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern 'test*' matches with namespace 'test'"), +// }, +// { +// description: "tc10", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "*efault", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// expectedError: errors.New("path: spec.validationFailureActionOverrides[1].namespaces: wildcard pattern '*efault' matches with namespace 'default'"), +// }, +// { +// description: "tc11", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default-*", +// "test", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// }, +// { +// description: "tc12", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default*?", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default", +// "test*", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// }, +// { +// description: "tc13", +// spec: &kyverno.Spec{ +// ValidationFailureAction: "Enforce", +// ValidationFailureActionOverrides: []kyverno.ValidationFailureActionOverride{ +// { +// Action: "Enforce", +// Namespaces: []string{ +// "default?", +// }, +// }, +// { +// Action: "Audit", +// Namespaces: []string{ +// "default", +// }, +// }, +// }, +// Rules: []kyverno.Rule{ +// { +// Name: "require-labels", +// MatchResources: kyverno.MatchResources{ResourceDescription: kyverno.ResourceDescription{Kinds: []string{"Pod"}}}, +// Validation: kyverno.Validation{ +// Message: "label 'app.kubernetes.io/name' is required", +// RawPattern: &apiextv1.JSON{Raw: []byte(`"metadata": {"lables": {"app.kubernetes.io/name": "?*"}}`)}, +// }, +// }, +// }, +// }, +// }, +// } + +// for _, tc := range testcases { +// t.Run(tc.description, func(t *testing.T) { +// err := validateNamespaces(tc.spec, field.NewPath("spec").Child("validationFailureActionOverrides")) +// if tc.expectedError != nil { +// assert.Error(t, err, tc.expectedError.Error()) +// } else { +// assert.NilError(t, err) +// } +// }) +// } +// } + +// func testResourceList() []*metav1.APIResourceList { +// return []*metav1.APIResourceList{ +// { +// GroupVersion: "v1", +// APIResources: []metav1.APIResource{ +// {Name: "pods", Namespaced: true, Kind: "Pod"}, +// {Name: "services", Namespaced: true, Kind: "Service"}, +// {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"}, +// {Name: "componentstatuses", Namespaced: false, Kind: "ComponentStatus"}, +// {Name: "nodes", Namespaced: false, Kind: "Node"}, +// {Name: "secrets", Namespaced: true, Kind: "Secret"}, +// {Name: "configmaps", Namespaced: true, Kind: "ConfigMap"}, +// {Name: "namespacedtype", Namespaced: true, Kind: "NamespacedType"}, +// {Name: "namespaces", Namespaced: false, Kind: "Namespace"}, +// {Name: "resourcequotas", Namespaced: true, Kind: "ResourceQuota"}, +// }, +// }, +// { +// GroupVersion: "apps/v1", +// APIResources: []metav1.APIResource{ +// {Name: "deployments", Namespaced: true, Kind: "Deployment"}, +// {Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"}, +// }, +// }, +// { +// GroupVersion: "storage.k8s.io/v1", +// APIResources: []metav1.APIResource{ +// {Name: "storageclasses", Namespaced: false, Kind: "StorageClass"}, +// }, +// }, +// } +// } + +// func Test_Any_wildcard_policy(t *testing.T) { +// var err error +// rawPolicy := []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "verify-image" +// }, +// "spec": { +// "validationFailureAction": "enforce", +// "background": false, +// "rules": [ +// { +// "name": "verify-image", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "*" +// ] +// } +// } +// ] +// }, +// "verifyImages": [ +// { +// "imageReferences": [ +// "ghcr.io/kyverno/test-verify-image:*" +// ], +// "mutateDigest": true, +// "attestors": [ +// { +// "entries": [ +// { +// "keys": { +// "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY----- \n" +// } +// } +// ] +// } +// ] +// } +// ] +// } +// ] +// } +// }`) +// var policy *kyverno.ClusterPolicy +// err = json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, err = Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Assert(t, err != nil) +// } + +// func Test_Validate_RuleImageExtractorsJMESPath(t *testing.T) { +// rawPolicy := []byte(`{ +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "jmes-path-and-mutate-digest" +// }, +// "spec": { +// "rules": [ +// { +// "match": { +// "resources": { +// "kinds": [ +// "CRD" +// ] +// } +// }, +// "imageExtractors": { +// "CRD": [ +// { +// "path": "/path/to/image/prefixed/with/scheme", +// "jmesPath": "trim_prefix(@, 'docker://')" +// } +// ] +// }, +// "verifyImages": [ +// { +// "mutateDigest": true, +// "attestors": [ +// { +// "count": 1, +// "entries": [ +// { +// "keys": { +// "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----" +// } +// } +// ] +// } +// ] +// } +// ] +// } +// ] +// } +// }`) + +// var policy *kyverno.ClusterPolicy +// err := json.Unmarshal(rawPolicy, &policy) +// assert.NilError(t, err) + +// expectedErr := fmt.Errorf("path: spec.rules[0]: jmespath may not be used in an image extractor when mutating digests with verify images") + +// openApiManager, _ := openapi.NewManager(logr.Discard()) +// _, actualErr := Validate(policy, nil, nil, true, openApiManager, "admin") +// assert.Equal(t, expectedErr.Error(), actualErr.Error()) +// } + +// func Test_ImmutableGenerateFields(t *testing.T) { +// tests := []struct { +// name string +// oldPolicy []byte +// newPolicy []byte +// expectedErr bool +// }{ +// { +// name: "update-apiVersion", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "apps/v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-kind", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Configmap", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-namespace", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.labels.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-name", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "new-name", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-sync-flag", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": false, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// expectedErr: false, +// }, +// { +// name: "update-clone-name", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "modifed-name" +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-clone-namespace", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "default", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "modifed-namespace", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-clone-namespace-unset-new", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "namespace": "prod", +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v2beta1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "cpol-clone-sync-modify-source" +// }, +// "spec": { +// "rules": [ +// { +// "name": "cpol-clone-sync-modify-source-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "apiVersion": "v1", +// "kind": "Secret", +// "name": "regcred", +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "clone": { +// "name": "regcred" +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-cloneList-kinds", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "default", +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ] +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "default", +// "kinds": [ +// "v1/Secret" +// ] +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-cloneList-namespace", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ] +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "prod", +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ] +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-cloneList-selector", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "default", +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ], +// "selector": { +// "matchLabels": { +// "allowedToBeCloned": "true" +// } +// } +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "default", +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ], +// "selector": { +// "matchLabels": { +// "allowedToBeCloned": "false" +// } +// } +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-clone-List-selector-unset", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "default", +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ], +// "selector": { +// "matchLabels": { +// "allowedToBeCloned": "true" +// } +// } +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "default", +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ] +// } +// } +// } +// ] +// } +// }`), +// expectedErr: true, +// }, +// { +// name: "update-cloneList-selector-nochange", +// oldPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "default", +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ], +// "selector": { +// "matchLabels": { +// "allowedToBeCloned": "true" +// } +// } +// } +// } +// } +// ] +// } +// }`), +// newPolicy: []byte(` +// { +// "apiVersion": "kyverno.io/v1", +// "kind": "ClusterPolicy", +// "metadata": { +// "name": "sync-with-multi-clone" +// }, +// "spec": { +// "generateExisting": false, +// "rules": [ +// { +// "name": "sync-secret", +// "match": { +// "any": [ +// { +// "resources": { +// "kinds": [ +// "Namespace" +// ] +// } +// } +// ] +// }, +// "generate": { +// "namespace": "{{request.object.metadata.name}}", +// "synchronize": true, +// "cloneList": { +// "namespace": "default", +// "kinds": [ +// "v1/Secret", +// "v1/ConfigMap" +// ], +// "selector": { +// "matchLabels": { +// "allowedToBeCloned": "true" +// } +// } +// } +// } +// } +// ] +// } +// }`), +// expectedErr: false, +// }, +// } + +// for _, test := range tests { +// var old, new *kyverno.Policy +// err := json.Unmarshal(test.oldPolicy, &old) +// assert.NilError(t, err) +// err = json.Unmarshal(test.newPolicy, &new) +// assert.NilError(t, err) + +// err = immutableGenerateFields(new, old) +// assert.Assert(t, (err != nil) == test.expectedErr, test.name, err) +// } +// } + +// func Test_isMapStringString(t *testing.T) { +// type args struct { +// m map[string]interface{} +// } +// tests := []struct { +// name string +// args args +// want bool +// }{{ +// name: "nil", +// args: args{ +// m: nil, +// }, +// want: true, +// }, { +// name: "empty", +// args: args{ +// m: map[string]interface{}{}, +// }, +// want: true, +// }, { +// name: "string values", +// args: args{ +// m: map[string]interface{}{ +// "a": "b", +// "c": "d", +// }, +// }, +// want: true, +// }, { +// name: "int value", +// args: args{ +// m: map[string]interface{}{ +// "a": "b", +// "c": 123, +// }, +// }, +// want: false, +// }, { +// name: "nil value", +// args: args{ +// m: map[string]interface{}{ +// "a": "b", +// "c": nil, +// }, +// }, +// want: false, +// }} +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// if got := isMapStringString(tt.args.m); got != tt.want { +// t.Errorf("checkLabelAnnotation() = %v, want %v", got, tt.want) +// } +// }) +// } +// } diff --git a/pkg/webhooks/policy/handlers.go b/pkg/webhooks/policy/handlers.go index bf4cba6332..e05199eff5 100644 --- a/pkg/webhooks/policy/handlers.go +++ b/pkg/webhooks/policy/handlers.go @@ -6,7 +6,6 @@ import ( "github.com/go-logr/logr" "github.com/kyverno/kyverno/pkg/clients/dclient" - "github.com/kyverno/kyverno/pkg/openapi" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" policyvalidate "github.com/kyverno/kyverno/pkg/validation/policy" "github.com/kyverno/kyverno/pkg/webhooks" @@ -15,14 +14,12 @@ import ( type policyHandlers struct { client dclient.Interface - openApiManager openapi.Manager backgroungServiceAccountName string } -func NewHandlers(client dclient.Interface, openApiManager openapi.Manager, serviceaccount string) webhooks.PolicyHandlers { +func NewHandlers(client dclient.Interface, serviceaccount string) webhooks.PolicyHandlers { return &policyHandlers{ client: client, - openApiManager: openApiManager, backgroungServiceAccountName: serviceaccount, } } @@ -33,7 +30,7 @@ func (h *policyHandlers) Validate(ctx context.Context, logger logr.Logger, reque logger.Error(err, "failed to unmarshal policies from admission request") return admissionutils.Response(request.UID, err) } - warnings, err := policyvalidate.Validate(policy, oldPolicy, h.client, false, h.openApiManager, h.backgroungServiceAccountName) + warnings, err := policyvalidate.Validate(policy, oldPolicy, h.client, false, h.backgroungServiceAccountName) if err != nil { logger.Error(err, "policy validation errors") } diff --git a/pkg/webhooks/resource/fake.go b/pkg/webhooks/resource/fake.go index be4ee2900a..c0c409ebc9 100644 --- a/pkg/webhooks/resource/fake.go +++ b/pkg/webhooks/resource/fake.go @@ -15,7 +15,6 @@ import ( "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/imageverifycache" "github.com/kyverno/kyverno/pkg/metrics" - "github.com/kyverno/kyverno/pkg/openapi" "github.com/kyverno/kyverno/pkg/policycache" "github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/webhooks" @@ -45,16 +44,15 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook rclient := registryclient.NewOrDie() return &resourceHandlers{ - client: dclient, - configuration: configuration, - metricsConfig: metricsConfig, - pCache: policyCache, - nsLister: informers.Core().V1().Namespaces().Lister(), - urLister: urLister, - urGenerator: updaterequest.NewFake(), - eventGen: event.NewFake(), - openApiManager: openapi.NewFake(), - pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp), + client: dclient, + configuration: configuration, + metricsConfig: metricsConfig, + pCache: policyCache, + nsLister: informers.Core().V1().Namespaces().Lister(), + urLister: urLister, + urGenerator: updaterequest.NewFake(), + eventGen: event.NewFake(), + pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp), engine: engine.NewEngine( configuration, config.NewDefaultMetricsConfiguration(), diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go index 6a0a9ed41c..499cb11dd2 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -17,7 +17,6 @@ import ( "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" - "github.com/kyverno/kyverno/pkg/openapi" "github.com/kyverno/kyverno/pkg/policycache" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" engineutils "github.com/kyverno/kyverno/pkg/utils/engine" @@ -52,10 +51,9 @@ type resourceHandlers struct { cpolLister kyvernov1listers.ClusterPolicyLister polLister kyvernov1listers.PolicyLister - urGenerator webhookgenerate.Generator - eventGen event.Interface - openApiManager openapi.ValidateInterface - pcBuilder webhookutils.PolicyContextBuilder + urGenerator webhookgenerate.Generator + eventGen event.Interface + pcBuilder webhookutils.PolicyContextBuilder admissionReports bool backgroungServiceAccountName string @@ -74,7 +72,6 @@ func NewHandlers( polInformer kyvernov1informers.PolicyInformer, urGenerator webhookgenerate.Generator, eventGen event.Interface, - openApiManager openapi.ValidateInterface, admissionReports bool, backgroungServiceAccountName string, jp jmespath.Interface, @@ -92,7 +89,6 @@ func NewHandlers( polLister: polInformer.Lister(), urGenerator: urGenerator, eventGen: eventGen, - openApiManager: openApiManager, pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp), admissionReports: admissionReports, backgroungServiceAccountName: backgroungServiceAccountName, @@ -158,7 +154,7 @@ func (h *resourceHandlers) Mutate(ctx context.Context, logger logr.Logger, reque logger.Error(err, "failed to build policy context") return admissionutils.Response(request.UID, err) } - mh := mutation.NewMutationHandler(logger, h.engine, h.eventGen, h.openApiManager, h.nsLister, h.metricsConfig) + mh := mutation.NewMutationHandler(logger, h.engine, h.eventGen, h.nsLister, h.metricsConfig) mutatePatches, mutateWarnings, err := mh.HandleMutation(ctx, request.AdmissionRequest, mutatePolicies, policyContext, startTime) if err != nil { logger.Error(err, "mutation failed") diff --git a/pkg/webhooks/resource/mutation/mutation.go b/pkg/webhooks/resource/mutation/mutation.go index d03f1fe972..c9698fe21a 100644 --- a/pkg/webhooks/resource/mutation/mutation.go +++ b/pkg/webhooks/resource/mutation/mutation.go @@ -12,7 +12,6 @@ import ( "github.com/kyverno/kyverno/pkg/engine/mutate/patch" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" - "github.com/kyverno/kyverno/pkg/openapi" "github.com/kyverno/kyverno/pkg/tracing" engineutils "github.com/kyverno/kyverno/pkg/utils/engine" jsonutils "github.com/kyverno/kyverno/pkg/utils/json" @@ -34,27 +33,24 @@ func NewMutationHandler( log logr.Logger, engine engineapi.Engine, eventGen event.Interface, - openApiManager openapi.ValidateInterface, nsLister corev1listers.NamespaceLister, metrics metrics.MetricsConfigManager, ) MutationHandler { return &mutationHandler{ - log: log, - engine: engine, - eventGen: eventGen, - openApiManager: openApiManager, - nsLister: nsLister, - metrics: metrics, + log: log, + engine: engine, + eventGen: eventGen, + nsLister: nsLister, + metrics: metrics, } } type mutationHandler struct { - log logr.Logger - engine engineapi.Engine - eventGen event.Interface - openApiManager openapi.ValidateInterface - nsLister corev1listers.NamespaceLister - metrics metrics.MetricsConfigManager + log logr.Logger + engine engineapi.Engine + eventGen event.Interface + nsLister corev1listers.NamespaceLister + metrics metrics.MetricsConfigManager } func (h *mutationHandler) HandleMutation( @@ -147,13 +143,6 @@ func (h *mutationHandler) applyMutation(ctx context.Context, request admissionv1 return nil, nil, fmt.Errorf("failed to apply policy %s rules %v", policyContext.Policy().GetName(), engineResponse.GetFailedRulesWithErrors()) } - if policyContext.Policy().ValidateSchema() && engineResponse.PatchedResource.GetKind() != "*" { - err := h.openApiManager.ValidateResource(*engineResponse.PatchedResource.DeepCopy(), engineResponse.PatchedResource.GetAPIVersion(), engineResponse.PatchedResource.GetKind()) - if err != nil { - return nil, nil, fmt.Errorf("failed to validate resource mutated by policy %s: %w", policyContext.Policy().GetName(), err) - } - } - return &engineResponse, policyPatches, nil } diff --git a/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/01-crd.yaml b/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/01-crd.yaml index c2a379e3d0..6db5372cb0 100644 --- a/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/01-crd.yaml +++ b/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/01-crd.yaml @@ -1,234 +1,6 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - creationTimestamp: null - name: roles.iam.aws.crossplane.io -spec: - group: iam.aws.crossplane.io - names: - categories: - - crossplane - - managed - - aws - kind: Role - listKind: RoleList - plural: roles - shortNames: - - iamrole - singular: role - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=='Ready')].status - name: READY - type: string - - jsonPath: .status.conditions[?(@.type=='Synced')].status - name: SYNCED - type: string - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: An Role is a managed resource that represents an AWS IAM Role. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: An RoleSpec defines the desired state of an Role. - properties: - deletionPolicy: - default: Delete - description: DeletionPolicy specifies what will happen to the underlying - external when this managed resource is deleted - either "Delete" - or "Orphan" the external resource. - enum: - - Orphan - - Delete - type: string - forProvider: - description: RoleParameters define the desired state of an AWS IAM - Role. - properties: - assumeRolePolicyDocument: - description: AssumeRolePolicyDocument is the the trust relationship - policy document that grants an entity permission to assume the - role. - type: string - description: - description: Description is a description of the role. - type: string - maxSessionDuration: - description: 'MaxSessionDuration is the duration (in seconds) - that you want to set for the specified role. The default maximum - of one hour is applied. This setting can have a value from 1 - hour to 12 hours. Default: 3600' - format: int32 - type: integer - path: - description: 'Path is the path to the role. Default: /' - type: string - permissionsBoundary: - description: PermissionsBoundary is the ARN of the policy that - is used to set the permissions boundary for the role. - type: string - tags: - description: Tags. For more information about tagging, see Tagging - IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) - in the IAM User Guide. - items: - description: Tag represents user-provided metadata that can - be associated with a IAM role. For more information about - tagging, see Tagging IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) - in the IAM User Guide. - properties: - key: - description: The key name that can be used to look up or - retrieve the associated value. For example, Department - or Cost Center are common choices. - type: string - value: - description: "The value associated with this tag. For example, - tags with a key name of Department could have values such - as Human Resources, Accounting, and Support. Tags with - a key name of Cost Center might have values that consist - of the number associated with the different cost centers - in your company. Typically, many resources have tags with - the same key name but with different values. \n AWS always - interprets the tag Value as a single string. If you need - to store an array, you can store comma-separated values - in the string. However, you must interpret the value in - your code." - type: string - required: - - key - type: object - type: array - required: - - assumeRolePolicyDocument - type: object - providerConfigRef: - default: - name: default - description: ProviderConfigReference specifies how the provider that - will be used to create, observe, update, and delete this managed - resource should be configured. - properties: - name: - description: Name of the referenced object. - type: string - required: - - name - type: object - providerRef: - description: 'ProviderReference specifies the provider that will be - used to create, observe, update, and delete this managed resource. - Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`' - properties: - name: - description: Name of the referenced object. - type: string - required: - - name - type: object - writeConnectionSecretToRef: - description: WriteConnectionSecretToReference specifies the namespace - and name of a Secret to which any connection details for this managed - resource should be written. Connection details frequently include - the endpoint, username, and password required to connect to the - managed resource. - properties: - name: - description: Name of the secret. - type: string - namespace: - description: Namespace of the secret. - type: string - required: - - name - - namespace - type: object - required: - - forProvider - type: object - status: - description: An RoleStatus represents the observed state of an Role. - properties: - atProvider: - description: RoleExternalStatus keeps the state for the external resource - properties: - arn: - description: ARN is the Amazon Resource Name (ARN) specifying - the role. For more information about ARNs and how to use them - in policies, see IAM Identifiers (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) - in the IAM User Guide guide. - type: string - roleID: - description: RoleID is the stable and unique string identifying - the role. For more information about IDs, see IAM Identifiers - (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) - in the Using IAM guide. - type: string - required: - - arn - - roleID - type: object - conditions: - description: Conditions of the resource. - items: - description: A Condition that may apply to a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time this condition - transitioned from one status to another. - format: date-time - type: string - message: - description: A Message containing details about this condition's - last transition from one status to another, if any. - type: string - reason: - description: A Reason for this condition's last transition from - one status to another. - type: string - status: - description: Status of this condition; is it currently True, - False, or Unknown? - type: string - type: - description: Type of this condition. At most one of each condition - type may apply to a resource at any point in time. - type: string - required: - - lastTransitionTime - - reason - - status - - type - type: object - type: array - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: - - v1beta1 +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +apply: +- crd.yaml +assert: +- crd-assert.yaml diff --git a/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/crd-assert.yaml b/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/crd-assert.yaml new file mode 100644 index 0000000000..ea60c1e395 --- /dev/null +++ b/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/crd-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: roles.iam.aws.crossplane.io +status: + acceptedNames: + kind: Role + listKind: RoleList + plural: roles + singular: role diff --git a/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/crd.yaml b/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/crd.yaml new file mode 100644 index 0000000000..c2a379e3d0 --- /dev/null +++ b/test/conformance/kuttl/events/clusterpolicy/no-events-upon-fail-generation/crd.yaml @@ -0,0 +1,234 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: roles.iam.aws.crossplane.io +spec: + group: iam.aws.crossplane.io + names: + categories: + - crossplane + - managed + - aws + kind: Role + listKind: RoleList + plural: roles + shortNames: + - iamrole + singular: role + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: READY + type: string + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: SYNCED + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: An Role is a managed resource that represents an AWS IAM Role. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: An RoleSpec defines the desired state of an Role. + properties: + deletionPolicy: + default: Delete + description: DeletionPolicy specifies what will happen to the underlying + external when this managed resource is deleted - either "Delete" + or "Orphan" the external resource. + enum: + - Orphan + - Delete + type: string + forProvider: + description: RoleParameters define the desired state of an AWS IAM + Role. + properties: + assumeRolePolicyDocument: + description: AssumeRolePolicyDocument is the the trust relationship + policy document that grants an entity permission to assume the + role. + type: string + description: + description: Description is a description of the role. + type: string + maxSessionDuration: + description: 'MaxSessionDuration is the duration (in seconds) + that you want to set for the specified role. The default maximum + of one hour is applied. This setting can have a value from 1 + hour to 12 hours. Default: 3600' + format: int32 + type: integer + path: + description: 'Path is the path to the role. Default: /' + type: string + permissionsBoundary: + description: PermissionsBoundary is the ARN of the policy that + is used to set the permissions boundary for the role. + type: string + tags: + description: Tags. For more information about tagging, see Tagging + IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) + in the IAM User Guide. + items: + description: Tag represents user-provided metadata that can + be associated with a IAM role. For more information about + tagging, see Tagging IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) + in the IAM User Guide. + properties: + key: + description: The key name that can be used to look up or + retrieve the associated value. For example, Department + or Cost Center are common choices. + type: string + value: + description: "The value associated with this tag. For example, + tags with a key name of Department could have values such + as Human Resources, Accounting, and Support. Tags with + a key name of Cost Center might have values that consist + of the number associated with the different cost centers + in your company. Typically, many resources have tags with + the same key name but with different values. \n AWS always + interprets the tag Value as a single string. If you need + to store an array, you can store comma-separated values + in the string. However, you must interpret the value in + your code." + type: string + required: + - key + type: object + type: array + required: + - assumeRolePolicyDocument + type: object + providerConfigRef: + default: + name: default + description: ProviderConfigReference specifies how the provider that + will be used to create, observe, update, and delete this managed + resource should be configured. + properties: + name: + description: Name of the referenced object. + type: string + required: + - name + type: object + providerRef: + description: 'ProviderReference specifies the provider that will be + used to create, observe, update, and delete this managed resource. + Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`' + properties: + name: + description: Name of the referenced object. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: WriteConnectionSecretToReference specifies the namespace + and name of a Secret to which any connection details for this managed + resource should be written. Connection details frequently include + the endpoint, username, and password required to connect to the + managed resource. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - forProvider + type: object + status: + description: An RoleStatus represents the observed state of an Role. + properties: + atProvider: + description: RoleExternalStatus keeps the state for the external resource + properties: + arn: + description: ARN is the Amazon Resource Name (ARN) specifying + the role. For more information about ARNs and how to use them + in policies, see IAM Identifiers (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) + in the IAM User Guide guide. + type: string + roleID: + description: RoleID is the stable and unique string identifying + the role. For more information about IDs, see IAM Identifiers + (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) + in the Using IAM guide. + type: string + required: + - arn + - roleID + type: object + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: + - v1beta1