mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-29 10:55:05 +00:00
added apiCalls support in kyverno-apply command (#4938)
Signed-off-by: Sandesh More <sandesh.more@infracloud.io> Signed-off-by: Sandesh More <sandesh.more@infracloud.io>
This commit is contained in:
parent
73712f3738
commit
fa178ebd82
6 changed files with 117 additions and 57 deletions
|
@ -170,7 +170,9 @@ func Command() *cobra.Command {
|
|||
func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, resources []*unstructured.Unstructured, skipInvalidPolicies SkippedInvalidPolicies, pvInfos []policyreport.Info, err error) {
|
||||
store.SetMock(true)
|
||||
store.SetRegistryAccess(c.RegistryAccess)
|
||||
|
||||
if c.Cluster {
|
||||
store.AllowApiCall(true)
|
||||
}
|
||||
fs := memfs.New()
|
||||
|
||||
if c.ValuesFile != "" && c.VariablesString != "" {
|
||||
|
@ -353,8 +355,21 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
|
|||
if err != nil {
|
||||
return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.GetName(), resource.GetName()), err)
|
||||
}
|
||||
|
||||
_, info, err := common.ApplyPolicyOnResource(policy, resource, c.MutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, userInfo, c.PolicyReport, namespaceSelectorMap, c.Stdin, rc, true, nil)
|
||||
applyPolicyConfig := common.ApplyPolicyConfig{
|
||||
Policy: policy,
|
||||
Resource: resource,
|
||||
MutateLogPath: c.MutateLogPath,
|
||||
MutateLogPathIsDir: mutateLogPathIsDir,
|
||||
Variables: thisPolicyResourceValues,
|
||||
UserInfo: userInfo,
|
||||
PolicyReport: c.PolicyReport,
|
||||
NamespaceSelectorMap: namespaceSelectorMap,
|
||||
Stdin: c.Stdin,
|
||||
Rc: rc,
|
||||
PrintPatchResource: true,
|
||||
Client: dClient,
|
||||
}
|
||||
_, info, err := common.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
if err != nil {
|
||||
return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
||||
}
|
||||
|
|
|
@ -1010,8 +1010,19 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, isGit bool,
|
|||
if err != nil {
|
||||
return sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.GetName(), resource.GetName()), err)
|
||||
}
|
||||
|
||||
ers, info, err := common.ApplyPolicyOnResource(policy, resource, "", false, thisPolicyResourceValues, userInfo, true, namespaceSelectorMap, false, &resultCounts, false, ruleToCloneSourceResource)
|
||||
applyPolicyConfig := common.ApplyPolicyConfig{
|
||||
Policy: policy,
|
||||
Resource: resource,
|
||||
MutateLogPath: "",
|
||||
Variables: thisPolicyResourceValues,
|
||||
UserInfo: userInfo,
|
||||
PolicyReport: true,
|
||||
NamespaceSelectorMap: namespaceSelectorMap,
|
||||
Rc: &resultCounts,
|
||||
RuleToCloneSourceResource: ruleToCloneSourceResource,
|
||||
Client: dClient,
|
||||
}
|
||||
ers, info, err := common.ApplyPolicyOnResource(applyPolicyConfig)
|
||||
if err != nil {
|
||||
return sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
||||
}
|
||||
|
|
|
@ -73,6 +73,22 @@ type NamespaceSelector struct {
|
|||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
type ApplyPolicyConfig struct {
|
||||
Policy kyvernov1.PolicyInterface
|
||||
Resource *unstructured.Unstructured
|
||||
MutateLogPath string
|
||||
MutateLogPathIsDir bool
|
||||
Variables map[string]interface{}
|
||||
UserInfo kyvernov1beta1.RequestInfo
|
||||
PolicyReport bool
|
||||
NamespaceSelectorMap map[string]map[string]string
|
||||
Stdin bool
|
||||
Rc *ResultCounts
|
||||
PrintPatchResource bool
|
||||
RuleToCloneSourceResource map[string]string
|
||||
Client dclient.Interface
|
||||
}
|
||||
|
||||
// HasVariables - check for variables in the policy
|
||||
func HasVariables(policy kyvernov1.PolicyInterface) [][]string {
|
||||
policyRaw, _ := json.Marshal(policy)
|
||||
|
@ -395,22 +411,18 @@ func MutatePolicies(policies []kyvernov1.PolicyInterface) ([]kyvernov1.PolicyInt
|
|||
}
|
||||
|
||||
// ApplyPolicyOnResource - function to apply policy on resource
|
||||
func ApplyPolicyOnResource(policy kyvernov1.PolicyInterface, resource *unstructured.Unstructured,
|
||||
mutateLogPath string, mutateLogPathIsDir bool, variables map[string]interface{}, userInfo kyvernov1beta1.RequestInfo, policyReport bool,
|
||||
namespaceSelectorMap map[string]map[string]string, stdin bool, rc *ResultCounts,
|
||||
printPatchResource bool, ruleToCloneSourceResource map[string]string,
|
||||
) ([]*response.EngineResponse, policyreport.Info, error) {
|
||||
func ApplyPolicyOnResource(c ApplyPolicyConfig) ([]*response.EngineResponse, policyreport.Info, error) {
|
||||
var engineResponses []*response.EngineResponse
|
||||
namespaceLabels := make(map[string]string)
|
||||
operationIsDelete := false
|
||||
|
||||
if variables["request.operation"] == "DELETE" {
|
||||
if c.Variables["request.operation"] == "DELETE" {
|
||||
operationIsDelete = true
|
||||
}
|
||||
|
||||
policyWithNamespaceSelector := false
|
||||
OuterLoop:
|
||||
for _, p := range autogen.ComputeRules(policy) {
|
||||
for _, p := range autogen.ComputeRules(c.Policy) {
|
||||
if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
|
||||
p.ExcludeResources.ResourceDescription.NamespaceSelector != nil {
|
||||
policyWithNamespaceSelector = true
|
||||
|
@ -443,17 +455,17 @@ OuterLoop:
|
|||
}
|
||||
|
||||
if policyWithNamespaceSelector {
|
||||
resourceNamespace := resource.GetNamespace()
|
||||
namespaceLabels = namespaceSelectorMap[resource.GetNamespace()]
|
||||
resourceNamespace := c.Resource.GetNamespace()
|
||||
namespaceLabels = c.NamespaceSelectorMap[c.Resource.GetNamespace()]
|
||||
if resourceNamespace != "default" && len(namespaceLabels) < 1 {
|
||||
return engineResponses, policyreport.Info{}, sanitizederror.NewWithError(fmt.Sprintf("failed to get namespace labels for resource %s. use --values-file flag to pass the namespace labels", resource.GetName()), nil)
|
||||
return engineResponses, policyreport.Info{}, 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", resource.GetNamespace(), resource.GetKind(), resource.GetName())
|
||||
log.Log.V(3).Info("applying policy on resource", "policy", policy.GetName(), "resource", resPath)
|
||||
resPath := fmt.Sprintf("%s/%s/%s", c.Resource.GetNamespace(), c.Resource.GetKind(), c.Resource.GetName())
|
||||
log.Log.V(3).Info("applying policy on resource", "policy", c.Policy.GetName(), "resource", resPath)
|
||||
|
||||
resourceRaw, err := resource.MarshalJSON()
|
||||
resourceRaw, err := c.Resource.MarshalJSON()
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to marshal resource")
|
||||
}
|
||||
|
@ -474,14 +486,14 @@ OuterLoop:
|
|||
log.Log.Error(err, "failed to load resource in context")
|
||||
}
|
||||
|
||||
for key, value := range variables {
|
||||
for key, value := range c.Variables {
|
||||
err = ctx.AddVariable(key, value)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to add variable to context")
|
||||
}
|
||||
}
|
||||
|
||||
if err := ctx.AddImageInfos(resource); err != nil {
|
||||
if err := ctx.AddImageInfos(c.Resource); err != nil {
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to add image variables to context")
|
||||
}
|
||||
|
@ -492,11 +504,12 @@ OuterLoop:
|
|||
}
|
||||
|
||||
policyContext := &engine.PolicyContext{
|
||||
Policy: policy,
|
||||
Policy: c.Policy,
|
||||
NewResource: *updatedResource,
|
||||
JSONContext: ctx,
|
||||
NamespaceLabels: namespaceLabels,
|
||||
AdmissionInfo: userInfo,
|
||||
AdmissionInfo: c.UserInfo,
|
||||
Client: c.Client,
|
||||
}
|
||||
|
||||
mutateResponse := engine.Mutate(policyContext)
|
||||
|
@ -504,25 +517,25 @@ OuterLoop:
|
|||
engineResponses = append(engineResponses, mutateResponse)
|
||||
}
|
||||
|
||||
err = processMutateEngineResponse(policy, mutateResponse, resPath, rc, mutateLogPath, stdin, mutateLogPathIsDir, resource.GetName(), printPatchResource)
|
||||
err = processMutateEngineResponse(c, mutateResponse, resPath)
|
||||
if err != nil {
|
||||
if !sanitizederror.IsErrorSanitized(err) {
|
||||
return engineResponses, policyreport.Info{}, sanitizederror.NewWithError("failed to print mutated result", err)
|
||||
}
|
||||
}
|
||||
|
||||
if resource.GetKind() == "Pod" && len(resource.GetOwnerReferences()) > 0 {
|
||||
if policy.HasAutoGenAnnotation() {
|
||||
annotations := policy.GetAnnotations()
|
||||
if c.Resource.GetKind() == "Pod" && len(c.Resource.GetOwnerReferences()) > 0 {
|
||||
if c.Policy.HasAutoGenAnnotation() {
|
||||
annotations := c.Policy.GetAnnotations()
|
||||
if _, ok := annotations[kyvernov1.PodControllersAnnotation]; ok {
|
||||
delete(annotations, kyvernov1.PodControllersAnnotation)
|
||||
policy.SetAnnotations(annotations)
|
||||
c.Policy.SetAnnotations(annotations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var policyHasValidate bool
|
||||
for _, rule := range autogen.ComputeRules(policy) {
|
||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
||||
if rule.HasValidate() || rule.HasImagesValidationChecks() {
|
||||
policyHasValidate = true
|
||||
}
|
||||
|
@ -534,7 +547,7 @@ OuterLoop:
|
|||
var validateResponse *response.EngineResponse
|
||||
if policyHasValidate {
|
||||
validateResponse = engine.Validate(policyContext)
|
||||
info = ProcessValidateEngineResponse(policy, validateResponse, resPath, rc, policyReport)
|
||||
info = ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport)
|
||||
}
|
||||
|
||||
if validateResponse != nil && !validateResponse.IsEmpty() {
|
||||
|
@ -544,11 +557,11 @@ OuterLoop:
|
|||
verifyImageResponse, _ := engine.VerifyAndPatchImages(policyContext)
|
||||
if verifyImageResponse != nil && !verifyImageResponse.IsEmpty() {
|
||||
engineResponses = append(engineResponses, verifyImageResponse)
|
||||
info = ProcessValidateEngineResponse(policy, verifyImageResponse, resPath, rc, policyReport)
|
||||
info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport)
|
||||
}
|
||||
|
||||
var policyHasGenerate bool
|
||||
for _, rule := range autogen.ComputeRules(policy) {
|
||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
||||
if rule.HasGenerate() {
|
||||
policyHasGenerate = true
|
||||
}
|
||||
|
@ -556,8 +569,8 @@ OuterLoop:
|
|||
|
||||
if policyHasGenerate {
|
||||
policyContext := &engine.PolicyContext{
|
||||
NewResource: *resource,
|
||||
Policy: policy,
|
||||
NewResource: *c.Resource,
|
||||
Policy: c.Policy,
|
||||
ExcludeGroupRole: []string{},
|
||||
ExcludeResourceFunc: func(s1, s2, s3 string) bool {
|
||||
return false
|
||||
|
@ -567,7 +580,7 @@ OuterLoop:
|
|||
}
|
||||
generateResponse := engine.ApplyBackgroundChecks(policyContext)
|
||||
if generateResponse != nil && !generateResponse.IsEmpty() {
|
||||
newRuleResponse, err := handleGeneratePolicy(generateResponse, *policyContext, ruleToCloneSourceResource)
|
||||
newRuleResponse, err := handleGeneratePolicy(generateResponse, *policyContext, c.RuleToCloneSourceResource)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "failed to apply generate policy")
|
||||
} else {
|
||||
|
@ -575,7 +588,7 @@ OuterLoop:
|
|||
}
|
||||
engineResponses = append(engineResponses, generateResponse)
|
||||
}
|
||||
updateResultCounts(policy, generateResponse, resPath, rc)
|
||||
updateResultCounts(c.Policy, generateResponse, resPath, c.Rc)
|
||||
}
|
||||
|
||||
return engineResponses, info, nil
|
||||
|
@ -879,9 +892,9 @@ func SetInStoreContext(mutatedPolicies []kyvernov1.PolicyInterface, variables ma
|
|||
return variables
|
||||
}
|
||||
|
||||
func processMutateEngineResponse(policy kyvernov1.PolicyInterface, mutateResponse *response.EngineResponse, resPath string, rc *ResultCounts, mutateLogPath string, stdin bool, mutateLogPathIsDir bool, resourceName string, printPatchResource bool) error {
|
||||
func processMutateEngineResponse(c ApplyPolicyConfig, mutateResponse *response.EngineResponse, resPath string) error {
|
||||
var policyHasMutate bool
|
||||
for _, rule := range autogen.ComputeRules(policy) {
|
||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
||||
if rule.HasMutate() {
|
||||
policyHasMutate = true
|
||||
}
|
||||
|
@ -892,52 +905,52 @@ func processMutateEngineResponse(policy kyvernov1.PolicyInterface, mutateRespons
|
|||
|
||||
printCount := 0
|
||||
printMutatedRes := false
|
||||
for _, policyRule := range autogen.ComputeRules(policy) {
|
||||
for _, policyRule := range autogen.ComputeRules(c.Policy) {
|
||||
ruleFoundInEngineResponse := false
|
||||
for i, mutateResponseRule := range mutateResponse.PolicyResponse.Rules {
|
||||
if policyRule.Name == mutateResponseRule.Name {
|
||||
ruleFoundInEngineResponse = true
|
||||
if mutateResponseRule.Status == response.RuleStatusPass {
|
||||
rc.Pass++
|
||||
c.Rc.Pass++
|
||||
printMutatedRes = true
|
||||
} else if mutateResponseRule.Status == response.RuleStatusSkip {
|
||||
fmt.Printf("\nskipped mutate policy %s -> resource %s", policy.GetName(), resPath)
|
||||
rc.Skip++
|
||||
fmt.Printf("\nskipped mutate policy %s -> resource %s", c.Policy.GetName(), resPath)
|
||||
c.Rc.Skip++
|
||||
} else if mutateResponseRule.Status == response.RuleStatusError {
|
||||
fmt.Printf("\nerror while applying mutate policy %s -> resource %s\nerror: %s", policy.GetName(), resPath, mutateResponseRule.Message)
|
||||
rc.Error++
|
||||
fmt.Printf("\nerror while applying mutate policy %s -> resource %s\nerror: %s", c.Policy.GetName(), resPath, mutateResponseRule.Message)
|
||||
c.Rc.Error++
|
||||
} else {
|
||||
if printCount < 1 {
|
||||
fmt.Printf("\nfailed to apply mutate policy %s -> resource %s", policy.GetName(), resPath)
|
||||
fmt.Printf("\nfailed to apply mutate policy %s -> resource %s", c.Policy.GetName(), resPath)
|
||||
printCount++
|
||||
}
|
||||
fmt.Printf("%d. %s - %s \n", i+1, mutateResponseRule.Name, mutateResponseRule.Message)
|
||||
rc.Fail++
|
||||
c.Rc.Fail++
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !ruleFoundInEngineResponse {
|
||||
rc.Skip++
|
||||
c.Rc.Skip++
|
||||
}
|
||||
}
|
||||
|
||||
if printMutatedRes && printPatchResource {
|
||||
if printMutatedRes && c.PrintPatchResource {
|
||||
yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object)
|
||||
if err != nil {
|
||||
return sanitizederror.NewWithError("failed to marshal", err)
|
||||
}
|
||||
|
||||
if mutateLogPath == "" {
|
||||
if c.MutateLogPath == "" {
|
||||
mutatedResource := string(yamlEncodedResource) + string("\n---")
|
||||
if len(strings.TrimSpace(mutatedResource)) > 0 {
|
||||
if !stdin {
|
||||
fmt.Printf("\nmutate policy %s applied to %s:", policy.GetName(), resPath)
|
||||
if !c.Stdin {
|
||||
fmt.Printf("\nmutate policy %s applied to %s:", c.Policy.GetName(), resPath)
|
||||
}
|
||||
fmt.Printf("\n" + mutatedResource + "\n")
|
||||
}
|
||||
} else {
|
||||
err := PrintMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resourceName+"-mutated")
|
||||
err := PrintMutatedOutput(c.MutateLogPath, c.MutateLogPathIsDir, string(yamlEncodedResource), c.Resource.GetName()+"-mutated")
|
||||
if err != nil {
|
||||
return sanitizederror.NewWithError("failed to print mutated result", err)
|
||||
}
|
||||
|
|
|
@ -95,12 +95,19 @@ func Test_NamespaceSelector(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
rc := &ResultCounts{}
|
||||
for _, tc := range testcases {
|
||||
policyArray, _ := yamlutils.GetPolicy(tc.policy)
|
||||
resourceArray, _ := GetResource(tc.resource)
|
||||
ApplyPolicyOnResource(policyArray[0], resourceArray[0], "", false, nil, v1beta1.RequestInfo{}, false, tc.namespaceSelectorMap, false, rc, false, nil)
|
||||
applyPolicyConfig := ApplyPolicyConfig{
|
||||
Policy: policyArray[0],
|
||||
Resource: resourceArray[0],
|
||||
MutateLogPath: "",
|
||||
UserInfo: v1beta1.RequestInfo{},
|
||||
NamespaceSelectorMap: tc.namespaceSelectorMap,
|
||||
Rc: rc,
|
||||
}
|
||||
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
|
||||
|
|
|
@ -6,10 +6,12 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
Mock, RegistryAccess bool
|
||||
ContextVar Context
|
||||
ForeachElement int
|
||||
Subjects Subject
|
||||
Mock bool
|
||||
RegistryAccess bool
|
||||
AllowApiCalls bool
|
||||
ContextVar Context
|
||||
ForeachElement int
|
||||
Subjects Subject
|
||||
)
|
||||
|
||||
func SetMock(mock bool) {
|
||||
|
@ -95,3 +97,11 @@ func GetSubjects() Subject {
|
|||
type Subject struct {
|
||||
Subject rbacv1.Subject `json:"subject,omitempty" yaml:"subject,omitempty"`
|
||||
}
|
||||
|
||||
func AllowApiCall(allow bool) {
|
||||
AllowApiCalls = allow
|
||||
}
|
||||
|
||||
func IsAllowApiCall() bool {
|
||||
return AllowApiCalls
|
||||
}
|
||||
|
|
|
@ -42,6 +42,10 @@ func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ct
|
|||
if err := loadVariable(logger, entry, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if entry.APICall != nil && store.IsAllowApiCall() {
|
||||
if err := loadAPIData(logger, entry, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue