mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 10:28:36 +00:00
fix: cli engine invocation order (#8327)
* fix: cli engine invocation order Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * mutate Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * verify images Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * validate 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> * generate 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 and tests Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix tests Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
7a982a4c2c
commit
10bb27b4da
10 changed files with 347 additions and 319 deletions
|
@ -66,7 +66,6 @@ type ApplyCommandConfig struct {
|
|||
warnNoPassed bool
|
||||
}
|
||||
|
||||
// allow os.exit to be overwritten during unit tests
|
||||
func Command() *cobra.Command {
|
||||
var cmd *cobra.Command
|
||||
var removeColor, detailedResults, table bool
|
||||
|
@ -248,61 +247,58 @@ func (c *ApplyCommandConfig) applyPolicytoResource(
|
|||
if vars != nil {
|
||||
vars.SetInStore()
|
||||
}
|
||||
|
||||
// validate policies
|
||||
var validPolicies []kyvernov1.PolicyInterface
|
||||
for _, pol := range policies {
|
||||
// TODO we should return this info to the caller
|
||||
_, err := policyvalidation.Validate(pol, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
if err != nil {
|
||||
log.Log.Error(err, "policy validation error")
|
||||
if strings.HasPrefix(err.Error(), "variable 'element.name'") {
|
||||
skipInvalidPolicies.invalid = append(skipInvalidPolicies.invalid, pol.GetName())
|
||||
} else {
|
||||
skipInvalidPolicies.skipped = append(skipInvalidPolicies.skipped, pol.GetName())
|
||||
}
|
||||
continue
|
||||
}
|
||||
matches, err := policy.ExtractVariables(pol)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "skipping invalid policy", "name", pol.GetName())
|
||||
continue
|
||||
}
|
||||
if !vars.HasVariables() && variables.NeedsVariables(matches...) {
|
||||
// check policy in variable file
|
||||
if !vars.HasPolicyVariables(pol.GetName()) {
|
||||
fmt.Printf("test skipped for policy %v (as required variables are not provided by the users) \n \n", pol.GetName())
|
||||
continue
|
||||
}
|
||||
}
|
||||
validPolicies = append(validPolicies, pol)
|
||||
}
|
||||
var rc processor.ResultCounts
|
||||
var responses []engineapi.EngineResponse
|
||||
for _, resource := range resources {
|
||||
for _, pol := range policies {
|
||||
_, err := policyvalidation.Validate(pol, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
if err != nil {
|
||||
log.Log.Error(err, "policy validation error")
|
||||
if strings.HasPrefix(err.Error(), "variable 'element.name'") {
|
||||
skipInvalidPolicies.invalid = append(skipInvalidPolicies.invalid, pol.GetName())
|
||||
} else {
|
||||
skipInvalidPolicies.skipped = append(skipInvalidPolicies.skipped, pol.GetName())
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
matches, err := policy.ExtractVariables(pol)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "skipping invalid policy", "name", pol.GetName())
|
||||
continue
|
||||
}
|
||||
if !vars.HasVariables() && variables.NeedsVariables(matches...) {
|
||||
// check policy in variable file
|
||||
if !vars.HasPolicyVariables(pol.GetName()) {
|
||||
skipInvalidPolicies.skipped = append(skipInvalidPolicies.skipped, pol.GetName())
|
||||
continue
|
||||
}
|
||||
}
|
||||
kindOnwhichPolicyIsApplied := common.GetKindsFromPolicy(pol, vars.Subresources(), dClient)
|
||||
resourceValues, err := vars.ComputeVariables(pol.GetName(), resource.GetName(), resource.GetKind(), kindOnwhichPolicyIsApplied, matches...)
|
||||
if err != nil {
|
||||
return &rc, resources, responses, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", pol.GetName(), resource.GetName()), err)
|
||||
}
|
||||
processor := processor.PolicyProcessor{
|
||||
Policy: pol,
|
||||
Resource: resource,
|
||||
MutateLogPath: c.MutateLogPath,
|
||||
MutateLogPathIsDir: mutateLogPathIsDir,
|
||||
Variables: resourceValues,
|
||||
UserInfo: userInfo,
|
||||
PolicyReport: c.PolicyReport,
|
||||
NamespaceSelectorMap: vars.NamespaceSelectors(),
|
||||
Stdin: c.Stdin,
|
||||
Rc: &rc,
|
||||
PrintPatchResource: true,
|
||||
Client: dClient,
|
||||
AuditWarn: c.AuditWarn,
|
||||
Subresources: vars.Subresources(),
|
||||
}
|
||||
ers, err := processor.ApplyPolicyOnResource()
|
||||
if err != nil {
|
||||
return &rc, resources, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", pol.GetName(), resource.GetName()).Error(), err)
|
||||
}
|
||||
responses = append(responses, processSkipEngineResponses(ers)...)
|
||||
processor := processor.PolicyProcessor{
|
||||
Policies: validPolicies,
|
||||
Resource: *resource,
|
||||
MutateLogPath: c.MutateLogPath,
|
||||
MutateLogPathIsDir: mutateLogPathIsDir,
|
||||
Variables: vars,
|
||||
UserInfo: userInfo,
|
||||
PolicyReport: c.PolicyReport,
|
||||
NamespaceSelectorMap: vars.NamespaceSelectors(),
|
||||
Stdin: c.Stdin,
|
||||
Rc: &rc,
|
||||
PrintPatchResource: true,
|
||||
Client: dClient,
|
||||
AuditWarn: c.AuditWarn,
|
||||
Subresources: vars.Subresources(),
|
||||
}
|
||||
ers, err := processor.ApplyPoliciesOnResource()
|
||||
if err != nil {
|
||||
return &rc, resources, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policies on resource %v", resource.GetName()).Error(), err)
|
||||
}
|
||||
responses = append(responses, processSkipEngineResponses(ers)...)
|
||||
}
|
||||
return &rc, resources, responses, nil
|
||||
}
|
||||
|
|
|
@ -329,7 +329,9 @@ func Test_Apply(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
verifyTestcase(t, tc, compareSummary)
|
||||
t.Run("", func(t *testing.T) {
|
||||
verifyTestcase(t, tc, compareSummary)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package test
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/pluralize"
|
||||
|
@ -112,11 +113,8 @@ func runTest(openApiManager openapi.Manager, testCase test.TestCase, auditWarn b
|
|||
}
|
||||
}
|
||||
}
|
||||
// execute engine
|
||||
fmt.Println(" Applying", len(policies), pluralize.Pluralize(len(policies), "policy", "policies"), "to", len(uniques), pluralize.Pluralize(len(uniques), "resource", "resources"), "...")
|
||||
var engineResponses []engineapi.EngineResponse
|
||||
var resultCounts processor.ResultCounts
|
||||
// TODO loop through resources first, then through policies second
|
||||
// validate policies
|
||||
var validPolicies []kyvernov1.PolicyInterface
|
||||
for _, pol := range policies {
|
||||
// TODO we should return this info to the caller
|
||||
_, err := policyvalidation.Validate(pol, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
|
@ -133,42 +131,36 @@ func runTest(openApiManager openapi.Manager, testCase test.TestCase, auditWarn b
|
|||
// check policy in variable file
|
||||
if !vars.HasPolicyVariables(pol.GetName()) {
|
||||
fmt.Printf("test skipped for policy %v (as required variables are not provided by the users) \n \n", pol.GetName())
|
||||
// TODO continue ? return error ?
|
||||
// continue
|
||||
}
|
||||
}
|
||||
validPolicies = append(validPolicies, pol)
|
||||
}
|
||||
// execute engine
|
||||
fmt.Println(" Applying", len(policies), pluralize.Pluralize(len(policies), "policy", "policies"), "to", len(uniques), pluralize.Pluralize(len(uniques), "resource", "resources"), "...")
|
||||
var engineResponses []engineapi.EngineResponse
|
||||
var resultCounts processor.ResultCounts
|
||||
|
||||
kindOnwhichPolicyIsApplied := common.GetKindsFromPolicy(pol, vars.Subresources(), dClient)
|
||||
|
||||
for _, resource := range uniques {
|
||||
resourceValues, err := vars.ComputeVariables(pol.GetName(), resource.GetName(), resource.GetKind(), kindOnwhichPolicyIsApplied, matches...)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf(
|
||||
"policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag",
|
||||
pol.GetName(),
|
||||
resource.GetName(),
|
||||
)
|
||||
return nil, sanitizederror.NewWithError(message, err)
|
||||
}
|
||||
processor := processor.PolicyProcessor{
|
||||
Policy: pol,
|
||||
Resource: resource,
|
||||
MutateLogPath: "",
|
||||
Variables: resourceValues,
|
||||
UserInfo: userInfo,
|
||||
PolicyReport: true,
|
||||
NamespaceSelectorMap: vars.NamespaceSelectors(),
|
||||
Rc: &resultCounts,
|
||||
RuleToCloneSourceResource: ruleToCloneSourceResource,
|
||||
Client: dClient,
|
||||
Subresources: vars.Subresources(),
|
||||
}
|
||||
ers, err := processor.ApplyPolicyOnResource()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("failed to apply policy %v on resource %v", pol.GetName(), resource.GetName())
|
||||
return nil, sanitizederror.NewWithError(message, err)
|
||||
}
|
||||
engineResponses = append(engineResponses, ers...)
|
||||
for _, resource := range uniques {
|
||||
processor := processor.PolicyProcessor{
|
||||
Policies: validPolicies,
|
||||
Resource: *resource,
|
||||
MutateLogPath: "",
|
||||
Variables: vars,
|
||||
UserInfo: userInfo,
|
||||
PolicyReport: true,
|
||||
NamespaceSelectorMap: vars.NamespaceSelectors(),
|
||||
Rc: &resultCounts,
|
||||
RuleToCloneSourceResource: ruleToCloneSourceResource,
|
||||
Client: dClient,
|
||||
Subresources: vars.Subresources(),
|
||||
}
|
||||
ers, err := processor.ApplyPoliciesOnResource()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("failed to apply policies on resource %v", resource.GetName())
|
||||
return nil, sanitizederror.NewWithError(message, err)
|
||||
}
|
||||
engineResponses = append(engineResponses, ers...)
|
||||
}
|
||||
for _, resource := range uniques {
|
||||
processor := processor.ValidatingAdmissionPolicyProcessor{
|
||||
|
|
|
@ -12,7 +12,9 @@ import (
|
|||
valuesapi "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/values"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/store"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
|
||||
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/variables"
|
||||
"github.com/kyverno/kyverno/pkg/autogen"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
|
@ -21,20 +23,20 @@ import (
|
|||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/engine/factories"
|
||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||
"github.com/kyverno/kyverno/pkg/engine/policycontext"
|
||||
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type PolicyProcessor struct {
|
||||
Policy kyvernov1.PolicyInterface
|
||||
Resource *unstructured.Unstructured
|
||||
Policies []kyvernov1.PolicyInterface
|
||||
Resource unstructured.Unstructured
|
||||
MutateLogPath string
|
||||
MutateLogPathIsDir bool
|
||||
Variables map[string]interface{}
|
||||
Variables *variables.Variables
|
||||
UserInfo *kyvernov1beta1.RequestInfo
|
||||
PolicyReport bool
|
||||
NamespaceSelectorMap map[string]map[string]string
|
||||
|
@ -47,45 +49,28 @@ type PolicyProcessor struct {
|
|||
Subresources []valuesapi.Subresource
|
||||
}
|
||||
|
||||
func (p *PolicyProcessor) ApplyPolicyOnResource() ([]engineapi.EngineResponse, error) {
|
||||
jp := jmespath.New(config.NewDefaultConfiguration(false))
|
||||
|
||||
var engineResponses []engineapi.EngineResponse
|
||||
namespaceLabels := make(map[string]string)
|
||||
operation := kyvernov1.Create
|
||||
|
||||
if p.Variables["request.operation"] == "DELETE" {
|
||||
operation = kyvernov1.Delete
|
||||
}
|
||||
rules := autogen.ComputeRules(p.Policy)
|
||||
|
||||
if needsNamespaceLabels(rules...) {
|
||||
resourceNamespace := p.Resource.GetNamespace()
|
||||
namespaceLabels = p.NamespaceSelectorMap[p.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", p.Resource.GetName()), nil)
|
||||
}
|
||||
}
|
||||
|
||||
resPath := fmt.Sprintf("%s/%s/%s", p.Resource.GetNamespace(), p.Resource.GetKind(), p.Resource.GetName())
|
||||
log.Log.V(3).Info("applying policy on resource", "policy", p.Policy.GetName(), "resource", resPath)
|
||||
|
||||
resourceRaw, err := p.Resource.MarshalJSON()
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to marshal resource")
|
||||
}
|
||||
|
||||
updatedResource, err := kubeutils.BytesToUnstructured(resourceRaw)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "unable to convert raw resource to unstructured")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to load resource in context")
|
||||
}
|
||||
|
||||
func (p *PolicyProcessor) ApplyPoliciesOnResource() ([]engineapi.EngineResponse, error) {
|
||||
cfg := config.NewDefaultConfiguration(false)
|
||||
gvk, subresource := updatedResource.GroupVersionKind(), ""
|
||||
jp := jmespath.New(cfg)
|
||||
resource := p.Resource
|
||||
namespaceLabels := p.NamespaceSelectorMap[p.Resource.GetNamespace()]
|
||||
var client engineapi.Client
|
||||
if p.Client != nil {
|
||||
client = adapters.Client(p.Client)
|
||||
}
|
||||
rclient := registryclient.NewOrDie()
|
||||
eng := engine.NewEngine(
|
||||
cfg,
|
||||
config.NewDefaultMetricsConfiguration(),
|
||||
jmespath.New(cfg),
|
||||
client,
|
||||
factories.DefaultRegistryClientFactory(adapters.RegistryClient(rclient), nil),
|
||||
imageverifycache.DisabledImageVerifyCache(),
|
||||
store.ContextLoaderFactory(nil),
|
||||
nil,
|
||||
"",
|
||||
)
|
||||
gvk, subresource := resource.GroupVersionKind(), ""
|
||||
// If --cluster flag is not set, then we need to find the top level resource GVK and subresource
|
||||
if p.Client == nil {
|
||||
for _, s := range p.Subresources {
|
||||
|
@ -105,25 +90,114 @@ func (p *PolicyProcessor) ApplyPolicyOnResource() ([]engineapi.EngineResponse, e
|
|||
}
|
||||
}
|
||||
}
|
||||
var client engineapi.Client
|
||||
if p.Client != nil {
|
||||
client = adapters.Client(p.Client)
|
||||
resPath := fmt.Sprintf("%s/%s/%s", resource.GetNamespace(), resource.GetKind(), resource.GetName())
|
||||
var responses []engineapi.EngineResponse
|
||||
// mutate
|
||||
for _, policy := range p.Policies {
|
||||
policyContext, err := p.makePolicyContext(jp, cfg, resource, policy, namespaceLabels, gvk, subresource)
|
||||
if err != nil {
|
||||
return responses, err
|
||||
}
|
||||
mutateResponse := eng.Mutate(context.Background(), policyContext)
|
||||
combineRuleResponses(mutateResponse)
|
||||
err = p.processMutateEngineResponse(mutateResponse, resPath)
|
||||
if err != nil {
|
||||
if !sanitizederror.IsErrorSanitized(err) {
|
||||
return responses, sanitizederror.NewWithError("failed to print mutated result", err)
|
||||
}
|
||||
}
|
||||
responses = append(responses, mutateResponse)
|
||||
resource = mutateResponse.PatchedResource
|
||||
}
|
||||
// verify images
|
||||
for _, policy := range p.Policies {
|
||||
policyContext, err := p.makePolicyContext(jp, cfg, resource, policy, namespaceLabels, gvk, subresource)
|
||||
if err != nil {
|
||||
return responses, err
|
||||
}
|
||||
// TODO annotation
|
||||
verifyImageResponse, _ := eng.VerifyAndPatchImages(context.TODO(), policyContext)
|
||||
if !verifyImageResponse.IsEmpty() {
|
||||
verifyImageResponse = combineRuleResponses(verifyImageResponse)
|
||||
responses = append(responses, verifyImageResponse)
|
||||
resource = verifyImageResponse.PatchedResource
|
||||
}
|
||||
}
|
||||
// validate
|
||||
for _, policy := range p.Policies {
|
||||
policyContext, err := p.makePolicyContext(jp, cfg, resource, policy, namespaceLabels, gvk, subresource)
|
||||
if err != nil {
|
||||
return responses, err
|
||||
}
|
||||
validateResponse := eng.Validate(context.TODO(), policyContext)
|
||||
if !validateResponse.IsEmpty() {
|
||||
validateResponse = combineRuleResponses(validateResponse)
|
||||
responses = append(responses, validateResponse)
|
||||
resource = validateResponse.PatchedResource
|
||||
}
|
||||
}
|
||||
// generate
|
||||
for _, policy := range p.Policies {
|
||||
var policyHasGenerate bool
|
||||
for _, rule := range autogen.ComputeRules(policy) {
|
||||
if rule.HasGenerate() {
|
||||
policyHasGenerate = true
|
||||
}
|
||||
}
|
||||
if policyHasGenerate {
|
||||
policyContext, err := p.makePolicyContext(jp, cfg, resource, policy, namespaceLabels, gvk, subresource)
|
||||
if err != nil {
|
||||
return responses, err
|
||||
}
|
||||
|
||||
generateResponse := eng.ApplyBackgroundChecks(context.TODO(), policyContext)
|
||||
if !generateResponse.IsEmpty() {
|
||||
newRuleResponse, err := handleGeneratePolicy(&generateResponse, *policyContext, p.RuleToCloneSourceResource)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to apply generate policy")
|
||||
} else {
|
||||
generateResponse.PolicyResponse.Rules = newRuleResponse
|
||||
}
|
||||
combineRuleResponses(generateResponse)
|
||||
responses = append(responses, generateResponse)
|
||||
}
|
||||
p.Rc.addGenerateResponse(p.AuditWarn, resPath, generateResponse)
|
||||
}
|
||||
}
|
||||
p.Rc.addEngineResponses(p.AuditWarn, responses...)
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (p *PolicyProcessor) makePolicyContext(
|
||||
jp jmespath.Interface,
|
||||
cfg config.Configuration,
|
||||
resource unstructured.Unstructured,
|
||||
policy kyvernov1.PolicyInterface,
|
||||
namespaceLabels map[string]string,
|
||||
gvk schema.GroupVersionKind,
|
||||
subresource string,
|
||||
) (*policycontext.PolicyContext, error) {
|
||||
operation := kyvernov1.Create
|
||||
var resourceValues map[string]interface{}
|
||||
if p.Variables != nil {
|
||||
kindOnwhichPolicyIsApplied := common.GetKindsFromPolicy(policy, p.Variables.Subresources(), p.Client)
|
||||
vals, err := p.Variables.ComputeVariables(policy.GetName(), resource.GetName(), resource.GetKind(), kindOnwhichPolicyIsApplied /*matches...*/)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf(
|
||||
"policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag",
|
||||
policy.GetName(),
|
||||
resource.GetName(),
|
||||
)
|
||||
return nil, sanitizederror.NewWithError(message, err)
|
||||
}
|
||||
resourceValues = vals
|
||||
}
|
||||
if resourceValues["request.operation"] == "DELETE" {
|
||||
operation = kyvernov1.Delete
|
||||
}
|
||||
rclient := registryclient.NewOrDie()
|
||||
eng := engine.NewEngine(
|
||||
cfg,
|
||||
config.NewDefaultMetricsConfiguration(),
|
||||
jmespath.New(cfg),
|
||||
client,
|
||||
factories.DefaultRegistryClientFactory(adapters.RegistryClient(rclient), nil),
|
||||
imageverifycache.DisabledImageVerifyCache(),
|
||||
store.ContextLoaderFactory(nil),
|
||||
nil,
|
||||
"",
|
||||
)
|
||||
policyContext, err := engine.NewPolicyContext(
|
||||
jp,
|
||||
*updatedResource,
|
||||
resource,
|
||||
operation,
|
||||
p.UserInfo,
|
||||
cfg,
|
||||
|
@ -131,80 +205,17 @@ func (p *PolicyProcessor) ApplyPolicyOnResource() ([]engineapi.EngineResponse, e
|
|||
if err != nil {
|
||||
log.Log.Error(err, "failed to create policy context")
|
||||
}
|
||||
|
||||
policyContext = policyContext.
|
||||
WithPolicy(p.Policy).
|
||||
WithPolicy(policy).
|
||||
WithNamespaceLabels(namespaceLabels).
|
||||
WithResourceKind(gvk, subresource)
|
||||
|
||||
for key, value := range p.Variables {
|
||||
for key, value := range resourceValues {
|
||||
err = policyContext.JSONContext().AddVariable(key, value)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to add variable to context")
|
||||
}
|
||||
}
|
||||
|
||||
mutateResponse := eng.Mutate(context.Background(), policyContext)
|
||||
combineRuleResponses(mutateResponse)
|
||||
engineResponses = append(engineResponses, mutateResponse)
|
||||
|
||||
err = p.processMutateEngineResponse(mutateResponse, resPath)
|
||||
if err != nil {
|
||||
if !sanitizederror.IsErrorSanitized(err) {
|
||||
return engineResponses, sanitizederror.NewWithError("failed to print mutated result", err)
|
||||
}
|
||||
}
|
||||
|
||||
verifyImageResponse, _ := eng.VerifyAndPatchImages(context.TODO(), policyContext)
|
||||
if !verifyImageResponse.IsEmpty() {
|
||||
verifyImageResponse = combineRuleResponses(verifyImageResponse)
|
||||
engineResponses = append(engineResponses, verifyImageResponse)
|
||||
}
|
||||
|
||||
var policyHasValidate bool
|
||||
for _, rule := range rules {
|
||||
if rule.HasValidate() || rule.HasVerifyImageChecks() {
|
||||
policyHasValidate = true
|
||||
}
|
||||
}
|
||||
|
||||
policyContext = policyContext.WithNewResource(mutateResponse.PatchedResource)
|
||||
|
||||
var validateResponse engineapi.EngineResponse
|
||||
if policyHasValidate {
|
||||
validateResponse = eng.Validate(context.Background(), policyContext)
|
||||
validateResponse = combineRuleResponses(validateResponse)
|
||||
}
|
||||
|
||||
if !validateResponse.IsEmpty() {
|
||||
engineResponses = append(engineResponses, validateResponse)
|
||||
}
|
||||
|
||||
var policyHasGenerate bool
|
||||
for _, rule := range rules {
|
||||
if rule.HasGenerate() {
|
||||
policyHasGenerate = true
|
||||
}
|
||||
}
|
||||
|
||||
if policyHasGenerate {
|
||||
generateResponse := eng.ApplyBackgroundChecks(context.TODO(), policyContext)
|
||||
if !generateResponse.IsEmpty() {
|
||||
newRuleResponse, err := handleGeneratePolicy(&generateResponse, *policyContext, p.RuleToCloneSourceResource)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to apply generate policy")
|
||||
} else {
|
||||
generateResponse.PolicyResponse.Rules = newRuleResponse
|
||||
}
|
||||
combineRuleResponses(generateResponse)
|
||||
engineResponses = append(engineResponses, generateResponse)
|
||||
}
|
||||
p.Rc.addGenerateResponse(p.AuditWarn, resPath, generateResponse)
|
||||
}
|
||||
|
||||
p.Rc.addEngineResponses(p.AuditWarn, engineResponses...)
|
||||
|
||||
return engineResponses, nil
|
||||
return policyContext, nil
|
||||
}
|
||||
|
||||
func (p *PolicyProcessor) processMutateEngineResponse(response engineapi.EngineResponse, resourcePath string) error {
|
||||
|
@ -219,7 +230,7 @@ func (p *PolicyProcessor) processMutateEngineResponse(response engineapi.EngineR
|
|||
mutatedResource := string(yamlEncodedResource) + string("\n---")
|
||||
if len(strings.TrimSpace(mutatedResource)) > 0 {
|
||||
if !p.Stdin {
|
||||
fmt.Printf("\nmutate policy %s applied to %s:", p.Policy.GetName(), resourcePath)
|
||||
fmt.Printf("\nmutate policy %s applied to %s:", response.Policy().GetName(), resourcePath)
|
||||
}
|
||||
fmt.Printf("\n" + mutatedResource + "\n")
|
||||
}
|
||||
|
|
123
cmd/cli/kubectl-kyverno/processor/policy_processor_test.go
Normal file
123
cmd/cli/kubectl-kyverno/processor/policy_processor_test.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package processor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
var policyNamespaceSelector = []byte(`{
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "enforce-pod-name"
|
||||
},
|
||||
"spec": {
|
||||
"validationFailureAction": "audit",
|
||||
"background": true,
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate-name",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
],
|
||||
"namespaceSelector": {
|
||||
"matchExpressions": [
|
||||
{
|
||||
"key": "foo.com/managed-state",
|
||||
"operator": "In",
|
||||
"values": [
|
||||
"managed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "The Pod must end with -nginx",
|
||||
"pattern": {
|
||||
"metadata": {
|
||||
"name": "*-nginx"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
func Test_NamespaceSelector(t *testing.T) {
|
||||
type resultCounts struct {
|
||||
pass int
|
||||
fail int
|
||||
warn int
|
||||
err int
|
||||
skip int
|
||||
}
|
||||
type TestCase struct {
|
||||
policy []byte
|
||||
resource []byte
|
||||
namespaceSelectorMap map[string]map[string]string
|
||||
result ResultCounts
|
||||
}
|
||||
|
||||
testcases := []TestCase{
|
||||
{
|
||||
policy: policyNamespaceSelector,
|
||||
resource: []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"nginx","namespace":"test1"},"spec":{"containers":[{"image":"nginx:latest","name":"test-fail"}]}}`),
|
||||
namespaceSelectorMap: map[string]map[string]string{
|
||||
"test1": {
|
||||
"foo.com/managed-state": "managed",
|
||||
},
|
||||
},
|
||||
result: ResultCounts{
|
||||
pass: 0,
|
||||
fail: 1,
|
||||
warn: 0,
|
||||
err: 0,
|
||||
skip: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
policy: policyNamespaceSelector,
|
||||
resource: []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"test-nginx","namespace":"test1"},"spec":{"containers":[{"image":"nginx:latest","name":"test-pass"}]}}`),
|
||||
namespaceSelectorMap: map[string]map[string]string{
|
||||
"test1": {
|
||||
"foo.com/managed-state": "managed",
|
||||
},
|
||||
},
|
||||
result: ResultCounts{
|
||||
pass: 1,
|
||||
fail: 1,
|
||||
warn: 0,
|
||||
err: 0,
|
||||
skip: 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
rc := &ResultCounts{}
|
||||
for _, tc := range testcases {
|
||||
policyArray, _, _ := yamlutils.GetPolicy(tc.policy)
|
||||
resourceArray, _ := resource.GetUnstructuredResources(tc.resource)
|
||||
processor := PolicyProcessor{
|
||||
Policies: policyArray,
|
||||
Resource: *resourceArray[0],
|
||||
MutateLogPath: "",
|
||||
UserInfo: nil,
|
||||
NamespaceSelectorMap: tc.namespaceSelectorMap,
|
||||
Rc: rc,
|
||||
}
|
||||
processor.ApplyPoliciesOnResource()
|
||||
assert.Equal(t, int64(rc.Pass()), int64(tc.result.pass))
|
||||
assert.Equal(t, int64(rc.Fail()), int64(tc.result.fail))
|
||||
assert.Equal(t, int64(rc.Skip()), int64(tc.result.skip))
|
||||
assert.Equal(t, int64(rc.Warn()), int64(tc.result.warn))
|
||||
assert.Equal(t, int64(rc.Error()), int64(tc.result.err))
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ package processor
|
|||
import (
|
||||
"strings"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
)
|
||||
|
||||
|
@ -78,33 +77,3 @@ func combineRuleResponses(imageResponse engineapi.EngineResponse) engineapi.Engi
|
|||
imageResponse.PolicyResponse.Rules = combineRuleResponses
|
||||
return imageResponse
|
||||
}
|
||||
|
||||
func needsNamespaceLabels(rules ...kyvernov1.Rule) bool {
|
||||
for _, p := range rules {
|
||||
if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
|
||||
p.ExcludeResources.ResourceDescription.NamespaceSelector != nil {
|
||||
return true
|
||||
}
|
||||
for _, m := range p.MatchResources.Any {
|
||||
if m.ResourceDescription.NamespaceSelector != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, m := range p.MatchResources.All {
|
||||
if m.ResourceDescription.NamespaceSelector != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, e := range p.ExcludeResources.Any {
|
||||
if e.ResourceDescription.NamespaceSelector != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, e := range p.ExcludeResources.All {
|
||||
if e.ResourceDescription.NamespaceSelector != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -4,9 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
valuesapi "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/values"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/processor"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||
|
||||
"gotest.tools/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
@ -55,76 +53,6 @@ var policyNamespaceSelector = []byte(`{
|
|||
}
|
||||
`)
|
||||
|
||||
func Test_NamespaceSelector(t *testing.T) {
|
||||
type ResultCounts struct {
|
||||
pass int
|
||||
fail int
|
||||
warn int
|
||||
err int
|
||||
skip int
|
||||
}
|
||||
type TestCase struct {
|
||||
policy []byte
|
||||
resource []byte
|
||||
namespaceSelectorMap map[string]map[string]string
|
||||
result ResultCounts
|
||||
}
|
||||
|
||||
testcases := []TestCase{
|
||||
{
|
||||
policy: policyNamespaceSelector,
|
||||
resource: []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"nginx","namespace":"test1"},"spec":{"containers":[{"image":"nginx:latest","name":"test-fail"}]}}`),
|
||||
namespaceSelectorMap: map[string]map[string]string{
|
||||
"test1": {
|
||||
"foo.com/managed-state": "managed",
|
||||
},
|
||||
},
|
||||
result: ResultCounts{
|
||||
pass: 0,
|
||||
fail: 1,
|
||||
warn: 0,
|
||||
err: 0,
|
||||
skip: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
policy: policyNamespaceSelector,
|
||||
resource: []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"test-nginx","namespace":"test1"},"spec":{"containers":[{"image":"nginx:latest","name":"test-pass"}]}}`),
|
||||
namespaceSelectorMap: map[string]map[string]string{
|
||||
"test1": {
|
||||
"foo.com/managed-state": "managed",
|
||||
},
|
||||
},
|
||||
result: ResultCounts{
|
||||
pass: 1,
|
||||
fail: 1,
|
||||
warn: 0,
|
||||
err: 0,
|
||||
skip: 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
rc := &processor.ResultCounts{}
|
||||
for _, tc := range testcases {
|
||||
policyArray, _, _ := yamlutils.GetPolicy(tc.policy)
|
||||
resourceArray, _ := resource.GetUnstructuredResources(tc.resource)
|
||||
processor := processor.PolicyProcessor{
|
||||
Policy: policyArray[0],
|
||||
Resource: resourceArray[0],
|
||||
MutateLogPath: "",
|
||||
UserInfo: nil,
|
||||
NamespaceSelectorMap: tc.namespaceSelectorMap,
|
||||
Rc: rc,
|
||||
}
|
||||
processor.ApplyPolicyOnResource()
|
||||
assert.Equal(t, int64(rc.Pass()), int64(tc.result.pass))
|
||||
assert.Equal(t, int64(rc.Fail()), int64(tc.result.fail))
|
||||
assert.Equal(t, int64(rc.Skip()), int64(tc.result.skip))
|
||||
assert.Equal(t, int64(rc.Warn()), int64(tc.result.warn))
|
||||
assert.Equal(t, int64(rc.Error()), int64(tc.result.err))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_GetGitBranchOrPolicyPaths(t *testing.T) {
|
||||
type TestCase struct {
|
||||
gitBranch string
|
||||
|
|
|
@ -10,11 +10,15 @@ spec:
|
|||
containers:
|
||||
- image: registry.digitalocean.com/runlevl4/nginxasdfasdf:latest
|
||||
name: busybox01
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
args:
|
||||
- sleep
|
||||
- "9999"
|
||||
- image: registry.digitalocean.com/runlevl4/bbbbbbbbbb-ccccc:1.28
|
||||
name: busybox02
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
args:
|
||||
- sleep
|
||||
- "9999"
|
||||
|
|
|
@ -5,3 +5,5 @@ metadata:
|
|||
annotations:
|
||||
key1: "1"
|
||||
key2: "1"
|
||||
labels:
|
||||
color: orange
|
|
@ -3,6 +3,7 @@ kind: Pod
|
|||
metadata:
|
||||
labels:
|
||||
foo: bar
|
||||
color: orange
|
||||
name: same-name-but-diff-namespace
|
||||
namespace: testing
|
||||
spec:
|
||||
|
|
Loading…
Add table
Reference in a new issue