1
0
Fork 0
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:
Sandesh More 2022-10-19 22:09:15 +05:30 committed by GitHub
parent 73712f3738
commit fa178ebd82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 117 additions and 57 deletions

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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

View file

@ -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
}

View file

@ -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
}
}
}