mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-15 20:20:22 +00:00
refactor: remove openapi package (#8538)
* refactor: openapi package Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * kubectl validate Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * rm Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * go mod Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix vscode Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
eedc993ed9
commit
482c243517
26 changed files with 3665 additions and 4851 deletions
|
@ -28,7 +28,6 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
"github.com/kyverno/kyverno/pkg/config"
|
"github.com/kyverno/kyverno/pkg/config"
|
||||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
gitutils "github.com/kyverno/kyverno/pkg/utils/git"
|
gitutils "github.com/kyverno/kyverno/pkg/utils/git"
|
||||||
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
|
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -142,10 +141,6 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, skipInvalidPolicies, nil, fmt.Errorf("failed to decode yaml (%w)", err)
|
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)
|
rc, resources1, skipInvalidPolicies, responses1, err, dClient := c.initStoreAndClusterClient(skipInvalidPolicies)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rc, resources1, skipInvalidPolicies, responses1, err
|
return rc, resources1, skipInvalidPolicies, responses1, err
|
||||||
|
@ -171,7 +166,6 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul
|
||||||
variables,
|
variables,
|
||||||
policies,
|
policies,
|
||||||
resources,
|
resources,
|
||||||
openApiManager,
|
|
||||||
&skipInvalidPolicies,
|
&skipInvalidPolicies,
|
||||||
dClient,
|
dClient,
|
||||||
userInfo,
|
userInfo,
|
||||||
|
@ -228,7 +222,6 @@ func (c *ApplyCommandConfig) applyPolicytoResource(
|
||||||
vars *variables.Variables,
|
vars *variables.Variables,
|
||||||
policies []kyvernov1.PolicyInterface,
|
policies []kyvernov1.PolicyInterface,
|
||||||
resources []*unstructured.Unstructured,
|
resources []*unstructured.Unstructured,
|
||||||
openApiManager openapi.Manager,
|
|
||||||
skipInvalidPolicies *SkippedInvalidPolicies,
|
skipInvalidPolicies *SkippedInvalidPolicies,
|
||||||
dClient dclient.Interface,
|
dClient dclient.Interface,
|
||||||
userInfo *v1beta1.RequestInfo,
|
userInfo *v1beta1.RequestInfo,
|
||||||
|
@ -241,7 +234,7 @@ func (c *ApplyCommandConfig) applyPolicytoResource(
|
||||||
var validPolicies []kyvernov1.PolicyInterface
|
var validPolicies []kyvernov1.PolicyInterface
|
||||||
for _, pol := range policies {
|
for _, pol := range policies {
|
||||||
// TODO we should return this info to the caller
|
// 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 {
|
if err != nil {
|
||||||
log.Log.Error(err, "policy validation error")
|
log.Log.Error(err, "policy validation error")
|
||||||
if strings.HasPrefix(err.Error(), "variable 'element.name'") {
|
if strings.HasPrefix(err.Error(), "variable 'element.name'") {
|
||||||
|
|
|
@ -14,10 +14,8 @@ import (
|
||||||
"github.com/google/go-containerregistry/pkg/v1/static"
|
"github.com/google/go-containerregistry/pkg/v1/static"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/oci/internal"
|
"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/cmd/cli/kubectl-kyverno/policy"
|
||||||
"github.com/kyverno/kyverno/pkg/config"
|
"github.com/kyverno/kyverno/pkg/config"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
policyutils "github.com/kyverno/kyverno/pkg/utils/policy"
|
policyutils "github.com/kyverno/kyverno/pkg/utils/policy"
|
||||||
policyvalidation "github.com/kyverno/kyverno/pkg/validation/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 {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to read policy file or directory %s (%w)", dir, err)
|
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 {
|
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)
|
return fmt.Errorf("validating policy %s: %v", policy.GetName(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,12 @@ import (
|
||||||
"github.com/go-git/go-billy/v5"
|
"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/apis/v1alpha1"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
|
"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/color"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/table"
|
"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/report"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/store"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/store"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/filter"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/filter"
|
||||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
)
|
)
|
||||||
|
@ -75,11 +73,6 @@ func testCommandExecute(
|
||||||
fmt.Fprintln(out, " Error:", e)
|
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
|
// load tests
|
||||||
tests, err := loadTests(dirPath, fileName, gitBranch)
|
tests, err := loadTests(dirPath, fileName, gitBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -122,7 +115,7 @@ func testCommandExecute(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resourcePath := filepath.Dir(test.Path)
|
resourcePath := filepath.Dir(test.Path)
|
||||||
responses, err := runTest(out, openApiManager, test, false)
|
responses, err := runTest(out, test, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to run test (%w)", err)
|
return fmt.Errorf("failed to run test (%w)", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,11 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
"github.com/kyverno/kyverno/pkg/config"
|
"github.com/kyverno/kyverno/pkg/config"
|
||||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
|
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"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
|
// don't process test case with errors
|
||||||
if testCase.Err != nil {
|
if testCase.Err != nil {
|
||||||
return nil, testCase.Err
|
return nil, testCase.Err
|
||||||
|
@ -116,7 +115,7 @@ func runTest(out io.Writer, openApiManager openapi.Manager, testCase test.TestCa
|
||||||
var validPolicies []kyvernov1.PolicyInterface
|
var validPolicies []kyvernov1.PolicyInterface
|
||||||
for _, pol := range policies {
|
for _, pol := range policies {
|
||||||
// TODO we should return this info to the caller
|
// 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 {
|
if err != nil {
|
||||||
log.Log.Error(err, "skipping invalid policy", "name", pol.GetName())
|
log.Log.Error(err, "skipping invalid policy", "name", pol.GetName())
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
genericloggingcontroller "github.com/kyverno/kyverno/pkg/controllers/generic/logging"
|
genericloggingcontroller "github.com/kyverno/kyverno/pkg/controllers/generic/logging"
|
||||||
genericwebhookcontroller "github.com/kyverno/kyverno/pkg/controllers/generic/webhook"
|
genericwebhookcontroller "github.com/kyverno/kyverno/pkg/controllers/generic/webhook"
|
||||||
policymetricscontroller "github.com/kyverno/kyverno/pkg/controllers/metrics/policy"
|
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"
|
policycachecontroller "github.com/kyverno/kyverno/pkg/controllers/policycache"
|
||||||
vapcontroller "github.com/kyverno/kyverno/pkg/controllers/validatingadmissionpolicy-generate"
|
vapcontroller "github.com/kyverno/kyverno/pkg/controllers/validatingadmissionpolicy-generate"
|
||||||
webhookcontroller "github.com/kyverno/kyverno/pkg/controllers/webhook"
|
webhookcontroller "github.com/kyverno/kyverno/pkg/controllers/webhook"
|
||||||
|
@ -29,7 +28,6 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/informers"
|
"github.com/kyverno/kyverno/pkg/informers"
|
||||||
"github.com/kyverno/kyverno/pkg/leaderelection"
|
"github.com/kyverno/kyverno/pkg/leaderelection"
|
||||||
"github.com/kyverno/kyverno/pkg/logging"
|
"github.com/kyverno/kyverno/pkg/logging"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
"github.com/kyverno/kyverno/pkg/policycache"
|
"github.com/kyverno/kyverno/pkg/policycache"
|
||||||
"github.com/kyverno/kyverno/pkg/tls"
|
"github.com/kyverno/kyverno/pkg/tls"
|
||||||
"github.com/kyverno/kyverno/pkg/toggle"
|
"github.com/kyverno/kyverno/pkg/toggle"
|
||||||
|
@ -82,7 +80,6 @@ func createNonLeaderControllers(
|
||||||
dynamicClient dclient.Interface,
|
dynamicClient dclient.Interface,
|
||||||
configuration config.Configuration,
|
configuration config.Configuration,
|
||||||
policyCache policycache.Cache,
|
policyCache policycache.Cache,
|
||||||
manager openapi.Manager,
|
|
||||||
) ([]internal.Controller, func(context.Context) error) {
|
) ([]internal.Controller, func(context.Context) error) {
|
||||||
policyCacheController := policycachecontroller.NewController(
|
policyCacheController := policycachecontroller.NewController(
|
||||||
dynamicClient,
|
dynamicClient,
|
||||||
|
@ -90,13 +87,8 @@ func createNonLeaderControllers(
|
||||||
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
|
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
|
||||||
kyvernoInformer.Kyverno().V1().Policies(),
|
kyvernoInformer.Kyverno().V1().Policies(),
|
||||||
)
|
)
|
||||||
openApiController := openapicontroller.NewController(
|
|
||||||
dynamicClient,
|
|
||||||
manager,
|
|
||||||
)
|
|
||||||
return []internal.Controller{
|
return []internal.Controller{
|
||||||
internal.NewController(policycachecontroller.ControllerName, policyCacheController, policycachecontroller.Workers),
|
internal.NewController(policycachecontroller.ControllerName, policyCacheController, policycachecontroller.Workers),
|
||||||
internal.NewController(openapicontroller.ControllerName, openApiController, openapicontroller.Workers),
|
|
||||||
},
|
},
|
||||||
func(ctx context.Context) error {
|
func(ctx context.Context) error {
|
||||||
if err := policyCacheController.WarmUp(); err != nil {
|
if err := policyCacheController.WarmUp(); err != nil {
|
||||||
|
@ -294,11 +286,6 @@ func main() {
|
||||||
kubeInformer := kubeinformers.NewSharedInformerFactory(setup.KubeClient, resyncPeriod)
|
kubeInformer := kubeinformers.NewSharedInformerFactory(setup.KubeClient, resyncPeriod)
|
||||||
kubeKyvernoInformer := kubeinformers.NewSharedInformerFactoryWithOptions(setup.KubeClient, resyncPeriod, kubeinformers.WithNamespace(config.KyvernoNamespace()))
|
kubeKyvernoInformer := kubeinformers.NewSharedInformerFactoryWithOptions(setup.KubeClient, resyncPeriod, kubeinformers.WithNamespace(config.KyvernoNamespace()))
|
||||||
kyvernoInformer := kyvernoinformer.NewSharedInformerFactory(setup.KyvernoClient, resyncPeriod)
|
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
|
var wg sync.WaitGroup
|
||||||
certRenewer := tls.NewCertRenewer(
|
certRenewer := tls.NewCertRenewer(
|
||||||
setup.KubeClient.CoreV1().Secrets(config.KyvernoNamespace()),
|
setup.KubeClient.CoreV1().Secrets(config.KyvernoNamespace()),
|
||||||
|
@ -375,7 +362,6 @@ func main() {
|
||||||
setup.KyvernoDynamicClient,
|
setup.KyvernoDynamicClient,
|
||||||
setup.Configuration,
|
setup.Configuration,
|
||||||
policyCache,
|
policyCache,
|
||||||
openApiManager,
|
|
||||||
)
|
)
|
||||||
// start informers and wait for cache sync
|
// start informers and wait for cache sync
|
||||||
if !internal.StartInformersAndWaitForCacheSync(signalCtx, setup.Logger, kyvernoInformer, kubeInformer, kubeKyvernoInformer) {
|
if !internal.StartInformersAndWaitForCacheSync(signalCtx, setup.Logger, kyvernoInformer, kubeInformer, kubeKyvernoInformer) {
|
||||||
|
@ -473,7 +459,6 @@ func main() {
|
||||||
)
|
)
|
||||||
policyHandlers := webhookspolicy.NewHandlers(
|
policyHandlers := webhookspolicy.NewHandlers(
|
||||||
setup.KyvernoDynamicClient,
|
setup.KyvernoDynamicClient,
|
||||||
openApiManager,
|
|
||||||
backgroundServiceAccountName,
|
backgroundServiceAccountName,
|
||||||
)
|
)
|
||||||
resourceHandlers := webhooksresource.NewHandlers(
|
resourceHandlers := webhooksresource.NewHandlers(
|
||||||
|
@ -489,7 +474,6 @@ func main() {
|
||||||
kyvernoInformer.Kyverno().V1().Policies(),
|
kyvernoInformer.Kyverno().V1().Policies(),
|
||||||
urgen,
|
urgen,
|
||||||
eventGenerator,
|
eventGenerator,
|
||||||
openApiManager,
|
|
||||||
admissionReports,
|
admissionReports,
|
||||||
backgroundServiceAccountName,
|
backgroundServiceAccountName,
|
||||||
setup.Jp,
|
setup.Jp,
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -36,7 +36,6 @@ require (
|
||||||
github.com/onsi/gomega v1.27.10
|
github.com/onsi/gomega v1.27.10
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc5
|
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/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.16.0
|
github.com/prometheus/client_golang v1.16.0
|
||||||
github.com/robfig/cron v1.2.0
|
github.com/robfig/cron v1.2.0
|
||||||
|
@ -70,7 +69,6 @@ require (
|
||||||
google.golang.org/grpc v1.58.2
|
google.golang.org/grpc v1.58.2
|
||||||
gopkg.in/inf.v0 v0.9.1
|
gopkg.in/inf.v0 v0.9.1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
gotest.tools v2.2.0+incompatible
|
gotest.tools v2.2.0+incompatible
|
||||||
k8s.io/api v0.28.2
|
k8s.io/api v0.28.2
|
||||||
k8s.io/apiextensions-apiserver v0.28.1
|
k8s.io/apiextensions-apiserver v0.28.1
|
||||||
|
@ -80,7 +78,6 @@ require (
|
||||||
k8s.io/client-go v0.28.2
|
k8s.io/client-go v0.28.2
|
||||||
k8s.io/klog/v2 v2.100.1
|
k8s.io/klog/v2 v2.100.1
|
||||||
k8s.io/kube-aggregator v0.28.2
|
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/pod-security-admission v0.28.2
|
||||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
|
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
|
||||||
sigs.k8s.io/controller-runtime v0.16.2
|
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/square/go-jose.v2 v2.6.0 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // 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
|
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
|
||||||
k8s.io/component-base v0.28.2 // 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
|
k8s.io/kubectl v0.28.2 // indirect
|
||||||
oras.land/oras-go/v2 v2.3.0 // indirect
|
oras.land/oras-go/v2 v2.3.0 // indirect
|
||||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.4 // indirect
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.4 // indirect
|
||||||
|
|
2
go.sum
2
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.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.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||||
github.com/openzipkin/zipkin-go v0.2.2/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.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 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0=
|
||||||
github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac=
|
github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac=
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package openapi
|
|
||||||
|
|
||||||
import "github.com/kyverno/kyverno/pkg/logging"
|
|
||||||
|
|
||||||
var logger = logging.ControllerLogger(ControllerName)
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"`
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -3,30 +3,17 @@ package policy
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
|
||||||
|
|
||||||
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
|
|
||||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
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) {
|
func FuzzValidatePolicy(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
ff := fuzz.NewConsumer(data)
|
ff := fuzz.NewConsumer(data)
|
||||||
p := &kyverno.ClusterPolicy{}
|
p := &kyverno.ClusterPolicy{}
|
||||||
ff.GenerateStruct(p)
|
ff.GenerateStruct(p)
|
||||||
|
|
||||||
Validate(p, nil, nil, true, fuzzOpenApiManager, "admin")
|
Validate(p, nil, nil, true, "admin")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package policy
|
package policy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -19,18 +18,15 @@ import (
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
"github.com/kyverno/kyverno/pkg/autogen"
|
"github.com/kyverno/kyverno/pkg/autogen"
|
||||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
openapicontroller "github.com/kyverno/kyverno/pkg/controllers/openapi"
|
|
||||||
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/variables"
|
"github.com/kyverno/kyverno/pkg/engine/variables"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
|
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
|
||||||
"github.com/kyverno/kyverno/pkg/logging"
|
"github.com/kyverno/kyverno/pkg/logging"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
apiutils "github.com/kyverno/kyverno/pkg/utils/api"
|
apiutils "github.com/kyverno/kyverno/pkg/utils/api"
|
||||||
datautils "github.com/kyverno/kyverno/pkg/utils/data"
|
datautils "github.com/kyverno/kyverno/pkg/utils/data"
|
||||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||||
"github.com/kyverno/kyverno/pkg/utils/wildcard"
|
"github.com/kyverno/kyverno/pkg/utils/wildcard"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
"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/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apimachinery/pkg/util/yaml"
|
"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
|
// 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
|
var warnings []string
|
||||||
spec := policy.GetSpec()
|
spec := policy.GetSpec()
|
||||||
background := spec.BackgroundProcessingEnabled()
|
background := spec.BackgroundProcessingEnabled()
|
||||||
mutateExistingOnPolicyUpdate := spec.GetMutateExistingOnPolicyUpdate()
|
mutateExistingOnPolicyUpdate := spec.GetMutateExistingOnPolicyUpdate()
|
||||||
if !mock {
|
|
||||||
openapicontroller.NewController(client, openApiManager).CheckSync(context.TODO())
|
|
||||||
}
|
|
||||||
|
|
||||||
warnings = append(warnings, checkValidationFailureAction(spec)...)
|
warnings = append(warnings, checkValidationFailureAction(spec)...)
|
||||||
var errs field.ErrorList
|
var errs field.ErrorList
|
||||||
|
@ -145,11 +138,14 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []*metav1.APIResourceList
|
getClusteredResources := func(invalidate bool) (sets.Set[string], error) {
|
||||||
clusterResources := sets.New[string]()
|
clusterResources := sets.New[string]()
|
||||||
if !mock {
|
|
||||||
// Get all the cluster type kind supported by cluster
|
// 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 err != nil {
|
||||||
if discovery.IsGroupDiscoveryFailedError(err) {
|
if discovery.IsGroupDiscoveryFailedError(err) {
|
||||||
err := err.(*discovery.ErrGroupDiscoveryFailed)
|
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)
|
logging.Error(err, "failed to list api resources", "group", gv)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return warnings, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, resList := range res {
|
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 {
|
// if not using a mock, we first try to validate and if it fails we retry with cache invalidation in between
|
||||||
return warnings, errs.ToAggregate()
|
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() {
|
if !policy.IsNamespaced() {
|
||||||
|
@ -364,11 +379,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
|
||||||
checkForDeprecatedFieldsInVerifyImages(rule, &warnings)
|
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
|
return warnings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,6 @@ import (
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
||||||
policyvalidate "github.com/kyverno/kyverno/pkg/validation/policy"
|
policyvalidate "github.com/kyverno/kyverno/pkg/validation/policy"
|
||||||
"github.com/kyverno/kyverno/pkg/webhooks"
|
"github.com/kyverno/kyverno/pkg/webhooks"
|
||||||
|
@ -15,14 +14,12 @@ import (
|
||||||
|
|
||||||
type policyHandlers struct {
|
type policyHandlers struct {
|
||||||
client dclient.Interface
|
client dclient.Interface
|
||||||
openApiManager openapi.Manager
|
|
||||||
backgroungServiceAccountName string
|
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{
|
return &policyHandlers{
|
||||||
client: client,
|
client: client,
|
||||||
openApiManager: openApiManager,
|
|
||||||
backgroungServiceAccountName: serviceaccount,
|
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")
|
logger.Error(err, "failed to unmarshal policies from admission request")
|
||||||
return admissionutils.Response(request.UID, err)
|
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 {
|
if err != nil {
|
||||||
logger.Error(err, "policy validation errors")
|
logger.Error(err, "policy validation errors")
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/event"
|
"github.com/kyverno/kyverno/pkg/event"
|
||||||
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
||||||
"github.com/kyverno/kyverno/pkg/metrics"
|
"github.com/kyverno/kyverno/pkg/metrics"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
"github.com/kyverno/kyverno/pkg/policycache"
|
"github.com/kyverno/kyverno/pkg/policycache"
|
||||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||||
"github.com/kyverno/kyverno/pkg/webhooks"
|
"github.com/kyverno/kyverno/pkg/webhooks"
|
||||||
|
@ -45,16 +44,15 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook
|
||||||
rclient := registryclient.NewOrDie()
|
rclient := registryclient.NewOrDie()
|
||||||
|
|
||||||
return &resourceHandlers{
|
return &resourceHandlers{
|
||||||
client: dclient,
|
client: dclient,
|
||||||
configuration: configuration,
|
configuration: configuration,
|
||||||
metricsConfig: metricsConfig,
|
metricsConfig: metricsConfig,
|
||||||
pCache: policyCache,
|
pCache: policyCache,
|
||||||
nsLister: informers.Core().V1().Namespaces().Lister(),
|
nsLister: informers.Core().V1().Namespaces().Lister(),
|
||||||
urLister: urLister,
|
urLister: urLister,
|
||||||
urGenerator: updaterequest.NewFake(),
|
urGenerator: updaterequest.NewFake(),
|
||||||
eventGen: event.NewFake(),
|
eventGen: event.NewFake(),
|
||||||
openApiManager: openapi.NewFake(),
|
pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp),
|
||||||
pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp),
|
|
||||||
engine: engine.NewEngine(
|
engine: engine.NewEngine(
|
||||||
configuration,
|
configuration,
|
||||||
config.NewDefaultMetricsConfiguration(),
|
config.NewDefaultMetricsConfiguration(),
|
||||||
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||||
"github.com/kyverno/kyverno/pkg/event"
|
"github.com/kyverno/kyverno/pkg/event"
|
||||||
"github.com/kyverno/kyverno/pkg/metrics"
|
"github.com/kyverno/kyverno/pkg/metrics"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
"github.com/kyverno/kyverno/pkg/policycache"
|
"github.com/kyverno/kyverno/pkg/policycache"
|
||||||
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
||||||
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
|
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
|
||||||
|
@ -52,10 +51,9 @@ type resourceHandlers struct {
|
||||||
cpolLister kyvernov1listers.ClusterPolicyLister
|
cpolLister kyvernov1listers.ClusterPolicyLister
|
||||||
polLister kyvernov1listers.PolicyLister
|
polLister kyvernov1listers.PolicyLister
|
||||||
|
|
||||||
urGenerator webhookgenerate.Generator
|
urGenerator webhookgenerate.Generator
|
||||||
eventGen event.Interface
|
eventGen event.Interface
|
||||||
openApiManager openapi.ValidateInterface
|
pcBuilder webhookutils.PolicyContextBuilder
|
||||||
pcBuilder webhookutils.PolicyContextBuilder
|
|
||||||
|
|
||||||
admissionReports bool
|
admissionReports bool
|
||||||
backgroungServiceAccountName string
|
backgroungServiceAccountName string
|
||||||
|
@ -74,7 +72,6 @@ func NewHandlers(
|
||||||
polInformer kyvernov1informers.PolicyInformer,
|
polInformer kyvernov1informers.PolicyInformer,
|
||||||
urGenerator webhookgenerate.Generator,
|
urGenerator webhookgenerate.Generator,
|
||||||
eventGen event.Interface,
|
eventGen event.Interface,
|
||||||
openApiManager openapi.ValidateInterface,
|
|
||||||
admissionReports bool,
|
admissionReports bool,
|
||||||
backgroungServiceAccountName string,
|
backgroungServiceAccountName string,
|
||||||
jp jmespath.Interface,
|
jp jmespath.Interface,
|
||||||
|
@ -92,7 +89,6 @@ func NewHandlers(
|
||||||
polLister: polInformer.Lister(),
|
polLister: polInformer.Lister(),
|
||||||
urGenerator: urGenerator,
|
urGenerator: urGenerator,
|
||||||
eventGen: eventGen,
|
eventGen: eventGen,
|
||||||
openApiManager: openApiManager,
|
|
||||||
pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp),
|
pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp),
|
||||||
admissionReports: admissionReports,
|
admissionReports: admissionReports,
|
||||||
backgroungServiceAccountName: backgroungServiceAccountName,
|
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")
|
logger.Error(err, "failed to build policy context")
|
||||||
return admissionutils.Response(request.UID, err)
|
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)
|
mutatePatches, mutateWarnings, err := mh.HandleMutation(ctx, request.AdmissionRequest, mutatePolicies, policyContext, startTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "mutation failed")
|
logger.Error(err, "mutation failed")
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/engine/mutate/patch"
|
"github.com/kyverno/kyverno/pkg/engine/mutate/patch"
|
||||||
"github.com/kyverno/kyverno/pkg/event"
|
"github.com/kyverno/kyverno/pkg/event"
|
||||||
"github.com/kyverno/kyverno/pkg/metrics"
|
"github.com/kyverno/kyverno/pkg/metrics"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
|
||||||
"github.com/kyverno/kyverno/pkg/tracing"
|
"github.com/kyverno/kyverno/pkg/tracing"
|
||||||
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
|
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
|
||||||
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
|
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
|
||||||
|
@ -34,27 +33,24 @@ func NewMutationHandler(
|
||||||
log logr.Logger,
|
log logr.Logger,
|
||||||
engine engineapi.Engine,
|
engine engineapi.Engine,
|
||||||
eventGen event.Interface,
|
eventGen event.Interface,
|
||||||
openApiManager openapi.ValidateInterface,
|
|
||||||
nsLister corev1listers.NamespaceLister,
|
nsLister corev1listers.NamespaceLister,
|
||||||
metrics metrics.MetricsConfigManager,
|
metrics metrics.MetricsConfigManager,
|
||||||
) MutationHandler {
|
) MutationHandler {
|
||||||
return &mutationHandler{
|
return &mutationHandler{
|
||||||
log: log,
|
log: log,
|
||||||
engine: engine,
|
engine: engine,
|
||||||
eventGen: eventGen,
|
eventGen: eventGen,
|
||||||
openApiManager: openApiManager,
|
nsLister: nsLister,
|
||||||
nsLister: nsLister,
|
metrics: metrics,
|
||||||
metrics: metrics,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type mutationHandler struct {
|
type mutationHandler struct {
|
||||||
log logr.Logger
|
log logr.Logger
|
||||||
engine engineapi.Engine
|
engine engineapi.Engine
|
||||||
eventGen event.Interface
|
eventGen event.Interface
|
||||||
openApiManager openapi.ValidateInterface
|
nsLister corev1listers.NamespaceLister
|
||||||
nsLister corev1listers.NamespaceLister
|
metrics metrics.MetricsConfigManager
|
||||||
metrics metrics.MetricsConfigManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mutationHandler) HandleMutation(
|
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())
|
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
|
return &engineResponse, policyPatches, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,234 +1,6 @@
|
||||||
apiVersion: apiextensions.k8s.io/v1
|
apiVersion: kuttl.dev/v1beta1
|
||||||
kind: CustomResourceDefinition
|
kind: TestStep
|
||||||
metadata:
|
apply:
|
||||||
creationTimestamp: null
|
- crd.yaml
|
||||||
name: roles.iam.aws.crossplane.io
|
assert:
|
||||||
spec:
|
- crd-assert.yaml
|
||||||
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
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Add table
Reference in a new issue