mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-05 07:26:55 +00:00
Supporting ValidatingAdmissionPolicy in kyverno cli (apply and test command) (#6656)
* feat: add policy reporter to the dev lab Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * refactor: remove obsolete structs from CLI Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more 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> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * codegen Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * Supporting ValidatingAdmissionPolicy in kyverno apply Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * chore: bump k8s from v0.26.3 to v0.27.0-rc.0 Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * Support validating admission policy in kyverno apply Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * Support validating admission policy in kyverno test Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * refactoring Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * Adding kyverno apply tests for validating admission policy Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * fix Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * fix Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * running codegen-all Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * fix Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * Adding IsVap field in TestResults Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * chore: bump k8s from v0.27.0-rc.0 to v0.27.1 Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * fix Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * fix Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * Fix vap in engine response Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> * codegen Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Signed-off-by: Mariam Fahmy <mariamfahmy66@gmail.com> Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Co-authored-by: Jim Bugwadia <jim@nirmata.com>
This commit is contained in:
parent
7a25afd01f
commit
bb628e1fe6
28 changed files with 1167 additions and 369 deletions
|
@ -24,6 +24,7 @@ import (
|
|||
gitutils "github.com/kyverno/kyverno/pkg/utils/git"
|
||||
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
@ -255,6 +256,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
|
|||
}
|
||||
|
||||
var policies []kyvernov1.PolicyInterface
|
||||
var validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy
|
||||
|
||||
isGit := common.IsGitSourcePath(c.PolicyPaths)
|
||||
|
||||
|
@ -289,7 +291,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
|
|||
sort.Strings(policyYamls)
|
||||
c.PolicyPaths = policyYamls
|
||||
}
|
||||
policies, err = common.GetPoliciesFromPaths(fs, c.PolicyPaths, isGit, "")
|
||||
policies, validatingAdmissionPolicies, err = common.GetPoliciesFromPaths(fs, c.PolicyPaths, isGit, "")
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
|
||||
osExit(1)
|
||||
|
@ -326,7 +328,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
|
|||
return rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError("failed to marshal mutated policy", err)
|
||||
}
|
||||
|
||||
resources, err = common.GetResourceAccordingToResourcePath(fs, c.ResourcePaths, c.Cluster, policies, dClient, c.Namespace, c.PolicyReport, false, "")
|
||||
resources, err = common.GetResourceAccordingToResourcePath(fs, c.ResourcePaths, c.Cluster, policies, validatingAdmissionPolicies, dClient, c.Namespace, c.PolicyReport, false, "")
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
|
||||
osExit(1)
|
||||
|
@ -387,6 +389,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
|
|||
skipInvalidPolicies.skipped = make([]string, 0)
|
||||
skipInvalidPolicies.invalid = make([]string, 0)
|
||||
|
||||
kyvernoPolicy := common.KyvernoPolicies{}
|
||||
for _, policy := range policies {
|
||||
_, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
if err != nil {
|
||||
|
@ -435,7 +438,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
|
|||
AuditWarn: c.AuditWarn,
|
||||
Subresources: subresources,
|
||||
}
|
||||
ers, err := common.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
ers, err := kyvernoPolicy.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
if err != nil {
|
||||
return rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
||||
}
|
||||
|
@ -488,6 +491,26 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
|
|||
}
|
||||
}
|
||||
|
||||
validatingAdmissionPolicy := common.ValidatingAdmissionPolicies{}
|
||||
for _, policy := range validatingAdmissionPolicies {
|
||||
for _, resource := range resources {
|
||||
applyPolicyConfig := common.ApplyPolicyConfig{
|
||||
ValidatingAdmissionPolicy: policy,
|
||||
Resource: resource,
|
||||
PolicyReport: c.PolicyReport,
|
||||
Rc: rc,
|
||||
Client: dClient,
|
||||
AuditWarn: c.AuditWarn,
|
||||
Subresources: subresources,
|
||||
}
|
||||
ers, err := validatingAdmissionPolicy.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
if err != nil {
|
||||
return rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
||||
}
|
||||
responses = append(responses, ers...)
|
||||
}
|
||||
}
|
||||
|
||||
return rc, resources, skipInvalidPolicies, responses, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -174,6 +174,42 @@ func Test_Apply(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/validate-deployment/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/validate-deployment/deployment1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/validate-deployment/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/validate-deployment/deployment2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 0,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
compareSummary := func(expected preport.PolicyReportSummary, actual map[string]interface{}, desc string) {
|
||||
|
|
|
@ -77,9 +77,24 @@ func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineRespo
|
|||
now := metav1.Timestamp{Seconds: time.Now().Unix()}
|
||||
|
||||
for _, engineResponse := range engineResponses {
|
||||
policy := engineResponse.Policy()
|
||||
var ns, policyName string
|
||||
var ann map[string]string
|
||||
|
||||
isVAP := engineResponse.IsValidatingAdmissionPolicy()
|
||||
|
||||
if isVAP {
|
||||
validatingAdmissionPolicy := engineResponse.ValidatingAdmissionPolicy()
|
||||
ns = validatingAdmissionPolicy.GetNamespace()
|
||||
policyName = validatingAdmissionPolicy.GetName()
|
||||
ann = validatingAdmissionPolicy.GetAnnotations()
|
||||
} else {
|
||||
kyvernoPolicy := engineResponse.Policy()
|
||||
ns = kyvernoPolicy.GetNamespace()
|
||||
policyName = kyvernoPolicy.GetName()
|
||||
ann = kyvernoPolicy.GetAnnotations()
|
||||
}
|
||||
|
||||
var appname string
|
||||
ns := policy.GetNamespace()
|
||||
if ns != "" {
|
||||
appname = fmt.Sprintf("policyreport-ns-%s", ns)
|
||||
} else {
|
||||
|
@ -92,7 +107,7 @@ func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineRespo
|
|||
}
|
||||
|
||||
result := policyreportv1alpha2.PolicyReportResult{
|
||||
Policy: policy.GetName(),
|
||||
Policy: policyName,
|
||||
Resources: []corev1.ObjectReference{
|
||||
{
|
||||
Kind: engineResponse.Resource.GetKind(),
|
||||
|
@ -105,7 +120,6 @@ func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineRespo
|
|||
Scored: true,
|
||||
}
|
||||
|
||||
ann := engineResponse.Policy().GetAnnotations()
|
||||
if ruleResponse.Status() == engineapi.RuleStatusSkip {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusError {
|
||||
|
@ -124,7 +138,9 @@ func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineRespo
|
|||
fmt.Println(ruleResponse)
|
||||
}
|
||||
|
||||
result.Rule = ruleResponse.Name()
|
||||
if !isVAP {
|
||||
result.Rule = ruleResponse.Name()
|
||||
}
|
||||
result.Message = ruleResponse.Message()
|
||||
result.Source = kyvernov1.ValueKyvernoApp
|
||||
result.Timestamp = now
|
||||
|
|
|
@ -92,7 +92,7 @@ kyverno oci pull -i <imgref> -d policies`,
|
|||
if err != nil {
|
||||
return fmt.Errorf("reading layer blob: %v", err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(layerBytes)
|
||||
policies, _, err := yamlutils.GetPolicy(layerBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling layer blob: %v", err)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ kyverno oci push -p policies. -i <imgref>`,
|
|||
return errors.New("image reference is required")
|
||||
}
|
||||
|
||||
policies, errs := common.GetPolicies([]string{policyRef})
|
||||
policies, _, errs := common.GetPolicies([]string{policyRef})
|
||||
if len(errs) != 0 {
|
||||
return fmt.Errorf("unable to read policy file or directory %s: %w", policyRef, multierr.Combine(errs...))
|
||||
}
|
||||
|
|
|
@ -18,7 +18,13 @@ type TestResults struct {
|
|||
// Policy mentions the name of the policy.
|
||||
Policy string `json:"policy"`
|
||||
// Rule mentions the name of the rule in the policy.
|
||||
Rule string `json:"rule"`
|
||||
// It's required in case policy is a kyverno policy.
|
||||
// +optional
|
||||
Rule string `json:"rule,omitempty"`
|
||||
// IsVap indicates if the policy is a validating admission policy.
|
||||
// It's required in case policy is a validating admission policy.
|
||||
// +optional
|
||||
IsVap bool `json:"isVap"`
|
||||
// Result mentions the result that the user is expecting.
|
||||
// Possible values are pass, fail and skip.
|
||||
Result policyreportv1alpha2.PolicyResult `json:"result"`
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/openapi"
|
||||
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
|
||||
"golang.org/x/exp/slices"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
@ -102,7 +103,7 @@ func applyPoliciesFromPath(
|
|||
values.Results[i].CloneSourceResource = CloneSourceResourceFullPath[0]
|
||||
}
|
||||
|
||||
policies, err := common.GetPoliciesFromPaths(fs, policyFullPath, isGit, policyResourcePath)
|
||||
policies, validatingAdmissionPolicies, err := common.GetPoliciesFromPaths(fs, policyFullPath, isGit, policyResourcePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
@ -118,12 +119,27 @@ func applyPoliciesFromPath(
|
|||
}
|
||||
}
|
||||
|
||||
var filteredVAPs []v1alpha1.ValidatingAdmissionPolicy
|
||||
for _, p := range validatingAdmissionPolicies {
|
||||
for _, res := range values.Results {
|
||||
if p.GetName() == res.Policy {
|
||||
filteredVAPs = append(filteredVAPs, p)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
validatingAdmissionPolicies = filteredVAPs
|
||||
|
||||
ruleToCloneSourceResource := map[string]string{}
|
||||
for _, p := range filteredPolicies {
|
||||
var filteredRules []kyvernov1.Rule
|
||||
|
||||
for _, rule := range autogen.ComputeRules(p) {
|
||||
for _, res := range values.Results {
|
||||
if res.IsVap {
|
||||
continue
|
||||
}
|
||||
|
||||
if rule.Name == res.Rule {
|
||||
filteredRules = append(filteredRules, rule)
|
||||
if rule.HasGenerate() {
|
||||
|
@ -156,7 +172,7 @@ func applyPoliciesFromPath(
|
|||
return nil, nil, sanitizederror.NewWithError("failed to print mutated policy", err)
|
||||
}
|
||||
|
||||
resources, err := common.GetResourceAccordingToResourcePath(fs, resourceFullPath, false, policies, dClient, "", false, isGit, policyResourcePath)
|
||||
resources, err := common.GetResourceAccordingToResourcePath(fs, resourceFullPath, false, policies, validatingAdmissionPolicies, dClient, "", false, isGit, policyResourcePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
@ -165,8 +181,8 @@ func applyPoliciesFromPath(
|
|||
checkableResources := selectResourcesForCheck(resources, values)
|
||||
|
||||
msgPolicies := "1 policy"
|
||||
if len(policies) > 1 {
|
||||
msgPolicies = fmt.Sprintf("%d policies", len(policies))
|
||||
if len(policies)+len(validatingAdmissionPolicies) > 1 {
|
||||
msgPolicies = fmt.Sprintf("%d policies", len(policies)+len(validatingAdmissionPolicies))
|
||||
}
|
||||
|
||||
msgResources := "1 resource"
|
||||
|
@ -178,6 +194,7 @@ func applyPoliciesFromPath(
|
|||
fmt.Printf("applying %s to %s... \n", msgPolicies, msgResources)
|
||||
}
|
||||
|
||||
kyvernoPolicy := common.KyvernoPolicies{}
|
||||
for _, policy := range policies {
|
||||
_, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
if err != nil {
|
||||
|
@ -217,7 +234,26 @@ func applyPoliciesFromPath(
|
|||
Client: dClient,
|
||||
Subresources: subresources,
|
||||
}
|
||||
ers, err := common.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
ers, err := kyvernoPolicy.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
if err != nil {
|
||||
return nil, nil, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
||||
}
|
||||
engineResponses = append(engineResponses, ers...)
|
||||
}
|
||||
}
|
||||
|
||||
validatingAdmissionPolicy := common.ValidatingAdmissionPolicies{}
|
||||
for _, policy := range validatingAdmissionPolicies {
|
||||
for _, resource := range resources {
|
||||
applyPolicyConfig := common.ApplyPolicyConfig{
|
||||
ValidatingAdmissionPolicy: policy,
|
||||
Resource: resource,
|
||||
PolicyReport: true,
|
||||
Rc: &resultCounts,
|
||||
Client: dClient,
|
||||
Subresources: subresources,
|
||||
}
|
||||
ers, err := validatingAdmissionPolicy.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
if err != nil {
|
||||
return nil, nil, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
||||
}
|
||||
|
@ -304,11 +340,28 @@ func buildPolicyResults(
|
|||
results := map[string]policyreportv1alpha2.PolicyReportResult{}
|
||||
|
||||
for _, resp := range engineResponses {
|
||||
policyName := resp.Policy().GetName()
|
||||
var ns, name string
|
||||
var ann map[string]string
|
||||
|
||||
isVAP := resp.IsValidatingAdmissionPolicy()
|
||||
|
||||
if isVAP {
|
||||
validatingAdmissionPolicy := resp.ValidatingAdmissionPolicy()
|
||||
ns = validatingAdmissionPolicy.GetNamespace()
|
||||
name = validatingAdmissionPolicy.GetName()
|
||||
ann = validatingAdmissionPolicy.GetAnnotations()
|
||||
} else {
|
||||
kyvernoPolicy := resp.Policy()
|
||||
ns = kyvernoPolicy.GetNamespace()
|
||||
name = kyvernoPolicy.GetName()
|
||||
ann = kyvernoPolicy.GetAnnotations()
|
||||
}
|
||||
|
||||
policyName := name
|
||||
resourceName := resp.Resource.GetName()
|
||||
resourceKind := resp.Resource.GetKind()
|
||||
resourceNamespace := resp.Resource.GetNamespace()
|
||||
policyNamespace := resp.Policy().GetNamespace()
|
||||
policyNamespace := ns
|
||||
|
||||
var rules []string
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
|
@ -351,29 +404,32 @@ func buildPolicyResults(
|
|||
for _, resource := range test.Resources {
|
||||
if resource == resourceName {
|
||||
var resultsKey string
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource)
|
||||
if !slices.Contains(rules, test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-"+test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-cronjob-"+test.Rule) {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsVap)
|
||||
if !test.IsVap {
|
||||
if !slices.Contains(rules, test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-"+test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-cronjob-"+test.Rule) {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsVap)
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource)
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsVap)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
results[resultsKey] = result
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
results[resultsKey] = result
|
||||
}
|
||||
patchedResourcePath = append(patchedResourcePath, test.PatchedResource)
|
||||
}
|
||||
|
||||
patchedResourcePath = append(patchedResourcePath, test.PatchedResource)
|
||||
if _, ok := results[resultsKey]; !ok {
|
||||
results[resultsKey] = result
|
||||
}
|
||||
|
@ -384,29 +440,32 @@ func buildPolicyResults(
|
|||
if test.Resource != "" {
|
||||
if test.Policy == policyName && test.Resource == resourceName {
|
||||
var resultsKey string
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
|
||||
if !slices.Contains(rules, test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-"+test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-cronjob-"+test.Rule) {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsVap)
|
||||
if !test.IsVap {
|
||||
if !slices.Contains(rules, test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-"+test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-cronjob-"+test.Rule) {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsVap)
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsVap)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
results[resultsKey] = result
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
results[resultsKey] = result
|
||||
}
|
||||
patchedResourcePath = append(patchedResourcePath, test.PatchedResource)
|
||||
}
|
||||
|
||||
patchedResourcePath = append(patchedResourcePath, test.PatchedResource)
|
||||
if _, ok := results[resultsKey]; !ok {
|
||||
results[resultsKey] = result
|
||||
}
|
||||
|
@ -421,7 +480,7 @@ func buildPolicyResults(
|
|||
var resultsKey []string
|
||||
var resultKey string
|
||||
var result policyreportv1alpha2.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName)
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsVap)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
|
@ -454,7 +513,7 @@ func buildPolicyResults(
|
|||
var resultsKey []string
|
||||
var resultKey string
|
||||
var result policyreportv1alpha2.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName)
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsVap)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
|
@ -484,14 +543,14 @@ func buildPolicyResults(
|
|||
}
|
||||
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
if rule.RuleType() != engineapi.Validation && rule.RuleType() != engineapi.ImageVerify || test.Rule != rule.Name() {
|
||||
if rule.RuleType() != engineapi.Validation && rule.RuleType() != engineapi.ImageVerify || test.Rule != rule.Name() && !test.IsVap {
|
||||
continue
|
||||
}
|
||||
|
||||
var resultsKey []string
|
||||
var resultKey string
|
||||
var result policyreportv1alpha2.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName)
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsVap)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
|
@ -500,7 +559,6 @@ func buildPolicyResults(
|
|||
continue
|
||||
}
|
||||
|
||||
ann := resp.Policy().GetAnnotations()
|
||||
if rule.Status() == engineapi.RuleStatusSkip {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else if rule.Status() == engineapi.RuleStatusError {
|
||||
|
@ -527,27 +585,50 @@ func buildPolicyResults(
|
|||
return results, testResults
|
||||
}
|
||||
|
||||
func GetAllPossibleResultsKey(policyNamespace, policy, rule, resourceNamespace, kind, resource string) []string {
|
||||
func GetAllPossibleResultsKey(policyNamespace, policy, rule, resourceNamespace, kind, resource string, isVap bool) []string {
|
||||
var resultsKey []string
|
||||
resultKey1 := fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
|
||||
resultKey2 := fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, resourceNamespace, kind, resource)
|
||||
resultKey3 := fmt.Sprintf("%s-%s-%s-%s-%s", policyNamespace, policy, rule, kind, resource)
|
||||
resultKey4 := fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNamespace, policy, rule, resourceNamespace, kind, resource)
|
||||
var resultKey1, resultKey2, resultKey3, resultKey4 string
|
||||
|
||||
if isVap {
|
||||
resultKey1 = fmt.Sprintf("%s-%s-%s", policy, kind, resource)
|
||||
resultKey2 = fmt.Sprintf("%s-%s-%s-%s", policy, resourceNamespace, kind, resource)
|
||||
resultKey3 = fmt.Sprintf("%s-%s-%s-%s", policyNamespace, policy, kind, resource)
|
||||
resultKey4 = fmt.Sprintf("%s-%s-%s-%s-%s", policyNamespace, policy, resourceNamespace, kind, resource)
|
||||
} else {
|
||||
resultKey1 = fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
|
||||
resultKey2 = fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, resourceNamespace, kind, resource)
|
||||
resultKey3 = fmt.Sprintf("%s-%s-%s-%s-%s", policyNamespace, policy, rule, kind, resource)
|
||||
resultKey4 = fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNamespace, policy, rule, resourceNamespace, kind, resource)
|
||||
}
|
||||
|
||||
resultsKey = append(resultsKey, resultKey1, resultKey2, resultKey3, resultKey4)
|
||||
return resultsKey
|
||||
}
|
||||
|
||||
func GetResultKeyAccordingToTestResults(policyNs, policy, rule, resourceNs, kind, resource string) string {
|
||||
func GetResultKeyAccordingToTestResults(policyNs, policy, rule, resourceNs, kind, resource string, isVap bool) string {
|
||||
var resultKey string
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
|
||||
if isVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", policy, kind, resource)
|
||||
|
||||
if policyNs != "" && resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNs, policy, rule, resourceNs, kind, resource)
|
||||
} else if policyNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policyNs, policy, rule, kind, resource)
|
||||
} else if resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, resourceNs, kind, resource)
|
||||
if policyNs != "" && resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policyNs, policy, resourceNs, kind, resource)
|
||||
} else if policyNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", policyNs, policy, kind, resource)
|
||||
} else if resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", policy, resourceNs, kind, resource)
|
||||
}
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
|
||||
|
||||
if policyNs != "" && resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNs, policy, rule, resourceNs, kind, resource)
|
||||
} else if policyNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policyNs, policy, rule, kind, resource)
|
||||
} else if resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, resourceNs, kind, resource)
|
||||
}
|
||||
}
|
||||
|
||||
return resultKey
|
||||
}
|
||||
|
||||
|
|
|
@ -195,25 +195,47 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
testCount++
|
||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(resource)
|
||||
var ruleNameInResultKey string
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
ruleNameInResultKey = v.Rule
|
||||
if !v.IsVap {
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
ruleNameInResultKey = v.Rule
|
||||
}
|
||||
}
|
||||
|
||||
var resultKey string
|
||||
if !v.IsVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", v.Policy, v.Kind, resource)
|
||||
}
|
||||
|
||||
resultKey := fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, resource)
|
||||
found, _ := isNamespacedPolicy(v.Policy)
|
||||
var ns string
|
||||
ns, v.Policy = getUserDefinedPolicyNameAndNamespace(v.Policy)
|
||||
if found && v.Namespace != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||
if !v.IsVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, v.Namespace, v.Kind, resource)
|
||||
}
|
||||
} else if found {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, resource)
|
||||
if !v.IsVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, resource)
|
||||
}
|
||||
|
||||
row.Policy = boldFgCyan.Sprint(ns) + "/" + boldFgCyan.Sprint(v.Policy)
|
||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(resource)
|
||||
} else if v.Namespace != "" {
|
||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(resource)
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||
|
||||
if !v.IsVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, v.Namespace, v.Kind, resource)
|
||||
}
|
||||
}
|
||||
|
||||
var testRes policyreportv1alpha2.PolicyReportResult
|
||||
|
@ -258,25 +280,47 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
countDeprecatedResource++
|
||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(v.Resource)
|
||||
var ruleNameInResultKey string
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
ruleNameInResultKey = v.Rule
|
||||
if !v.IsVap {
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
ruleNameInResultKey = v.Rule
|
||||
}
|
||||
}
|
||||
|
||||
var resultKey string
|
||||
if !v.IsVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", v.Policy, v.Kind, v.Resource)
|
||||
}
|
||||
|
||||
resultKey := fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
found, _ := isNamespacedPolicy(v.Policy)
|
||||
var ns string
|
||||
ns, v.Policy = getUserDefinedPolicyNameAndNamespace(v.Policy)
|
||||
if found && v.Namespace != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
if !v.IsVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, v.Namespace, v.Kind, v.Resource)
|
||||
}
|
||||
} else if found {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
if !v.IsVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, v.Resource)
|
||||
}
|
||||
|
||||
row.Policy = boldFgCyan.Sprint(ns) + "/" + boldFgCyan.Sprint(v.Policy)
|
||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(v.Resource)
|
||||
} else if v.Namespace != "" {
|
||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(v.Resource)
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
|
||||
if !v.IsVap {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, v.Namespace, v.Kind, v.Resource)
|
||||
}
|
||||
}
|
||||
|
||||
var testRes policyreportv1alpha2.PolicyReportResult
|
||||
|
|
|
@ -59,7 +59,7 @@ func Test_selectResourcesForCheck(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
// read policies
|
||||
policies, err := common.GetPoliciesFromPaths(
|
||||
policies, validatingAdmissionPolicies, err := common.GetPoliciesFromPaths(
|
||||
fs,
|
||||
[]string{filepath.Join(baseTestDir, values.Policies[0])},
|
||||
false,
|
||||
|
@ -73,6 +73,7 @@ func Test_selectResourcesForCheck(t *testing.T) {
|
|||
[]string{filepath.Join(baseTestDir, values.Resources[0])},
|
||||
false,
|
||||
policies,
|
||||
validatingAdmissionPolicies,
|
||||
nil,
|
||||
"",
|
||||
false,
|
||||
|
|
|
@ -22,15 +22,14 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
engineContext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
|
||||
"github.com/kyverno/kyverno/pkg/logging"
|
||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||
datautils "github.com/kyverno/kyverno/pkg/utils/data"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
@ -83,6 +82,7 @@ type NamespaceSelector struct {
|
|||
|
||||
type ApplyPolicyConfig struct {
|
||||
Policy kyvernov1.PolicyInterface
|
||||
ValidatingAdmissionPolicy v1alpha1.ValidatingAdmissionPolicy
|
||||
Resource *unstructured.Unstructured
|
||||
MutateLogPath string
|
||||
MutateLogPathIsDir bool
|
||||
|
@ -107,7 +107,7 @@ func HasVariables(policy kyvernov1.PolicyInterface) [][]string {
|
|||
}
|
||||
|
||||
// GetPolicies - Extracting the policies from multiple YAML
|
||||
func GetPolicies(paths []string) (policies []kyvernov1.PolicyInterface, errors []error) {
|
||||
func GetPolicies(paths []string) (policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, errors []error) {
|
||||
for _, path := range paths {
|
||||
log.V(5).Info("reading policies", "path", path)
|
||||
|
||||
|
@ -146,9 +146,10 @@ func GetPolicies(paths []string) (policies []kyvernov1.PolicyInterface, errors [
|
|||
}
|
||||
}
|
||||
|
||||
policiesFromDir, errorsFromDir := GetPolicies(listOfFiles)
|
||||
policiesFromDir, admissionPoliciesFromDir, errorsFromDir := GetPolicies(listOfFiles)
|
||||
errors = append(errors, errorsFromDir...)
|
||||
policies = append(policies, policiesFromDir...)
|
||||
validatingAdmissionPolicies = append(validatingAdmissionPolicies, admissionPoliciesFromDir...)
|
||||
} else {
|
||||
var fileBytes []byte
|
||||
if isHTTPPath {
|
||||
|
@ -190,7 +191,7 @@ func GetPolicies(paths []string) (policies []kyvernov1.PolicyInterface, errors [
|
|||
}
|
||||
}
|
||||
|
||||
policiesFromFile, errFromFile := yamlutils.GetPolicy(fileBytes)
|
||||
policiesFromFile, admissionPoliciesFromFile, errFromFile := yamlutils.GetPolicy(fileBytes)
|
||||
if errFromFile != nil {
|
||||
err := fmt.Errorf("failed to process %s: %v", path, errFromFile.Error())
|
||||
errors = append(errors, err)
|
||||
|
@ -198,11 +199,12 @@ func GetPolicies(paths []string) (policies []kyvernov1.PolicyInterface, errors [
|
|||
}
|
||||
|
||||
policies = append(policies, policiesFromFile...)
|
||||
validatingAdmissionPolicies = append(validatingAdmissionPolicies, admissionPoliciesFromFile...)
|
||||
}
|
||||
}
|
||||
|
||||
log.V(3).Info("read policies", "policies", len(policies), "errors", len(errors))
|
||||
return policies, errors
|
||||
return policies, validatingAdmissionPolicies, errors
|
||||
}
|
||||
|
||||
// IsInputFromPipe - check if input is passed using pipe
|
||||
|
@ -370,191 +372,6 @@ func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit
|
|||
return variables, globalValMap, valuesMapResource, namespaceSelectorMap, subresources, nil
|
||||
}
|
||||
|
||||
// ApplyPolicyOnResource - function to apply policy on resource
|
||||
func ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error) {
|
||||
jp := jmespath.New(config.NewDefaultConfiguration(false))
|
||||
|
||||
var engineResponses []engineapi.EngineResponse
|
||||
namespaceLabels := make(map[string]string)
|
||||
operationIsDelete := false
|
||||
|
||||
if c.Variables["request.operation"] == "DELETE" {
|
||||
operationIsDelete = true
|
||||
}
|
||||
|
||||
policyWithNamespaceSelector := false
|
||||
OuterLoop:
|
||||
for _, p := range autogen.ComputeRules(c.Policy) {
|
||||
if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
|
||||
p.ExcludeResources.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break
|
||||
}
|
||||
for _, m := range p.MatchResources.Any {
|
||||
if m.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
for _, m := range p.MatchResources.All {
|
||||
if m.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
for _, e := range p.ExcludeResources.Any {
|
||||
if e.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
for _, e := range p.ExcludeResources.All {
|
||||
if e.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if policyWithNamespaceSelector {
|
||||
resourceNamespace := c.Resource.GetNamespace()
|
||||
namespaceLabels = c.NamespaceSelectorMap[c.Resource.GetNamespace()]
|
||||
if resourceNamespace != "default" && len(namespaceLabels) < 1 {
|
||||
return engineResponses, sanitizederror.NewWithError(fmt.Sprintf("failed to get namespace labels for resource %s. use --values-file flag to pass the namespace labels", c.Resource.GetName()), nil)
|
||||
}
|
||||
}
|
||||
|
||||
resPath := fmt.Sprintf("%s/%s/%s", c.Resource.GetNamespace(), c.Resource.GetKind(), c.Resource.GetName())
|
||||
log.V(3).Info("applying policy on resource", "policy", c.Policy.GetName(), "resource", resPath)
|
||||
|
||||
resourceRaw, err := c.Resource.MarshalJSON()
|
||||
if err != nil {
|
||||
log.Error(err, "failed to marshal resource")
|
||||
}
|
||||
|
||||
updatedResource, err := kubeutils.BytesToUnstructured(resourceRaw)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to convert raw resource to unstructured")
|
||||
}
|
||||
ctx := engineContext.NewContext(jp)
|
||||
|
||||
if operationIsDelete {
|
||||
err = engineContext.AddOldResource(ctx, resourceRaw)
|
||||
} else {
|
||||
err = engineContext.AddResource(ctx, resourceRaw)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err, "failed to load resource in context")
|
||||
}
|
||||
|
||||
for key, value := range c.Variables {
|
||||
err = ctx.AddVariable(key, value)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to add variable to context")
|
||||
}
|
||||
}
|
||||
|
||||
cfg := config.NewDefaultConfiguration(false)
|
||||
if err := ctx.AddImageInfos(c.Resource, cfg); err != nil {
|
||||
log.Error(err, "failed to add image variables to context")
|
||||
}
|
||||
|
||||
gvk, subresource := updatedResource.GroupVersionKind(), ""
|
||||
// If --cluster flag is not set, then we need to find the top level resource GVK and subresource
|
||||
if c.Client == nil {
|
||||
for _, s := range c.Subresources {
|
||||
subgvk := schema.GroupVersionKind{
|
||||
Group: s.APIResource.Group,
|
||||
Version: s.APIResource.Version,
|
||||
Kind: s.APIResource.Kind,
|
||||
}
|
||||
if gvk == subgvk {
|
||||
gvk = schema.GroupVersionKind{
|
||||
Group: s.ParentResource.Group,
|
||||
Version: s.ParentResource.Version,
|
||||
Kind: s.ParentResource.Kind,
|
||||
}
|
||||
parts := strings.Split(s.APIResource.Name, "/")
|
||||
subresource = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
eng := engine.NewEngine(
|
||||
cfg,
|
||||
config.NewDefaultMetricsConfiguration(),
|
||||
jmespath.New(cfg),
|
||||
c.Client,
|
||||
registryclient.NewOrDie(),
|
||||
store.ContextLoaderFactory(nil),
|
||||
nil,
|
||||
)
|
||||
policyContext := engine.NewPolicyContextWithJsonContext(kyvernov1.Create, ctx).
|
||||
WithPolicy(c.Policy).
|
||||
WithNewResource(*updatedResource).
|
||||
WithNamespaceLabels(namespaceLabels).
|
||||
WithAdmissionInfo(c.UserInfo).
|
||||
WithResourceKind(gvk, subresource)
|
||||
|
||||
mutateResponse := eng.Mutate(context.Background(), policyContext)
|
||||
engineResponses = append(engineResponses, mutateResponse)
|
||||
|
||||
err = processMutateEngineResponse(c, &mutateResponse, resPath)
|
||||
if err != nil {
|
||||
if !sanitizederror.IsErrorSanitized(err) {
|
||||
return engineResponses, sanitizederror.NewWithError("failed to print mutated result", err)
|
||||
}
|
||||
}
|
||||
|
||||
var policyHasValidate bool
|
||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
||||
if rule.HasValidate() || rule.HasVerifyImageChecks() {
|
||||
policyHasValidate = true
|
||||
}
|
||||
}
|
||||
|
||||
policyContext = policyContext.WithNewResource(mutateResponse.PatchedResource)
|
||||
|
||||
var validateResponse engineapi.EngineResponse
|
||||
if policyHasValidate {
|
||||
validateResponse = eng.Validate(context.Background(), policyContext)
|
||||
ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
|
||||
}
|
||||
|
||||
if !validateResponse.IsEmpty() {
|
||||
engineResponses = append(engineResponses, validateResponse)
|
||||
}
|
||||
|
||||
verifyImageResponse, _ := eng.VerifyAndPatchImages(context.TODO(), policyContext)
|
||||
if !verifyImageResponse.IsEmpty() {
|
||||
engineResponses = append(engineResponses, verifyImageResponse)
|
||||
ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
|
||||
}
|
||||
|
||||
var policyHasGenerate bool
|
||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
||||
if rule.HasGenerate() {
|
||||
policyHasGenerate = true
|
||||
}
|
||||
}
|
||||
|
||||
if policyHasGenerate {
|
||||
generateResponse := eng.ApplyBackgroundChecks(context.TODO(), policyContext)
|
||||
if !generateResponse.IsEmpty() {
|
||||
newRuleResponse, err := handleGeneratePolicy(&generateResponse, *policyContext, c.RuleToCloneSourceResource)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to apply generate policy")
|
||||
} else {
|
||||
generateResponse.PolicyResponse.Rules = newRuleResponse
|
||||
}
|
||||
engineResponses = append(engineResponses, generateResponse)
|
||||
}
|
||||
updateResultCounts(c.Policy, &generateResponse, resPath, c.Rc, c.AuditWarn)
|
||||
}
|
||||
|
||||
return engineResponses, nil
|
||||
}
|
||||
|
||||
func ProcessValidateEngineResponse(policy kyvernov1.PolicyInterface, validateResponse engineapi.EngineResponse, resPath string, rc *ResultCounts, policyReport bool, auditWarn bool) {
|
||||
printCount := 0
|
||||
for _, policyRule := range autogen.ComputeRules(policy) {
|
||||
|
@ -641,7 +458,7 @@ func PrintMutatedOutput(mutateLogPath string, mutateLogPathIsDir bool, yaml stri
|
|||
}
|
||||
|
||||
// GetPoliciesFromPaths - get policies according to the resource path
|
||||
func GetPoliciesFromPaths(fs billy.Filesystem, dirPath []string, isGit bool, policyResourcePath string) (policies []kyvernov1.PolicyInterface, err error) {
|
||||
func GetPoliciesFromPaths(fs billy.Filesystem, dirPath []string, isGit bool, policyResourcePath string) (policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, err error) {
|
||||
if isGit {
|
||||
for _, pp := range dirPath {
|
||||
filep, err := fs.Open(filepath.Join(policyResourcePath, pp))
|
||||
|
@ -659,12 +476,13 @@ func GetPoliciesFromPaths(fs billy.Filesystem, dirPath []string, isGit bool, pol
|
|||
fmt.Printf("failed to convert to JSON: %v", err)
|
||||
continue
|
||||
}
|
||||
policiesFromFile, errFromFile := yamlutils.GetPolicy(policyBytes)
|
||||
policiesFromFile, admissionPoliciesFromFile, errFromFile := yamlutils.GetPolicy(policyBytes)
|
||||
if errFromFile != nil {
|
||||
fmt.Printf("failed to process : %v", errFromFile.Error())
|
||||
continue
|
||||
}
|
||||
policies = append(policies, policiesFromFile...)
|
||||
validatingAdmissionPolicies = append(validatingAdmissionPolicies, admissionPoliciesFromFile...)
|
||||
}
|
||||
} else {
|
||||
if len(dirPath) > 0 && dirPath[0] == "-" {
|
||||
|
@ -675,19 +493,19 @@ func GetPoliciesFromPaths(fs billy.Filesystem, dirPath []string, isGit bool, pol
|
|||
policyStr = policyStr + scanner.Text() + "\n"
|
||||
}
|
||||
yamlBytes := []byte(policyStr)
|
||||
policies, err = yamlutils.GetPolicy(yamlBytes)
|
||||
policies, validatingAdmissionPolicies, err = yamlutils.GetPolicy(yamlBytes)
|
||||
if err != nil {
|
||||
return nil, sanitizederror.NewWithError("failed to extract the resources", err)
|
||||
return nil, nil, sanitizederror.NewWithError("failed to extract the resources", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var errors []error
|
||||
policies, errors = GetPolicies(dirPath)
|
||||
if len(policies) == 0 {
|
||||
policies, validatingAdmissionPolicies, errors = GetPolicies(dirPath)
|
||||
if len(policies) == 0 && len(validatingAdmissionPolicies) == 0 {
|
||||
if len(errors) > 0 {
|
||||
return nil, sanitizederror.NewWithErrors("failed to read file", errors)
|
||||
return nil, nil, sanitizederror.NewWithErrors("failed to read file", errors)
|
||||
}
|
||||
return nil, sanitizederror.New(fmt.Sprintf("no file found in paths %v", dirPath))
|
||||
return nil, nil, sanitizederror.New(fmt.Sprintf("no file found in paths %v", dirPath))
|
||||
}
|
||||
if len(errors) > 0 && log.V(1).Enabled() {
|
||||
fmt.Printf("ignoring errors: \n")
|
||||
|
@ -702,7 +520,7 @@ func GetPoliciesFromPaths(fs billy.Filesystem, dirPath []string, isGit bool, pol
|
|||
|
||||
// GetResourceAccordingToResourcePath - get resources according to the resource path
|
||||
func GetResourceAccordingToResourcePath(fs billy.Filesystem, resourcePaths []string,
|
||||
cluster bool, policies []kyvernov1.PolicyInterface, dClient dclient.Interface, namespace string, policyReport bool, isGit bool, policyResourcePath string,
|
||||
cluster bool, policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, dClient dclient.Interface, namespace string, policyReport bool, isGit bool, policyResourcePath string,
|
||||
) (resources []*unstructured.Unstructured, err error) {
|
||||
if isGit {
|
||||
resources, err = GetResourcesWithTest(fs, policies, resourcePaths, isGit, policyResourcePath)
|
||||
|
@ -746,7 +564,7 @@ func GetResourceAccordingToResourcePath(fs billy.Filesystem, resourcePaths []str
|
|||
}
|
||||
}
|
||||
|
||||
resources, err = GetResources(policies, resourcePaths, dClient, cluster, namespace, policyReport)
|
||||
resources, err = GetResources(policies, validatingAdmissionPolicies, resourcePaths, dClient, cluster, namespace, policyReport)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
}
|
||||
|
|
|
@ -96,8 +96,9 @@ func Test_NamespaceSelector(t *testing.T) {
|
|||
},
|
||||
}
|
||||
rc := &ResultCounts{}
|
||||
kyvernoPolicy := KyvernoPolicies{}
|
||||
for _, tc := range testcases {
|
||||
policyArray, _ := yamlutils.GetPolicy(tc.policy)
|
||||
policyArray, _, _ := yamlutils.GetPolicy(tc.policy)
|
||||
resourceArray, _ := GetResource(tc.resource)
|
||||
applyPolicyConfig := ApplyPolicyConfig{
|
||||
Policy: policyArray[0],
|
||||
|
@ -107,7 +108,7 @@ func Test_NamespaceSelector(t *testing.T) {
|
|||
NamespaceSelectorMap: tc.namespaceSelectorMap,
|
||||
Rc: rc,
|
||||
}
|
||||
ApplyPolicyOnResource(applyPolicyConfig)
|
||||
kyvernoPolicy.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
assert.Equal(t, int64(rc.Pass), int64(tc.result.Pass))
|
||||
assert.Equal(t, int64(rc.Fail), int64(tc.result.Fail))
|
||||
// TODO: autogen rules seem to not be present when autogen internals is disabled
|
||||
|
|
|
@ -16,6 +16,10 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -28,34 +32,33 @@ import (
|
|||
// - local paths to resources, if given
|
||||
// - the k8s cluster, if given
|
||||
func GetResources(
|
||||
policies []kyvernov1.PolicyInterface, resourcePaths []string, dClient dclient.Interface, cluster bool,
|
||||
policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resourcePaths []string, dClient dclient.Interface, cluster bool,
|
||||
namespace string, policyReport bool,
|
||||
) ([]*unstructured.Unstructured, error) {
|
||||
resources := make([]*unstructured.Unstructured, 0)
|
||||
var err error
|
||||
|
||||
if cluster && dClient != nil {
|
||||
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
|
||||
var resourceTypes []schema.GroupVersionKind
|
||||
var subresourceMap map[schema.GroupVersionKind]Subresource
|
||||
if len(policies) > 0 {
|
||||
matchedResources := &KyvernoResources{
|
||||
policies: policies,
|
||||
}
|
||||
|
||||
for _, policy := range policies {
|
||||
for _, rule := range autogen.ComputeRules(policy) {
|
||||
var resourceTypesInRule map[schema.GroupVersionKind]bool
|
||||
resourceTypesInRule, subresourceMap = GetKindsFromRule(rule, dClient)
|
||||
for resourceKind := range resourceTypesInRule {
|
||||
resourceTypesMap[resourceKind] = true
|
||||
}
|
||||
resources, err = matchedResources.FetchResourcesFromPolicy(resourcePaths, dClient, namespace, policyReport)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
}
|
||||
}
|
||||
|
||||
for kind := range resourceTypesMap {
|
||||
resourceTypes = append(resourceTypes, kind)
|
||||
}
|
||||
if len(validatingAdmissionPolicies) > 0 {
|
||||
matchedResources := &ValidatingAdmissionResources{
|
||||
policies: validatingAdmissionPolicies,
|
||||
}
|
||||
|
||||
resources, err = whenClusterIsTrue(resourceTypes, subresourceMap, dClient, namespace, resourcePaths, policyReport)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
resources, err = matchedResources.FetchResourcesFromPolicy(resourcePaths, dClient, namespace, policyReport)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
}
|
||||
}
|
||||
} else if len(resourcePaths) > 0 {
|
||||
resources, err = whenClusterIsFalse(resourcePaths, policyReport)
|
||||
|
@ -340,6 +343,64 @@ func GetKindsFromRule(rule kyvernov1.Rule, client dclient.Interface) (map[schema
|
|||
return resourceTypesMap, subresourceMap
|
||||
}
|
||||
|
||||
func getKindsFromValidatingAdmissionRule(rule admissionregistrationv1.Rule, client dclient.Interface) (map[schema.GroupVersionKind]bool, map[schema.GroupVersionKind]Subresource, error) {
|
||||
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
|
||||
subresourceMap := make(map[schema.GroupVersionKind]Subresource)
|
||||
|
||||
group := rule.APIGroups[0]
|
||||
if group == "" {
|
||||
group = "*"
|
||||
}
|
||||
version := rule.APIVersions[0]
|
||||
|
||||
for _, resource := range rule.Resources {
|
||||
var kind, subresource string
|
||||
|
||||
isSubresource := kubeutils.IsSubresource(resource)
|
||||
if isSubresource {
|
||||
parts := strings.Split(resource, "/")
|
||||
kind = cases.Title(language.English, cases.NoLower).String(parts[0])
|
||||
kind, _ = strings.CutSuffix(kind, "s")
|
||||
|
||||
subresource = parts[1]
|
||||
} else {
|
||||
resource = cases.Title(language.English, cases.NoLower).String(resource)
|
||||
resource, _ = strings.CutSuffix(resource, "s")
|
||||
|
||||
kind = resource
|
||||
subresource = ""
|
||||
}
|
||||
|
||||
gvrss, err := client.Discovery().FindResources(group, version, kind, subresource)
|
||||
if err != nil {
|
||||
log.Info("failed to find resource", "kind", kind, "error", err)
|
||||
return resourceTypesMap, subresourceMap, err
|
||||
}
|
||||
|
||||
for parent, child := range gvrss {
|
||||
// The resource is not a subresource
|
||||
if parent.SubResource == "" {
|
||||
resourceTypesMap[parent.GroupVersionKind()] = true
|
||||
} else {
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: child.Group, Version: child.Version, Kind: child.Kind,
|
||||
}
|
||||
subresourceMap[gvk] = Subresource{
|
||||
APIResource: child,
|
||||
ParentResource: metav1.APIResource{
|
||||
Group: parent.Group,
|
||||
Version: parent.Version,
|
||||
Kind: parent.Kind,
|
||||
Name: parent.Resource,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourceTypesMap, subresourceMap, nil
|
||||
}
|
||||
|
||||
func addGVKToResourceTypesMap(kind string, resourceTypesMap map[schema.GroupVersionKind]bool, subresourceMap map[schema.GroupVersionKind]Subresource, client dclient.Interface) {
|
||||
group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
|
||||
gvrss, err := client.Discovery().FindResources(group, version, kind, subresource)
|
||||
|
|
206
cmd/cli/kubectl-kyverno/utils/common/kyverno_policies_types.go
Normal file
206
cmd/cli/kubectl-kyverno/utils/common/kyverno_policies_types.go
Normal file
|
@ -0,0 +1,206 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
|
||||
"github.com/kyverno/kyverno/pkg/autogen"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
engineContext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type KyvernoPolicies struct{}
|
||||
|
||||
func (p *KyvernoPolicies) ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error) {
|
||||
jp := jmespath.New(config.NewDefaultConfiguration(false))
|
||||
|
||||
var engineResponses []engineapi.EngineResponse
|
||||
namespaceLabels := make(map[string]string)
|
||||
operationIsDelete := false
|
||||
|
||||
if c.Variables["request.operation"] == "DELETE" {
|
||||
operationIsDelete = true
|
||||
}
|
||||
|
||||
policyWithNamespaceSelector := false
|
||||
OuterLoop:
|
||||
for _, p := range autogen.ComputeRules(c.Policy) {
|
||||
if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
|
||||
p.ExcludeResources.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break
|
||||
}
|
||||
for _, m := range p.MatchResources.Any {
|
||||
if m.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
for _, m := range p.MatchResources.All {
|
||||
if m.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
for _, e := range p.ExcludeResources.Any {
|
||||
if e.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
for _, e := range p.ExcludeResources.All {
|
||||
if e.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if policyWithNamespaceSelector {
|
||||
resourceNamespace := c.Resource.GetNamespace()
|
||||
namespaceLabels = c.NamespaceSelectorMap[c.Resource.GetNamespace()]
|
||||
if resourceNamespace != "default" && len(namespaceLabels) < 1 {
|
||||
return engineResponses, sanitizederror.NewWithError(fmt.Sprintf("failed to get namespace labels for resource %s. use --values-file flag to pass the namespace labels", c.Resource.GetName()), nil)
|
||||
}
|
||||
}
|
||||
|
||||
resPath := fmt.Sprintf("%s/%s/%s", c.Resource.GetNamespace(), c.Resource.GetKind(), c.Resource.GetName())
|
||||
log.V(3).Info("applying policy on resource", "policy", c.Policy.GetName(), "resource", resPath)
|
||||
|
||||
resourceRaw, err := c.Resource.MarshalJSON()
|
||||
if err != nil {
|
||||
log.Error(err, "failed to marshal resource")
|
||||
}
|
||||
|
||||
updatedResource, err := kubeutils.BytesToUnstructured(resourceRaw)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to convert raw resource to unstructured")
|
||||
}
|
||||
ctx := engineContext.NewContext(jp)
|
||||
|
||||
if operationIsDelete {
|
||||
err = engineContext.AddOldResource(ctx, resourceRaw)
|
||||
} else {
|
||||
err = engineContext.AddResource(ctx, resourceRaw)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err, "failed to load resource in context")
|
||||
}
|
||||
|
||||
for key, value := range c.Variables {
|
||||
err = ctx.AddVariable(key, value)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to add variable to context")
|
||||
}
|
||||
}
|
||||
|
||||
cfg := config.NewDefaultConfiguration(false)
|
||||
if err := ctx.AddImageInfos(c.Resource, cfg); err != nil {
|
||||
log.Error(err, "failed to add image variables to context")
|
||||
}
|
||||
|
||||
gvk, subresource := updatedResource.GroupVersionKind(), ""
|
||||
// If --cluster flag is not set, then we need to find the top level resource GVK and subresource
|
||||
if c.Client == nil {
|
||||
for _, s := range c.Subresources {
|
||||
subgvk := schema.GroupVersionKind{
|
||||
Group: s.APIResource.Group,
|
||||
Version: s.APIResource.Version,
|
||||
Kind: s.APIResource.Kind,
|
||||
}
|
||||
if gvk == subgvk {
|
||||
gvk = schema.GroupVersionKind{
|
||||
Group: s.ParentResource.Group,
|
||||
Version: s.ParentResource.Version,
|
||||
Kind: s.ParentResource.Kind,
|
||||
}
|
||||
parts := strings.Split(s.APIResource.Name, "/")
|
||||
subresource = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
eng := engine.NewEngine(
|
||||
cfg,
|
||||
config.NewDefaultMetricsConfiguration(),
|
||||
jmespath.New(cfg),
|
||||
c.Client,
|
||||
registryclient.NewOrDie(),
|
||||
store.ContextLoaderFactory(nil),
|
||||
nil,
|
||||
)
|
||||
policyContext := engine.NewPolicyContextWithJsonContext(kyvernov1.Create, ctx).
|
||||
WithPolicy(c.Policy).
|
||||
WithNewResource(*updatedResource).
|
||||
WithNamespaceLabels(namespaceLabels).
|
||||
WithAdmissionInfo(c.UserInfo).
|
||||
WithResourceKind(gvk, subresource)
|
||||
|
||||
mutateResponse := eng.Mutate(context.Background(), policyContext)
|
||||
engineResponses = append(engineResponses, mutateResponse)
|
||||
|
||||
err = processMutateEngineResponse(c, &mutateResponse, resPath)
|
||||
if err != nil {
|
||||
if !sanitizederror.IsErrorSanitized(err) {
|
||||
return engineResponses, sanitizederror.NewWithError("failed to print mutated result", err)
|
||||
}
|
||||
}
|
||||
|
||||
var policyHasValidate bool
|
||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
||||
if rule.HasValidate() || rule.HasVerifyImageChecks() {
|
||||
policyHasValidate = true
|
||||
}
|
||||
}
|
||||
|
||||
policyContext = policyContext.WithNewResource(mutateResponse.PatchedResource)
|
||||
|
||||
var validateResponse engineapi.EngineResponse
|
||||
if policyHasValidate {
|
||||
validateResponse = eng.Validate(context.Background(), policyContext)
|
||||
ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
|
||||
}
|
||||
|
||||
if !validateResponse.IsEmpty() {
|
||||
engineResponses = append(engineResponses, validateResponse)
|
||||
}
|
||||
|
||||
verifyImageResponse, _ := eng.VerifyAndPatchImages(context.TODO(), policyContext)
|
||||
if !verifyImageResponse.IsEmpty() {
|
||||
engineResponses = append(engineResponses, verifyImageResponse)
|
||||
ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
|
||||
}
|
||||
|
||||
var policyHasGenerate bool
|
||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
||||
if rule.HasGenerate() {
|
||||
policyHasGenerate = true
|
||||
}
|
||||
}
|
||||
|
||||
if policyHasGenerate {
|
||||
generateResponse := eng.ApplyBackgroundChecks(context.TODO(), policyContext)
|
||||
if !generateResponse.IsEmpty() {
|
||||
newRuleResponse, err := handleGeneratePolicy(&generateResponse, *policyContext, c.RuleToCloneSourceResource)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to apply generate policy")
|
||||
} else {
|
||||
generateResponse.PolicyResponse.Rules = newRuleResponse
|
||||
}
|
||||
engineResponses = append(engineResponses, generateResponse)
|
||||
}
|
||||
updateResultCounts(c.Policy, &generateResponse, resPath, c.Rc, c.AuditWarn)
|
||||
}
|
||||
|
||||
return engineResponses, nil
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/autogen"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type KyvernoResources struct {
|
||||
policies []kyvernov1.PolicyInterface
|
||||
}
|
||||
|
||||
func (r *KyvernoResources) FetchResourcesFromPolicy(resourcePaths []string, dClient dclient.Interface, namespace string, policyReport bool) ([]*unstructured.Unstructured, error) {
|
||||
var resources []*unstructured.Unstructured
|
||||
var err error
|
||||
|
||||
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
|
||||
var resourceTypes []schema.GroupVersionKind
|
||||
var subresourceMap map[schema.GroupVersionKind]Subresource
|
||||
|
||||
for _, policy := range r.policies {
|
||||
for _, rule := range autogen.ComputeRules(policy) {
|
||||
var resourceTypesInRule map[schema.GroupVersionKind]bool
|
||||
resourceTypesInRule, subresourceMap = GetKindsFromRule(rule, dClient)
|
||||
for resourceKind := range resourceTypesInRule {
|
||||
resourceTypesMap[resourceKind] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for kind := range resourceTypesMap {
|
||||
resourceTypes = append(resourceTypes, kind)
|
||||
}
|
||||
|
||||
resources, err = whenClusterIsTrue(resourceTypes, subresourceMap, dClient, namespace, resourcePaths, policyReport)
|
||||
|
||||
return resources, err
|
||||
}
|
9
cmd/cli/kubectl-kyverno/utils/common/policy_interface.go
Normal file
9
cmd/cli/kubectl-kyverno/utils/common/policy_interface.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
)
|
||||
|
||||
type PolicyInterface interface {
|
||||
ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error)
|
||||
}
|
11
cmd/cli/kubectl-kyverno/utils/common/resource_interface.go
Normal file
11
cmd/cli/kubectl-kyverno/utils/common/resource_interface.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// ResourceInterface abstracts the matched resources by either Kyverno Policies or Validating Admission Policies
|
||||
type ResourceInterface interface {
|
||||
FetchResourcesFromPolicy(resourcePaths []string, dClient dclient.Interface, namespace string, policyReport bool) ([]*unstructured.Unstructured, error)
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/cel"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
|
||||
celconfig "k8s.io/apiserver/pkg/apis/cel"
|
||||
)
|
||||
|
||||
type ValidatingAdmissionPolicies struct{}
|
||||
|
||||
func (p *ValidatingAdmissionPolicies) ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error) {
|
||||
var engineResponses []engineapi.EngineResponse
|
||||
|
||||
resPath := fmt.Sprintf("%s/%s/%s", c.Resource.GetNamespace(), c.Resource.GetKind(), c.Resource.GetName())
|
||||
log.V(3).Info("applying policy on resource", "policy", c.ValidatingAdmissionPolicy.GetName(), "resource", resPath)
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
validations := c.ValidatingAdmissionPolicy.Spec.Validations
|
||||
var expressions, messageExpressions []cel.ExpressionAccessor
|
||||
|
||||
for _, validation := range validations {
|
||||
var reason metav1.StatusReason
|
||||
if validation.Reason == nil {
|
||||
reason = metav1.StatusReasonInvalid
|
||||
} else {
|
||||
reason = *validation.Reason
|
||||
}
|
||||
|
||||
var message string
|
||||
if validation.Message != "" && validation.MessageExpression != "" {
|
||||
message = validation.MessageExpression
|
||||
} else if validation.Message != "" {
|
||||
message = validation.Message
|
||||
} else if validation.MessageExpression != "" {
|
||||
message = validation.MessageExpression
|
||||
} else {
|
||||
message = fmt.Sprintf("error: failed to create %s: %s \"%s\" is forbidden: ValidatingAdmissionPolicy '%s' denied request: failed expression: %s", c.Resource.GetKind(), c.Resource.GetAPIVersion(), c.Resource.GetName(), c.ValidatingAdmissionPolicy.Name, validation.Expression)
|
||||
}
|
||||
|
||||
condition := &validatingadmissionpolicy.ValidationCondition{
|
||||
Expression: validation.Expression,
|
||||
Message: message,
|
||||
Reason: &reason,
|
||||
}
|
||||
|
||||
messageCondition := &validatingadmissionpolicy.MessageExpressionCondition{
|
||||
MessageExpression: validation.MessageExpression,
|
||||
}
|
||||
|
||||
expressions = append(expressions, condition)
|
||||
messageExpressions = append(messageExpressions, messageCondition)
|
||||
}
|
||||
|
||||
hasParams := c.ValidatingAdmissionPolicy.Spec.ParamKind != nil
|
||||
|
||||
filterCompiler := cel.NewFilterCompiler()
|
||||
filter := filterCompiler.Compile(expressions, cel.OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: false}, celconfig.PerCallLimit)
|
||||
messageExpressionfilter := filterCompiler.Compile(messageExpressions, cel.OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: false}, celconfig.PerCallLimit)
|
||||
|
||||
admissionAttributes := admission.NewAttributesRecord(c.Resource.DeepCopyObject(), nil, c.Resource.GroupVersionKind(), c.Resource.GetNamespace(), c.Resource.GetName(), schema.GroupVersionResource{}, "", admission.Create, nil, false, nil)
|
||||
versionedAttr, _ := admission.NewVersionedAttributes(admissionAttributes, admissionAttributes.GetKind(), nil)
|
||||
|
||||
ctx := context.TODO()
|
||||
failPolicy := admissionregistrationv1.FailurePolicyType(*c.ValidatingAdmissionPolicy.Spec.FailurePolicy)
|
||||
|
||||
matchConditions := c.ValidatingAdmissionPolicy.Spec.MatchConditions
|
||||
var matchExpressions []cel.ExpressionAccessor
|
||||
|
||||
for _, expression := range matchConditions {
|
||||
condition := &matchconditions.MatchCondition{
|
||||
Name: expression.Name,
|
||||
Expression: expression.Expression,
|
||||
}
|
||||
matchExpressions = append(matchExpressions, condition)
|
||||
}
|
||||
|
||||
matchFilter := filterCompiler.Compile(matchExpressions, cel.OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: false}, celconfig.PerCallLimit)
|
||||
|
||||
var matchPolicy *v1alpha1.MatchPolicyType
|
||||
if c.ValidatingAdmissionPolicy.Spec.MatchConstraints.MatchPolicy == nil {
|
||||
equivalent := v1alpha1.Equivalent
|
||||
matchPolicy = &equivalent
|
||||
} else {
|
||||
matchPolicy = c.ValidatingAdmissionPolicy.Spec.MatchConstraints.MatchPolicy
|
||||
}
|
||||
|
||||
newMatcher := matchconditions.NewMatcher(matchFilter, nil, &failPolicy, string(*matchPolicy), "")
|
||||
|
||||
auditAnnotations := c.ValidatingAdmissionPolicy.Spec.AuditAnnotations
|
||||
var auditExpressions []cel.ExpressionAccessor
|
||||
|
||||
for _, expression := range auditAnnotations {
|
||||
condition := &validatingadmissionpolicy.AuditAnnotationCondition{
|
||||
Key: expression.Key,
|
||||
ValueExpression: expression.ValueExpression,
|
||||
}
|
||||
auditExpressions = append(auditExpressions, condition)
|
||||
}
|
||||
auditAnnotationFilter := filterCompiler.Compile(auditExpressions, cel.OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: false}, celconfig.PerCallLimit)
|
||||
|
||||
validator := validatingadmissionpolicy.NewValidator(filter, newMatcher, auditAnnotationFilter, messageExpressionfilter, &failPolicy, nil)
|
||||
validateResult := validator.Validate(ctx, versionedAttr, nil, celconfig.RuntimeCELCostBudget)
|
||||
|
||||
engineResponse := engineapi.NewEngineResponseWithValidatingAdmissionPolicy(*c.Resource, c.ValidatingAdmissionPolicy, nil)
|
||||
policyResp := engineapi.NewPolicyResponse()
|
||||
var ruleResp *engineapi.RuleResponse
|
||||
isPass := true
|
||||
|
||||
for _, policyDecision := range validateResult.Decisions {
|
||||
if policyDecision.Evaluation == validatingadmissionpolicy.EvalError {
|
||||
isPass = false
|
||||
c.Rc.Error++
|
||||
ruleResp = engineapi.RuleError(c.ValidatingAdmissionPolicy.GetName(), engineapi.Validation, policyDecision.Message, nil)
|
||||
break
|
||||
} else if policyDecision.Action == validatingadmissionpolicy.ActionDeny {
|
||||
isPass = false
|
||||
c.Rc.Fail++
|
||||
ruleResp = engineapi.RuleFail(c.ValidatingAdmissionPolicy.GetName(), engineapi.Validation, policyDecision.Message)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isPass {
|
||||
c.Rc.Pass++
|
||||
ruleResp = engineapi.RulePass(c.ValidatingAdmissionPolicy.GetName(), engineapi.Validation, "")
|
||||
}
|
||||
|
||||
policyResp.Add(engineapi.NewExecutionStats(startTime, time.Now()), *ruleResp)
|
||||
|
||||
engineResponse = engineResponse.WithPolicyResponse(policyResp)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
|
||||
return engineResponses, nil
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type ValidatingAdmissionResources struct {
|
||||
policies []v1alpha1.ValidatingAdmissionPolicy
|
||||
}
|
||||
|
||||
func (r *ValidatingAdmissionResources) FetchResourcesFromPolicy(resourcePaths []string, dClient dclient.Interface, namespace string, policyReport bool) ([]*unstructured.Unstructured, error) {
|
||||
var resources []*unstructured.Unstructured
|
||||
var err error
|
||||
|
||||
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
|
||||
var resourceTypes []schema.GroupVersionKind
|
||||
var subresourceMap map[schema.GroupVersionKind]Subresource
|
||||
|
||||
for _, policy := range r.policies {
|
||||
for _, rule := range policy.Spec.MatchConstraints.ResourceRules {
|
||||
var resourceTypesInRule map[schema.GroupVersionKind]bool
|
||||
resourceTypesInRule, subresourceMap, err = getKindsFromValidatingAdmissionRule(rule.RuleWithOperations.Rule, dClient)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
}
|
||||
for resourceKind := range resourceTypesInRule {
|
||||
resourceTypesMap[resourceKind] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for kind := range resourceTypesMap {
|
||||
resourceTypes = append(resourceTypes, kind)
|
||||
}
|
||||
|
||||
resources, err = whenClusterIsTrue(resourceTypes, subresourceMap, dClient, namespace, resourcePaths, policyReport)
|
||||
return resources, err
|
||||
}
|
6
go.mod
6
go.mod
|
@ -59,6 +59,7 @@ require (
|
|||
go.uber.org/zap v1.24.0
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/text v0.9.0
|
||||
google.golang.org/grpc v1.55.0
|
||||
gopkg.in/inf.v0 v0.9.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
|
@ -67,6 +68,7 @@ require (
|
|||
k8s.io/api v0.27.1
|
||||
k8s.io/apiextensions-apiserver v0.27.1
|
||||
k8s.io/apimachinery v0.27.1
|
||||
k8s.io/apiserver v0.27.1
|
||||
k8s.io/cli-runtime v0.27.1
|
||||
k8s.io/client-go v0.27.1
|
||||
k8s.io/klog/v2 v2.100.1
|
||||
|
@ -117,6 +119,7 @@ require (
|
|||
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
||||
github.com/aliyun/credentials-go v1.2.7 // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18 // indirect
|
||||
|
@ -189,6 +192,7 @@ require (
|
|||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/cel-go v0.12.6 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.4 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-github/v45 v45.2.0 // indirect
|
||||
|
@ -276,6 +280,7 @@ require (
|
|||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.15.0 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.1.3 // indirect
|
||||
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
|
@ -309,7 +314,6 @@ require (
|
|||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/term v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
google.golang.org/api v0.115.0 // indirect
|
||||
|
|
8
go.sum
8
go.sum
|
@ -185,6 +185,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
|||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
|
||||
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
|
@ -648,6 +650,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
|||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M=
|
||||
github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs=
|
||||
github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY=
|
||||
|
@ -1318,6 +1322,7 @@ github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jH
|
|||
github.com/spiffe/go-spiffe/v2 v2.1.3 h1:P5L9Ixo5eqJiHnktAU0UD/6UfHsQs7yAtc8a/FFUi9M=
|
||||
github.com/spiffe/go-spiffe/v2 v2.1.3/go.mod h1:eVDqm9xFvyqao6C+eQensb9ZPkyNEeaUbqbBpOhBnNk=
|
||||
github.com/ssgreg/nlreturn/v2 v2.1.0/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
|
@ -2152,6 +2157,8 @@ k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc=
|
|||
k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM=
|
||||
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
|
||||
k8s.io/apiserver v0.20.2/go.mod h1:2nKd93WyMhZx4Hp3RfgH2K5PhwyTrprrkWYnI7id7jA=
|
||||
k8s.io/apiserver v0.27.1 h1:phY+BtXjjzd+ta3a4kYbomC81azQSLa1K8jo9RBw7Lg=
|
||||
k8s.io/apiserver v0.27.1/go.mod h1:UGrOjLY2KsieA9Fw6lLiTObxTb8Z1xEba4uqSuMY0WU=
|
||||
k8s.io/cli-runtime v0.27.1 h1:MMzp5Q/Xmr5L1Lrowuc+Y/r95XINC6c6/fE3aN7JDRM=
|
||||
k8s.io/cli-runtime v0.27.1/go.mod h1:tEbTB1XP/nTH3wujsi52bw91gWpErtWiS15R6CwYsAI=
|
||||
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
|
||||
|
@ -2197,6 +2204,7 @@ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
|||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.1 h1:MB1zkK+WMOmfLxEpjr1wEmkpcIhZC7kfTkZ0stg5bog=
|
||||
sigs.k8s.io/controller-runtime v0.8.2/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU=
|
||||
sigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU=
|
||||
sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA=
|
||||
|
|
|
@ -318,7 +318,7 @@ func Test_Any(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ func Test_All(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ func Test_Exclude(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -429,7 +429,7 @@ func Test_CronJobOnly(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ func Test_ForEachPod(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -494,7 +494,7 @@ func Test_CronJob_hasExclude(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -531,7 +531,7 @@ func Test_CronJobAndDeployment(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -603,7 +603,7 @@ func Test_UpdateVariablePath(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -633,7 +633,7 @@ func Test_Deny(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
policies, err := yamlutils.GetPolicy(file)
|
||||
policies, _, err := yamlutils.GetPolicy(file)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -841,7 +841,7 @@ kA==
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
policies, err := yamlutils.GetPolicy([]byte(test.policy))
|
||||
policies, _, err := yamlutils.GetPolicy([]byte(test.policy))
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, 1, len(policies))
|
||||
rules := computeRules(policies[0])
|
||||
|
@ -852,7 +852,7 @@ kA==
|
|||
|
||||
func Test_PodSecurityWithNoExceptions(t *testing.T) {
|
||||
policy := []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"pod-security"},"spec":{"validationFailureAction":"enforce","rules":[{"name":"restricted","match":{"all":[{"resources":{"kinds":["Pod"]}}]},"validate":{"podSecurity":{"level":"restricted","version":"v1.24"}}}]}}`)
|
||||
policies, err := yamlutils.GetPolicy([]byte(policy))
|
||||
policies, _, err := yamlutils.GetPolicy([]byte(policy))
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, 1, len(policies))
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
datautils "github.com/kyverno/kyverno/pkg/utils/data"
|
||||
utils "github.com/kyverno/kyverno/pkg/utils/match"
|
||||
"github.com/kyverno/kyverno/pkg/utils/wildcard"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
|
@ -14,8 +15,10 @@ import (
|
|||
type EngineResponse struct {
|
||||
// Resource is the original resource
|
||||
Resource unstructured.Unstructured
|
||||
// policy is the original policy
|
||||
// Policy is the original policy
|
||||
policy kyvernov1.PolicyInterface
|
||||
// Policy is the validating admission policy
|
||||
validatingAdmissionPolicy v1alpha1.ValidatingAdmissionPolicy
|
||||
// namespaceLabels given by policy context
|
||||
namespaceLabels map[string]string
|
||||
// PatchedResource is the resource patched with the engine action changes
|
||||
|
@ -75,6 +78,19 @@ func (er EngineResponse) WithPatchedResource(patchedResource unstructured.Unstru
|
|||
return er
|
||||
}
|
||||
|
||||
func NewEngineResponseWithValidatingAdmissionPolicy(
|
||||
resource unstructured.Unstructured,
|
||||
policy v1alpha1.ValidatingAdmissionPolicy,
|
||||
namespaceLabels map[string]string,
|
||||
) EngineResponse {
|
||||
response := EngineResponse{
|
||||
Resource: resource,
|
||||
validatingAdmissionPolicy: policy,
|
||||
namespaceLabels: namespaceLabels,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func (er EngineResponse) WithNamespaceLabels(namespaceLabels map[string]string) EngineResponse {
|
||||
er.namespaceLabels = namespaceLabels
|
||||
return er
|
||||
|
@ -88,6 +104,10 @@ func (er *EngineResponse) Policy() kyvernov1.PolicyInterface {
|
|||
return er.policy
|
||||
}
|
||||
|
||||
func (er *EngineResponse) ValidatingAdmissionPolicy() v1alpha1.ValidatingAdmissionPolicy {
|
||||
return er.validatingAdmissionPolicy
|
||||
}
|
||||
|
||||
// IsOneOf checks if any rule has status in a given list
|
||||
func (er EngineResponse) IsOneOf(status ...RuleStatus) bool {
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
|
@ -128,6 +148,10 @@ func (er EngineResponse) IsNil() bool {
|
|||
return datautils.DeepEqual(er, EngineResponse{})
|
||||
}
|
||||
|
||||
func (er EngineResponse) IsValidatingAdmissionPolicy() bool {
|
||||
return !datautils.DeepEqual(er.validatingAdmissionPolicy, v1alpha1.ValidatingAdmissionPolicy{})
|
||||
}
|
||||
|
||||
// GetPatches returns all the patches joined
|
||||
func (er EngineResponse) GetPatches() [][]byte {
|
||||
var patches [][]byte
|
||||
|
|
|
@ -3,78 +3,98 @@ package yaml
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
log "github.com/kyverno/kyverno/pkg/logging"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
// GetPolicy extracts policies from YAML bytes
|
||||
func GetPolicy(bytes []byte) (policies []kyvernov1.PolicyInterface, err error) {
|
||||
func GetPolicy(bytes []byte) (policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, err error) {
|
||||
documents, err := SplitDocuments(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, thisPolicyBytes := range documents {
|
||||
policyBytes, err := yaml.ToJSON(thisPolicyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert to JSON: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to convert to JSON: %v", err)
|
||||
}
|
||||
us := &unstructured.Unstructured{}
|
||||
|
||||
if err := json.Unmarshal(policyBytes, us); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode policy: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to decode policy: %v", err)
|
||||
}
|
||||
if us.IsList() {
|
||||
list, err := us.ToList()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode policy list: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to decode policy list: %v", err)
|
||||
}
|
||||
|
||||
for i := range list.Items {
|
||||
item := list.Items[i]
|
||||
if policies, err = addPolicy(policies, &item); err != nil {
|
||||
return nil, err
|
||||
if policies, validatingAdmissionPolicies, err = addPolicy(policies, validatingAdmissionPolicies, &item); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if policies, err = addPolicy(policies, us); err != nil {
|
||||
return nil, err
|
||||
if policies, validatingAdmissionPolicies, err = addPolicy(policies, validatingAdmissionPolicies, us); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return policies, nil
|
||||
return policies, validatingAdmissionPolicies, nil
|
||||
}
|
||||
|
||||
func addPolicy(policies []kyvernov1.PolicyInterface, us *unstructured.Unstructured) ([]kyvernov1.PolicyInterface, error) {
|
||||
var policy kyvernov1.PolicyInterface
|
||||
if us.GetKind() == "ClusterPolicy" {
|
||||
policy = &kyvernov1.ClusterPolicy{}
|
||||
} else {
|
||||
policy = &kyvernov1.Policy{}
|
||||
}
|
||||
func addPolicy(policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, us *unstructured.Unstructured) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
||||
kind := us.GetKind()
|
||||
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(us.Object, policy); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode policy: %v", err)
|
||||
}
|
||||
if strings.Compare(kind, "ValidatingAdmissionPolicy") == 0 {
|
||||
validatingAdmissionPolicy := &v1alpha1.ValidatingAdmissionPolicy{}
|
||||
|
||||
if policy.GetKind() == "" {
|
||||
log.V(3).Info("skipping file as policy.TypeMeta.Kind not found")
|
||||
return policies, nil
|
||||
}
|
||||
if policy.GetKind() != "ClusterPolicy" && policy.GetKind() != "Policy" {
|
||||
return nil, fmt.Errorf("resource %s/%s is not a Policy or a ClusterPolicy", policy.GetKind(), policy.GetName())
|
||||
}
|
||||
|
||||
if policy.GetKind() == "Policy" {
|
||||
if policy.GetNamespace() == "" {
|
||||
policy.SetNamespace("default")
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(us.Object, validatingAdmissionPolicy); err != nil {
|
||||
return policies, nil, fmt.Errorf("failed to decode policy: %v", err)
|
||||
}
|
||||
|
||||
if validatingAdmissionPolicy.Kind == "" {
|
||||
log.V(3).Info("skipping file as ValidatingAdmissionPolicy.Kind not found")
|
||||
return policies, validatingAdmissionPolicies, nil
|
||||
}
|
||||
|
||||
validatingAdmissionPolicies = append(validatingAdmissionPolicies, *validatingAdmissionPolicy)
|
||||
} else {
|
||||
policy.SetNamespace("")
|
||||
var policy kyvernov1.PolicyInterface
|
||||
if us.GetKind() == "ClusterPolicy" {
|
||||
policy = &kyvernov1.ClusterPolicy{}
|
||||
} else {
|
||||
policy = &kyvernov1.Policy{}
|
||||
}
|
||||
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(us.Object, policy); err != nil {
|
||||
return nil, validatingAdmissionPolicies, fmt.Errorf("failed to decode policy: %v", err)
|
||||
}
|
||||
|
||||
if policy.GetKind() == "" {
|
||||
log.V(3).Info("skipping file as policy.TypeMeta.Kind not found")
|
||||
return policies, validatingAdmissionPolicies, nil
|
||||
}
|
||||
if policy.GetKind() != "ClusterPolicy" && policy.GetKind() != "Policy" {
|
||||
return nil, validatingAdmissionPolicies, fmt.Errorf("resource %s/%s is not a Policy or a ClusterPolicy", policy.GetKind(), policy.GetName())
|
||||
}
|
||||
|
||||
if policy.GetKind() == "Policy" {
|
||||
if policy.GetNamespace() == "" {
|
||||
policy.SetNamespace("default")
|
||||
}
|
||||
} else {
|
||||
policy.SetNamespace("")
|
||||
}
|
||||
policies = append(policies, policy)
|
||||
}
|
||||
policies = append(policies, policy)
|
||||
return policies, nil
|
||||
|
||||
return policies, validatingAdmissionPolicies, nil
|
||||
}
|
||||
|
|
|
@ -15,10 +15,11 @@ func TestGetPolicy(t *testing.T) {
|
|||
namespace string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantPolicies []policy
|
||||
wantErr bool
|
||||
name string
|
||||
args args
|
||||
wantPolicies []policy
|
||||
validatingAdmissionPolicies []policy
|
||||
wantErr bool
|
||||
}{{
|
||||
name: "policy",
|
||||
args: args{
|
||||
|
@ -297,10 +298,140 @@ items:
|
|||
{"ClusterPolicy", ""},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "ValidatingAdmissionPolicy",
|
||||
args: args{
|
||||
[]byte(`
|
||||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "demo-policy.example.com"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments"]
|
||||
validations:
|
||||
- expression: "object.spec.replicas <= 5"
|
||||
`),
|
||||
}, validatingAdmissionPolicies: []policy{
|
||||
{"ValidatingAdmissionPolicy", ""},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "ValidatingAdmissionPolicy and Policy",
|
||||
args: args{
|
||||
[]byte(`
|
||||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "demo-policy.example.com"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments"]
|
||||
validations:
|
||||
- expression: "object.spec.replicas <= 5"
|
||||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: Policy
|
||||
metadata:
|
||||
name: generate-policy
|
||||
namespace: ns-1
|
||||
spec:
|
||||
rules:
|
||||
- name: copy-game-demo
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
- default
|
||||
- kube-public
|
||||
- kyverno
|
||||
generate:
|
||||
kind: ConfigMap
|
||||
name: game-demo
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
synchronize: true
|
||||
clone:
|
||||
namespace: default
|
||||
name: game-demo
|
||||
`),
|
||||
}, wantPolicies: []policy{
|
||||
{"Policy", "ns-1"},
|
||||
},
|
||||
validatingAdmissionPolicies: []policy{
|
||||
{"ValidatingAdmissionPolicy", ""},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "ValidatingAdmissionPolicy and ClusterPolicy",
|
||||
args: args{
|
||||
[]byte(`
|
||||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "demo-policy.example.com"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments"]
|
||||
validations:
|
||||
- expression: "object.spec.replicas <= 5"
|
||||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: generate-policy
|
||||
spec:
|
||||
rules:
|
||||
- name: copy-game-demo
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
- default
|
||||
- kube-public
|
||||
- kyverno
|
||||
generate:
|
||||
kind: ConfigMap
|
||||
name: game-demo
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
synchronize: true
|
||||
clone:
|
||||
namespace: default
|
||||
name: game-demo
|
||||
`),
|
||||
}, wantPolicies: []policy{
|
||||
{"ClusterPolicy", ""},
|
||||
},
|
||||
validatingAdmissionPolicies: []policy{
|
||||
{"ValidatingAdmissionPolicy", ""},
|
||||
},
|
||||
wantErr: false,
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotPolicies, err := GetPolicy(tt.args.bytes)
|
||||
gotPolicies, gotValidatingAdmissionPolicies, err := GetPolicy(tt.args.bytes)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
|
@ -311,6 +442,13 @@ items:
|
|||
assert.Equal(t, tt.wantPolicies[i].namespace, gotPolicies[i].GetNamespace())
|
||||
}
|
||||
}
|
||||
|
||||
if assert.Equal(t, len(tt.validatingAdmissionPolicies), len(gotValidatingAdmissionPolicies)) {
|
||||
for i := range tt.validatingAdmissionPolicies {
|
||||
assert.Equal(t, tt.validatingAdmissionPolicies[i].kind, gotValidatingAdmissionPolicies[i].Kind)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ func TestNotAllowedVars_MatchSection(t *testing.T) {
|
|||
}
|
||||
`)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyWithVarInMatch)
|
||||
policy, _, err := yamlutils.GetPolicy(policyWithVarInMatch)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -106,7 +106,7 @@ func TestNotAllowedVars_ExcludeSection(t *testing.T) {
|
|||
}
|
||||
`)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyWithVarInExclude)
|
||||
policy, _, err := yamlutils.GetPolicy(policyWithVarInExclude)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -159,7 +159,7 @@ func TestNotAllowedVars_ExcludeSection_PositiveCase(t *testing.T) {
|
|||
}
|
||||
`)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyWithVarInExclude)
|
||||
policy, _, err := yamlutils.GetPolicy(policyWithVarInExclude)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -193,7 +193,7 @@ func TestNotAllowedVars_JSONPatchPath(t *testing.T) {
|
|||
}
|
||||
}`)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyWithVarInExclude)
|
||||
policy, _, err := yamlutils.GetPolicy(policyWithVarInExclude)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -238,7 +238,7 @@ func TestNotAllowedVars_JSONPatchPath_ContextRootPositive(t *testing.T) {
|
|||
}
|
||||
}`)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyManifest)
|
||||
policy, _, err := yamlutils.GetPolicy(policyManifest)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -281,7 +281,7 @@ func TestNotAllowedVars_JSONPatchPath_ContextSubPositive(t *testing.T) {
|
|||
}
|
||||
}`)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyManifest)
|
||||
policy, _, err := yamlutils.GetPolicy(policyManifest)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -315,7 +315,7 @@ func TestNotAllowedVars_JSONPatchPath_PositiveCase(t *testing.T) {
|
|||
}
|
||||
}`)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyWithVarInExclude)
|
||||
policy, _, err := yamlutils.GetPolicy(policyWithVarInExclude)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -347,7 +347,7 @@ spec:
|
|||
policyJSON, err := yaml.ToJSON(policyYAML)
|
||||
assert.NilError(t, err)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyJSON)
|
||||
policy, _, err := yamlutils.GetPolicy(policyJSON)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -435,7 +435,7 @@ func TestNotAllowedVars_VariableFormats(t *testing.T) {
|
|||
value: "foo.com"
|
||||
`, tc.input))
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyYAML)
|
||||
policy, _, err := yamlutils.GetPolicy(policyYAML)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
@ -481,7 +481,7 @@ spec:
|
|||
policyJSON, err := yaml.ToJSON(policyYAML)
|
||||
assert.NilError(t, err)
|
||||
|
||||
policy, err := yamlutils.GetPolicy(policyJSON)
|
||||
policy, _, err := yamlutils.GetPolicy(policyJSON)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = hasInvalidVariables(policy[0], false)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "demo-policy.example.com"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments"]
|
||||
validations:
|
||||
- expression: "object.spec.template.spec.containers.all(container, !container.image.contains('latest'))"
|
||||
message: "Using a mutable image tag e.g. 'latest' is not allowed."
|
||||
- expression: "object.spec.replicas <= 5"
|
||||
message: "Replicas must be less than or equal 5"
|
||||
matchConditions:
|
||||
- name: "deployment name"
|
||||
expression: "object.metadata.name == 'nginx-deployment'"
|
Loading…
Add table
Reference in a new issue