1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

feat: propagate context through engine (#5639)

* feat: propagate context through engine

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* feat: propagate context through engine

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* feat: propagate context through engine

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* feat: propagate context through engine

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:
Charles-Edouard Brétéché 2022-12-09 14:45:11 +01:00 committed by GitHub
parent 5079790aed
commit ff728d5f2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 420 additions and 368 deletions

View file

@ -458,7 +458,7 @@ OuterLoop:
WithAdmissionInfo(c.UserInfo).
WithClient(c.Client)
mutateResponse := engine.Mutate(registryclient.NewOrDie(), policyContext)
mutateResponse := engine.Mutate(context.Background(), registryclient.NewOrDie(), policyContext)
if mutateResponse != nil {
engineResponses = append(engineResponses, mutateResponse)
}
@ -482,7 +482,7 @@ OuterLoop:
var info Info
var validateResponse *response.EngineResponse
if policyHasValidate {
validateResponse = engine.Validate(registryclient.NewOrDie(), policyContext)
validateResponse = engine.Validate(context.Background(), registryclient.NewOrDie(), policyContext)
info = ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
}
@ -490,7 +490,7 @@ OuterLoop:
engineResponses = append(engineResponses, validateResponse)
}
verifyImageResponse, _ := engine.VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
verifyImageResponse, _ := engine.VerifyAndPatchImages(context.Background(), registryclient.NewOrDie(), policyContext)
if verifyImageResponse != nil && !verifyImageResponse.IsEmpty() {
engineResponses = append(engineResponses, verifyImageResponse)
info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)

View file

@ -62,17 +62,15 @@ const (
resyncPeriod = 15 * time.Minute
)
func setupRegistryClient(logger logr.Logger, lister corev1listers.SecretNamespaceLister, imagePullSecrets string, allowInsecureRegistry bool) (registryclient.Client, error) {
func setupRegistryClient(ctx context.Context, logger logr.Logger, lister corev1listers.SecretNamespaceLister, imagePullSecrets string, allowInsecureRegistry bool) (registryclient.Client, error) {
logger = logger.WithName("registry-client")
logger.Info("setup registry client...", "secrets", imagePullSecrets, "insecure", allowInsecureRegistry)
var registryOptions []registryclient.Option
registryOptions := []registryclient.Option{
registryclient.WithTracing(),
}
secrets := strings.Split(imagePullSecrets, ",")
if imagePullSecrets != "" && len(secrets) > 0 {
registryOptions = append(registryOptions, registryclient.WithKeychainPullSecrets(
context.TODO(),
lister,
secrets...,
))
registryOptions = append(registryOptions, registryclient.WithKeychainPullSecrets(ctx, lister, secrets...))
}
if allowInsecureRegistry {
registryOptions = append(registryOptions, registryclient.WithAllowInsecureRegistry())
@ -415,7 +413,7 @@ func main() {
}
secretLister := kubeKyvernoInformer.Core().V1().Secrets().Lister().Secrets(config.KyvernoNamespace())
// setup registry client
rclient, err := setupRegistryClient(logger, secretLister, imagePullSecrets, allowInsecureRegistry)
rclient, err := setupRegistryClient(signalCtx, logger, secretLister, imagePullSecrets, allowInsecureRegistry)
if err != nil {
logger.Error(err, "failed to setup registry client")
os.Exit(1)

View file

@ -345,7 +345,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
}
// add configmap json data to context
if err := engine.LoadContext(log, c.rclient, rule.Context, policyContext, rule.Name); err != nil {
if err := engine.LoadContext(context.TODO(), log, c.rclient, rule.Context, policyContext, rule.Name); err != nil {
log.Error(err, "cannot add configmaps to context")
return nil, processExisting, err
}

View file

@ -93,7 +93,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e
continue
}
er := engine.Mutate(c.rclient, policyContext)
er := engine.Mutate(context.TODO(), c.rclient, policyContext)
for _, r := range er.PolicyResponse.Rules {
patched := r.PatchedTarget
switch r.Status {

View file

@ -241,7 +241,7 @@ func (c *controller) updateReport(ctx context.Context, meta metav1.Object, gvk s
nsLabels = ns.GetLabels()
}
var responses []*response.EngineResponse
for _, result := range scanner.ScanResource(*resource, nsLabels, backgroundPolicies...) {
for _, result := range scanner.ScanResource(ctx, *resource, nsLabels, backgroundPolicies...) {
if result.Error != nil {
logger.Error(result.Error, "failed to apply policy")
} else {
@ -321,7 +321,7 @@ func (c *controller) updateReport(ctx context.Context, meta metav1.Object, gvk s
}
nsLabels = ns.GetLabels()
}
for _, result := range scanner.ScanResource(*resource, nsLabels, toCreate...) {
for _, result := range scanner.ScanResource(ctx, *resource, nsLabels, toCreate...) {
if result.Error != nil {
return result.Error
} else {

View file

@ -1,11 +1,13 @@
package utils
import (
"context"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/context"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/registryclient"
"go.uber.org/multierr"
@ -25,7 +27,7 @@ type ScanResult struct {
}
type Scanner interface {
ScanResource(unstructured.Unstructured, map[string]string, ...kyvernov1.PolicyInterface) map[kyvernov1.PolicyInterface]ScanResult
ScanResource(context.Context, unstructured.Unstructured, map[string]string, ...kyvernov1.PolicyInterface) map[kyvernov1.PolicyInterface]ScanResult
}
func NewScanner(logger logr.Logger, client dclient.Interface, rclient registryclient.Client, excludeGroupRole ...string) Scanner {
@ -37,18 +39,18 @@ func NewScanner(logger logr.Logger, client dclient.Interface, rclient registrycl
}
}
func (s *scanner) ScanResource(resource unstructured.Unstructured, nsLabels map[string]string, policies ...kyvernov1.PolicyInterface) map[kyvernov1.PolicyInterface]ScanResult {
func (s *scanner) ScanResource(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, policies ...kyvernov1.PolicyInterface) map[kyvernov1.PolicyInterface]ScanResult {
results := map[kyvernov1.PolicyInterface]ScanResult{}
for _, policy := range policies {
var errors []error
response, err := s.validateResource(resource, nsLabels, policy)
response, err := s.validateResource(ctx, resource, nsLabels, policy)
if err != nil {
s.logger.Error(err, "failed to scan resource")
errors = append(errors, err)
}
spec := policy.GetSpec()
if spec.HasVerifyImages() {
ivResponse, err := s.validateImages(resource, nsLabels, policy)
ivResponse, err := s.validateImages(ctx, resource, nsLabels, policy)
if err != nil {
s.logger.Error(err, "failed to scan images")
errors = append(errors, err)
@ -64,50 +66,50 @@ func (s *scanner) ScanResource(resource unstructured.Unstructured, nsLabels map[
return results
}
func (s *scanner) validateResource(resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*response.EngineResponse, error) {
ctx := context.NewContext()
if err := ctx.AddResource(resource.Object); err != nil {
func (s *scanner) validateResource(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*response.EngineResponse, error) {
enginectx := enginecontext.NewContext()
if err := enginectx.AddResource(resource.Object); err != nil {
return nil, err
}
if err := ctx.AddNamespace(resource.GetNamespace()); err != nil {
if err := enginectx.AddNamespace(resource.GetNamespace()); err != nil {
return nil, err
}
if err := ctx.AddImageInfos(&resource); err != nil {
if err := enginectx.AddImageInfos(&resource); err != nil {
return nil, err
}
if err := ctx.AddOperation("CREATE"); err != nil {
if err := enginectx.AddOperation("CREATE"); err != nil {
return nil, err
}
policyCtx := engine.NewPolicyContextWithJsonContext(ctx).
policyCtx := engine.NewPolicyContextWithJsonContext(enginectx).
WithNewResource(resource).
WithPolicy(policy).
WithClient(s.client).
WithNamespaceLabels(nsLabels).
WithExcludeGroupRole(s.excludeGroupRole...)
return engine.Validate(s.rclient, policyCtx), nil
return engine.Validate(ctx, s.rclient, policyCtx), nil
}
func (s *scanner) validateImages(resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*response.EngineResponse, error) {
ctx := context.NewContext()
if err := ctx.AddResource(resource.Object); err != nil {
func (s *scanner) validateImages(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*response.EngineResponse, error) {
enginectx := enginecontext.NewContext()
if err := enginectx.AddResource(resource.Object); err != nil {
return nil, err
}
if err := ctx.AddNamespace(resource.GetNamespace()); err != nil {
if err := enginectx.AddNamespace(resource.GetNamespace()); err != nil {
return nil, err
}
if err := ctx.AddImageInfos(&resource); err != nil {
if err := enginectx.AddImageInfos(&resource); err != nil {
return nil, err
}
if err := ctx.AddOperation("CREATE"); err != nil {
if err := enginectx.AddOperation("CREATE"); err != nil {
return nil, err
}
policyCtx := engine.NewPolicyContextWithJsonContext(ctx).
policyCtx := engine.NewPolicyContextWithJsonContext(enginectx).
WithNewResource(resource).
WithPolicy(policy).
WithClient(s.client).
WithNamespaceLabels(nsLabels).
WithExcludeGroupRole(s.excludeGroupRole...)
response, _ := engine.VerifyAndPatchImages(s.rclient, policyCtx)
response, _ := engine.VerifyAndPatchImages(ctx, s.rclient, policyCtx)
if len(response.PolicyResponse.Rules) > 0 {
s.logger.Info("validateImages", "policy", policy, "response", response)
}

View file

@ -59,13 +59,13 @@ type Response struct {
type CosignError struct{}
// VerifySignature verifies that the image has the expected signatures
func VerifySignature(rclient registryclient.Client, opts Options) (*Response, error) {
func VerifySignature(ctx context.Context, rclient registryclient.Client, opts Options) (*Response, error) {
ref, err := name.ParseReference(opts.ImageRef)
if err != nil {
return nil, fmt.Errorf("failed to parse image %s", opts.ImageRef)
}
cosignOpts, err := buildCosignOptions(rclient, opts)
cosignOpts, err := buildCosignOptions(ctx, rclient, opts)
if err != nil {
return nil, err
}
@ -75,7 +75,7 @@ func VerifySignature(rclient registryclient.Client, opts Options) (*Response, er
bundleVerified bool
)
tracing.DoInSpan(context.Background(), "cosign", "verify_image_signatures", func(ctx context.Context) {
tracing.DoInSpan(ctx, "cosign", "verify_image_signatures", func(ctx context.Context) {
signatures, bundleVerified, err = client.VerifyImageSignatures(ctx, ref, cosignOpts)
})
@ -110,7 +110,7 @@ func VerifySignature(rclient registryclient.Client, opts Options) (*Response, er
return &Response{Digest: digest}, nil
}
func buildCosignOptions(rclient registryclient.Client, opts Options) (*cosign.CheckOpts, error) {
func buildCosignOptions(ctx context.Context, rclient registryclient.Client, opts Options) (*cosign.CheckOpts, error) {
var remoteOpts []remote.Option
var err error
signatureAlgorithmMap := map[string]crypto.Hash{
@ -119,11 +119,11 @@ func buildCosignOptions(rclient registryclient.Client, opts Options) (*cosign.Ch
"sha512": crypto.SHA512,
}
ro := options.RegistryOptions{}
remoteOpts, err = ro.ClientOpts(context.Background())
remoteOpts, err = ro.ClientOpts(ctx)
if err != nil {
return nil, errors.Wrap(err, "constructing client options")
}
remoteOpts = append(remoteOpts, rclient.BuildRemoteOption(context.TODO()))
remoteOpts = append(remoteOpts, rclient.BuildRemoteOption(ctx))
cosignOpts := &cosign.CheckOpts{
Annotations: map[string]interface{}{},
RegistryClientOpts: remoteOpts,
@ -151,7 +151,7 @@ func buildCosignOptions(rclient registryclient.Client, opts Options) (*cosign.Ch
}
} else {
// this supports Kubernetes secrets and KMS
cosignOpts.SigVerifier, err = sigs.PublicKeyFromKeyRef(context.Background(), opts.Key)
cosignOpts.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, opts.Key)
if err != nil {
return nil, errors.Wrapf(err, "failed to load public key from %s", opts.Key)
}
@ -254,8 +254,8 @@ func loadCertChain(pem []byte) ([]*x509.Certificate, error) {
// FetchAttestations retrieves signed attestations and decodes them into in-toto statements
// https://github.com/in-toto/attestation/blob/main/spec/README.md#statement
func FetchAttestations(rclient registryclient.Client, opts Options) (*Response, error) {
cosignOpts, err := buildCosignOptions(rclient, opts)
func FetchAttestations(ctx context.Context, rclient registryclient.Client, opts Options) (*Response, error) {
cosignOpts, err := buildCosignOptions(ctx, rclient, opts)
if err != nil {
return nil, err
}
@ -268,8 +268,8 @@ func FetchAttestations(rclient registryclient.Client, opts Options) (*Response,
var signatures []oci.Signature
var bundleVerified bool
tracing.DoInSpan(context.Background(), "cosign_operations", "verify_image_signatures", func(ctx context.Context) {
signatures, bundleVerified, err = client.VerifyImageAttestations(context.Background(), ref, cosignOpts)
tracing.DoInSpan(ctx, "cosign_operations", "verify_image_signatures", func(ctx context.Context) {
signatures, bundleVerified, err = client.VerifyImageAttestations(ctx, ref, cosignOpts)
})
if err != nil {

View file

@ -1,6 +1,7 @@
package cosign
import (
"context"
"crypto/x509"
"fmt"
"io"
@ -80,15 +81,15 @@ func TestCosignKeyless(t *testing.T) {
client, err := registryclient.New()
assert.NilError(t, err)
_, err = VerifySignature(client, opts)
_, err = VerifySignature(context.TODO(), client, opts)
assert.ErrorContains(t, err, "subject mismatch: expected jim, received jim@nirmata.com")
opts.Subject = "jim@nirmata.com"
_, err = VerifySignature(client, opts)
_, err = VerifySignature(context.TODO(), client, opts)
assert.ErrorContains(t, err, "issuer mismatch: expected https://github.com/, received https://github.com/login/oauth")
opts.Issuer = "https://github.com/login/oauth"
_, err = VerifySignature(client, opts)
_, err = VerifySignature(context.TODO(), client, opts)
assert.NilError(t, err)
}

View file

@ -1,6 +1,7 @@
package engine
import (
"context"
"time"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
@ -109,7 +110,7 @@ func filterRule(rclient registryclient.Client, rule kyvernov1.Rule, policyContex
policyContext.jsonContext.Checkpoint()
defer policyContext.jsonContext.Restore()
if err = LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
if err = LoadContext(context.TODO(), logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
return nil
}

View file

@ -63,7 +63,11 @@ func extractMatchingImages(policyContext *PolicyContext, rule *kyvernov1.Rule) (
return matchingImages, imageRefs, nil
}
func VerifyAndPatchImages(rclient registryclient.Client, policyContext *PolicyContext) (*response.EngineResponse, *ImageVerificationMetadata) {
func VerifyAndPatchImages(
ctx context.Context,
rclient registryclient.Client,
policyContext *PolicyContext,
) (*response.EngineResponse, *ImageVerificationMetadata) {
resp := &response.EngineResponse{}
policy := policyContext.policy
@ -112,7 +116,7 @@ func VerifyAndPatchImages(rclient registryclient.Client, policyContext *PolicyCo
}
policyContext.jsonContext.Restore()
if err := LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
if err := LoadContext(ctx, logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
appendResponse(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), response.RuleStatusError)
continue
}
@ -133,7 +137,7 @@ func VerifyAndPatchImages(rclient registryclient.Client, policyContext *PolicyCo
}
for _, imageVerify := range ruleCopy.VerifyImages {
iv.verify(imageVerify, ruleImages)
iv.verify(ctx, imageVerify, ruleImages)
}
if applyRules == kyvernov1.ApplyOne && resp.PolicyResponse.RulesAppliedCount > 0 {
@ -182,7 +186,7 @@ type imageVerifier struct {
// verify applies policy rules to each matching image. The policy rule results and annotation patches are
// added to tme imageVerifier `resp` and `ivm` fields.
func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matchedImageInfos []apiutils.ImageInfo) {
func (iv *imageVerifier) verify(ctx context.Context, imageVerify kyvernov1.ImageVerification, matchedImageInfos []apiutils.ImageInfo) {
// for backward compatibility
imageVerify = *imageVerify.Convert()
@ -211,10 +215,10 @@ func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matched
continue
}
ruleResp, digest := iv.verifyImage(imageVerify, imageInfo)
ruleResp, digest := iv.verifyImage(ctx, imageVerify, imageInfo)
if imageVerify.MutateDigest {
patch, retrievedDigest, err := iv.handleMutateDigest(digest, imageInfo)
patch, retrievedDigest, err := iv.handleMutateDigest(ctx, digest, imageInfo)
if err != nil {
ruleResp = ruleError(iv.rule, response.ImageVerify, "failed to update digest", err)
} else if patch != nil {
@ -240,13 +244,13 @@ func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matched
}
}
func (iv *imageVerifier) handleMutateDigest(digest string, imageInfo apiutils.ImageInfo) ([]byte, string, error) {
func (iv *imageVerifier) handleMutateDigest(ctx context.Context, digest string, imageInfo apiutils.ImageInfo) ([]byte, string, error) {
if imageInfo.Digest != "" {
return nil, "", nil
}
if digest == "" {
desc, err := iv.rclient.FetchImageDescriptor(context.TODO(), imageInfo.String())
desc, err := iv.rclient.FetchImageDescriptor(ctx, imageInfo.String())
if err != nil {
return nil, "", err
}
@ -290,7 +294,11 @@ func imageMatches(image string, imagePatterns []string) bool {
return false
}
func (iv *imageVerifier) verifyImage(imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) {
func (iv *imageVerifier) verifyImage(
ctx context.Context,
imageVerify kyvernov1.ImageVerification,
imageInfo apiutils.ImageInfo,
) (*response.RuleResponse, string) {
if len(imageVerify.Attestors) <= 0 && len(imageVerify.Attestations) <= 0 {
return nil, ""
}
@ -306,17 +314,21 @@ func (iv *imageVerifier) verifyImage(imageVerify kyvernov1.ImageVerification, im
}
if len(imageVerify.Attestors) > 0 {
ruleResp, _, _ := iv.verifyAttestors(imageVerify.Attestors, imageVerify, imageInfo, "")
ruleResp, _, _ := iv.verifyAttestors(ctx, imageVerify.Attestors, imageVerify, imageInfo, "")
if ruleResp.Status != response.RuleStatusPass {
return ruleResp, ""
}
}
return iv.verifyAttestations(imageVerify, imageInfo)
return iv.verifyAttestations(ctx, imageVerify, imageInfo)
}
func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification,
imageInfo apiutils.ImageInfo, predicateType string,
func (iv *imageVerifier) verifyAttestors(
ctx context.Context,
attestors []kyvernov1.AttestorSet,
imageVerify kyvernov1.ImageVerification,
imageInfo apiutils.ImageInfo,
predicateType string,
) (*response.RuleResponse, *cosign.Response, []kyvernov1.AttestorSet) {
var cosignResponse *cosign.Response
var newAttestors []kyvernov1.AttestorSet
@ -326,7 +338,7 @@ func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imag
var err error
path := fmt.Sprintf(".attestors[%d]", i)
iv.logger.V(4).Info("verifying attestors", "path", path)
cosignResponse, err = iv.verifyAttestorSet(attestorSet, imageVerify, imageInfo, path, predicateType)
cosignResponse, err = iv.verifyAttestorSet(ctx, attestorSet, imageVerify, imageInfo, path, predicateType)
if err != nil {
iv.logger.Error(err, "failed to verify image")
msg := fmt.Sprintf("failed to verify image %s: %s", image, err.Error())
@ -350,7 +362,11 @@ func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imag
return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusPass, nil), cosignResponse, newAttestors
}
func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) {
func (iv *imageVerifier) verifyAttestations(
ctx context.Context,
imageVerify kyvernov1.ImageVerification,
imageInfo apiutils.ImageInfo,
) (*response.RuleResponse, string) {
image := imageInfo.String()
for i, attestation := range imageVerify.Attestations {
var attestationError error
@ -375,7 +391,7 @@ func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerificat
for _, a := range entries {
entryPath := fmt.Sprintf("%s.entries[%d]", attestorPath, i)
opts, subPath := iv.buildOptionsAndPath(a, imageVerify, image, attestation)
cosignResp, err := cosign.FetchAttestations(iv.rclient, *opts)
cosignResp, err := cosign.FetchAttestations(ctx, iv.rclient, *opts)
if err != nil {
iv.logger.Error(err, "failed to fetch attestations")
msg := fmt.Sprintf("failed to fetch attestations %s: %s", image, err.Error())
@ -410,8 +426,13 @@ func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerificat
return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusPass, nil), ""
}
func (iv *imageVerifier) verifyAttestorSet(attestorSet kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification,
imageInfo apiutils.ImageInfo, path, predicateType string,
func (iv *imageVerifier) verifyAttestorSet(
ctx context.Context,
attestorSet kyvernov1.AttestorSet,
imageVerify kyvernov1.ImageVerification,
imageInfo apiutils.ImageInfo,
path string,
predicateType string,
) (*cosign.Response, error) {
var errorList []error
verifiedCount := 0
@ -431,11 +452,11 @@ func (iv *imageVerifier) verifyAttestorSet(attestorSet kyvernov1.AttestorSet, im
entryError = errors.Wrapf(err, "failed to unmarshal nested attestor %s", attestorPath)
} else {
attestorPath += ".attestor"
cosignResp, entryError = iv.verifyAttestorSet(*nestedAttestorSet, imageVerify, imageInfo, attestorPath, predicateType)
cosignResp, entryError = iv.verifyAttestorSet(ctx, *nestedAttestorSet, imageVerify, imageInfo, attestorPath, predicateType)
}
} else {
opts, subPath := iv.buildOptionsAndPath(a, imageVerify, image, kyvernov1.Attestation{PredicateType: predicateType})
cosignResp, entryError = cosign.VerifySignature(iv.rclient, *opts)
cosignResp, entryError = cosign.VerifySignature(ctx, iv.rclient, *opts)
if entryError != nil {
entryError = errors.Wrapf(entryError, attestorPath+subPath)
}

View file

@ -1,6 +1,7 @@
package engine
import (
"context"
"fmt"
"reflect"
@ -14,20 +15,20 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func processImageValidationRule(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse {
if isDeleteRequest(ctx) {
func processImageValidationRule(ctx context.Context, log logr.Logger, rclient registryclient.Client, enginectx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse {
if isDeleteRequest(enginectx) {
return nil
}
log = log.WithValues("rule", rule.Name)
matchingImages, _, err := extractMatchingImages(ctx, rule)
matchingImages, _, err := extractMatchingImages(enginectx, rule)
if err != nil {
return ruleResponse(*rule, response.Validation, err.Error(), response.RuleStatusError, nil)
}
if len(matchingImages) == 0 {
return ruleResponse(*rule, response.Validation, "image verified", response.RuleStatusSkip, nil)
}
if err := LoadContext(log, rclient, rule.Context, ctx, rule.Name); err != nil {
if err := LoadContext(ctx, log, rclient, rule.Context, enginectx, rule.Name); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
log.V(3).Info("failed to load context", "reason", err.Error())
} else {
@ -37,13 +38,13 @@ func processImageValidationRule(log logr.Logger, rclient registryclient.Client,
return ruleError(rule, response.Validation, "failed to load context", err)
}
preconditionsPassed, err := checkPreconditions(log, ctx, rule.RawAnyAllConditions)
preconditionsPassed, err := checkPreconditions(log, enginectx, rule.RawAnyAllConditions)
if err != nil {
return ruleError(rule, response.Validation, "failed to evaluate preconditions", err)
}
if !preconditionsPassed {
if ctx.policy.GetSpec().ValidationFailureAction.Audit() {
if enginectx.policy.GetSpec().ValidationFailureAction.Audit() {
return nil
}
@ -52,7 +53,7 @@ func processImageValidationRule(log logr.Logger, rclient registryclient.Client,
for _, v := range rule.VerifyImages {
imageVerify := v.Convert()
for _, infoMap := range ctx.jsonContext.ImageInfo() {
for _, infoMap := range enginectx.jsonContext.ImageInfo() {
for name, imageInfo := range infoMap {
image := imageInfo.String()
log = log.WithValues("rule", rule.Name)
@ -63,7 +64,7 @@ func processImageValidationRule(log logr.Logger, rclient registryclient.Client,
}
log.V(4).Info("validating image", "image", image)
if err := validateImage(ctx, imageVerify, name, imageInfo, log); err != nil {
if err := validateImage(enginectx, imageVerify, name, imageInfo, log); err != nil {
return ruleResponse(*rule, response.ImageVerify, err.Error(), response.RuleStatusFail, nil)
}
}

View file

@ -1,6 +1,7 @@
package engine
import (
"context"
"encoding/json"
"fmt"
"strings"
@ -13,7 +14,7 @@ import (
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/cosign"
"github.com/kyverno/kyverno/pkg/engine/context"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/context/resolvers"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils"
@ -150,7 +151,7 @@ func Test_CosignMockAttest(t *testing.T) {
err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads)
assert.NilError(t, err)
er, ivm := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
er, ivm := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass,
fmt.Sprintf("expected: %v, got: %v, failure: %v",
@ -164,7 +165,7 @@ func Test_CosignMockAttest_fail(t *testing.T) {
err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads)
assert.NilError(t, err)
er, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
er, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
}
@ -178,8 +179,8 @@ func buildContext(t *testing.T, policy, resource string, oldResource string) *Po
resourceUnstructured, err := utils.ConvertToUnstructured([]byte(resource))
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, []byte(resource))
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, []byte(resource))
assert.NilError(t, err)
policyContext := &PolicyContext{
@ -192,7 +193,7 @@ func buildContext(t *testing.T, policy, resource string, oldResource string) *Po
oldResourceUnstructured, err := utils.ConvertToUnstructured([]byte(oldResource))
assert.NilError(t, err)
err = context.AddOldResource(ctx, []byte(oldResource))
err = enginecontext.AddOldResource(ctx, []byte(oldResource))
assert.NilError(t, err)
policyContext.oldResource = *oldResourceUnstructured
@ -412,7 +413,7 @@ var testOtherKey = `-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
func Test_ConfigMapMissingSuccess(t *testing.T) {
policyContext := buildContext(t, testConfigMapMissing, testConfigMapMissingResource, "")
cosign.ClearMock()
err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusSkip, err.PolicyResponse.Rules[0].Message)
}
@ -424,7 +425,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) {
assert.NilError(t, err)
policyContext.informerCacheResolvers = resolver
cosign.ClearMock()
resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusError, resp.PolicyResponse.Rules[0].Message)
}
@ -432,7 +433,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) {
func Test_SignatureGoodSigned(t *testing.T) {
policyContext := buildContext(t, testSampleSingleKeyPolicy, testSampleResource, "")
cosign.ClearMock()
err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message)
}
@ -441,7 +442,7 @@ func Test_SignatureUnsigned(t *testing.T) {
cosign.ClearMock()
unsigned := strings.Replace(testSampleResource, ":signed", ":unsigned", -1)
policyContext := buildContext(t, testSampleSingleKeyPolicy, unsigned, "")
err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message)
}
@ -450,7 +451,7 @@ func Test_SignatureWrongKey(t *testing.T) {
cosign.ClearMock()
otherKey := strings.Replace(testSampleResource, ":signed", ":signed-by-someone-else", -1)
policyContext := buildContext(t, testSampleSingleKeyPolicy, otherKey, "")
err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message)
}
@ -461,7 +462,7 @@ func Test_SignaturesMultiKey(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1)
policy = strings.Replace(policy, "COUNT", "0", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message)
}
@ -471,7 +472,7 @@ func Test_SignaturesMultiKeyFail(t *testing.T) {
policy := strings.Replace(testSampleMultipleKeyPolicy, "KEY1", testVerifyImageKey, -1)
policy = strings.Replace(policy, "COUNT", "0", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message)
}
@ -482,7 +483,7 @@ func Test_SignaturesMultiKeyOneGoodKey(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
policy = strings.Replace(policy, "COUNT", "1", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message)
}
@ -493,7 +494,7 @@ func Test_SignaturesMultiKeyZeroGoodKey(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
policy = strings.Replace(policy, "COUNT", "1", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusFail, resp.PolicyResponse.Rules[0].Message)
}
@ -509,14 +510,14 @@ func Test_RuleSelectorImageVerify(t *testing.T) {
applyAll := kyverno.ApplyAll
spec.ApplyRules = &applyAll
resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(resp.PolicyResponse.Rules), 2)
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusPass, resp.PolicyResponse.Rules[0].Message)
assert.Equal(t, resp.PolicyResponse.Rules[1].Status, response.RuleStatusFail, resp.PolicyResponse.Rules[1].Message)
applyOne := kyverno.ApplyOne
spec.ApplyRules = &applyOne
resp, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
resp, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusPass, resp.PolicyResponse.Rules[0].Message)
}
@ -620,7 +621,7 @@ func Test_NestedAttestors(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1)
policy = strings.Replace(policy, "COUNT", "0", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@ -628,7 +629,7 @@ func Test_NestedAttestors(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
policy = strings.Replace(policy, "COUNT", "0", -1)
policyContext = buildContext(t, policy, testSampleResource, "")
err, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
@ -636,7 +637,7 @@ func Test_NestedAttestors(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
policy = strings.Replace(policy, "COUNT", "1", -1)
policyContext = buildContext(t, policy, testSampleResource, "")
err, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
err, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
}
@ -729,7 +730,7 @@ func Test_MarkImageVerified(t *testing.T) {
err := cosign.SetMock(image, attestationPayloads)
assert.NilError(t, err)
engineResponse, verifiedImages := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
engineResponse, verifiedImages := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Assert(t, engineResponse != nil)
assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1)
assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@ -822,7 +823,7 @@ func Test_ParsePEMDelimited(t *testing.T) {
err := cosign.SetMock(image, signaturePayloads)
assert.NilError(t, err)
engineResponse, verifiedImages := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
engineResponse, verifiedImages := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Assert(t, engineResponse != nil)
assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1)
assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, response.RuleStatusPass)

View file

@ -14,18 +14,18 @@ import (
)
// LoadContext - Fetches and adds external data to the Context.
func LoadContext(logger logr.Logger, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, ctx *PolicyContext, ruleName string) error {
func LoadContext(ctx context.Context, logger logr.Logger, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, enginectx *PolicyContext, ruleName string) error {
if len(contextEntries) == 0 {
return nil
}
policyName := ctx.policy.GetName()
policyName := enginectx.policy.GetName()
if store.GetMock() {
rule := store.GetPolicyRuleFromContext(policyName, ruleName)
if rule != nil && len(rule.Values) > 0 {
variables := rule.Values
for key, value := range variables {
if err := ctx.jsonContext.AddVariable(key, value); err != nil {
if err := enginectx.jsonContext.AddVariable(key, value); err != nil {
return err
}
}
@ -37,15 +37,15 @@ func LoadContext(logger logr.Logger, rclient registryclient.Client, contextEntri
for _, entry := range contextEntries {
if entry.ImageRegistry != nil && hasRegistryAccess {
rclient := store.GetRegistryClient()
if err := loadImageData(rclient, logger, entry, ctx); err != nil {
if err := loadImageData(ctx, rclient, logger, entry, enginectx); err != nil {
return err
}
} else if entry.Variable != nil {
if err := loadVariable(logger, entry, ctx); err != nil {
if err := loadVariable(logger, entry, enginectx); err != nil {
return err
}
} else if entry.APICall != nil && store.IsAllowApiCall() {
if err := loadAPIData(logger, entry, ctx); err != nil {
if err := loadAPIData(ctx, logger, entry, enginectx); err != nil {
return err
}
}
@ -53,7 +53,7 @@ func LoadContext(logger logr.Logger, rclient registryclient.Client, contextEntri
if rule != nil && len(rule.ForeachValues) > 0 {
for key, value := range rule.ForeachValues {
if err := ctx.jsonContext.AddVariable(key, value[store.ForeachElement]); err != nil {
if err := enginectx.jsonContext.AddVariable(key, value[store.ForeachElement]); err != nil {
return err
}
}
@ -61,19 +61,19 @@ func LoadContext(logger logr.Logger, rclient registryclient.Client, contextEntri
} else {
for _, entry := range contextEntries {
if entry.ConfigMap != nil {
if err := loadConfigMap(logger, entry, ctx); err != nil {
if err := loadConfigMap(ctx, logger, entry, enginectx); err != nil {
return err
}
} else if entry.APICall != nil {
if err := loadAPIData(logger, entry, ctx); err != nil {
if err := loadAPIData(ctx, logger, entry, enginectx); err != nil {
return err
}
} else if entry.ImageRegistry != nil {
if err := loadImageData(rclient, logger, entry, ctx); err != nil {
if err := loadImageData(ctx, rclient, logger, entry, enginectx); err != nil {
return err
}
} else if entry.Variable != nil {
if err := loadVariable(logger, entry, ctx); err != nil {
if err := loadVariable(logger, entry, enginectx); err != nil {
return err
}
}
@ -141,8 +141,8 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
}
}
func loadImageData(rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error {
imageData, err := fetchImageData(rclient, logger, entry, ctx)
func loadImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error {
imageData, err := fetchImageData(ctx, rclient, logger, entry, enginectx)
if err != nil {
return err
}
@ -150,14 +150,14 @@ func loadImageData(rclient registryclient.Client, logger logr.Logger, entry kyve
if err != nil {
return err
}
if err := ctx.jsonContext.AddContextEntry(entry.Name, jsonBytes); err != nil {
if err := enginectx.jsonContext.AddContextEntry(entry.Name, jsonBytes); err != nil {
return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err)
}
return nil
}
func fetchImageData(rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (interface{}, error) {
ref, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ImageRegistry.Reference)
func fetchImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) (interface{}, error) {
ref, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ImageRegistry.Reference)
if err != nil {
return nil, fmt.Errorf("ailed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.Reference, err)
}
@ -165,11 +165,11 @@ func fetchImageData(rclient registryclient.Client, logger logr.Logger, entry kyv
if !ok {
return nil, fmt.Errorf("invalid image reference %s, image reference must be a string", ref)
}
path, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ImageRegistry.JMESPath)
path, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ImageRegistry.JMESPath)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.JMESPath, err)
}
imageData, err := fetchImageDataMap(rclient, refString)
imageData, err := fetchImageDataMap(ctx, rclient, refString)
if err != nil {
return nil, err
}
@ -183,8 +183,8 @@ func fetchImageData(rclient registryclient.Client, logger logr.Logger, entry kyv
}
// FetchImageDataMap fetches image information from the remote registry.
func fetchImageDataMap(rclient registryclient.Client, ref string) (interface{}, error) {
desc, err := rclient.FetchImageDescriptor(context.TODO(), ref)
func fetchImageDataMap(ctx context.Context, rclient registryclient.Client, ref string) (interface{}, error) {
desc, err := rclient.FetchImageDescriptor(ctx, ref)
if err != nil {
return nil, err
}
@ -237,14 +237,14 @@ func fetchImageDataMap(rclient registryclient.Client, ref string) (interface{},
return untyped, nil
}
func loadAPIData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error {
jsonData, err := fetchAPIData(logger, entry, ctx)
func loadAPIData(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error {
jsonData, err := fetchAPIData(ctx, logger, entry, enginectx)
if err != nil {
return err
}
if entry.APICall.JMESPath == "" {
err = ctx.jsonContext.AddContextEntry(entry.Name, jsonData)
err = enginectx.jsonContext.AddContextEntry(entry.Name, jsonData)
if err != nil {
return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err)
}
@ -252,7 +252,7 @@ func loadAPIData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCo
return nil
}
path, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.APICall.JMESPath)
path, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.APICall.JMESPath)
if err != nil {
return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.APICall.JMESPath, err)
}
@ -267,7 +267,7 @@ func loadAPIData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCo
return fmt.Errorf("failed to marshall data %v for context entry %v: %v", contextData, entry, err)
}
err = ctx.jsonContext.AddContextEntry(entry.Name, contextData)
err = enginectx.jsonContext.AddContextEntry(entry.Name, contextData)
if err != nil {
return fmt.Errorf("failed to add JMESPath (%s) results to context, error: %v", entry.APICall.JMESPath, err)
}
@ -294,19 +294,19 @@ func applyJMESPathJSON(jmesPath string, jsonData []byte) (interface{}, error) {
return applyJMESPath(jmesPath, data)
}
func fetchAPIData(log logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) ([]byte, error) {
func fetchAPIData(ctx context.Context, log logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) ([]byte, error) {
if entry.APICall == nil {
return nil, fmt.Errorf("missing APICall in context entry %s %v", entry.Name, entry.APICall)
}
path, err := variables.SubstituteAll(log, ctx.jsonContext, entry.APICall.URLPath)
path, err := variables.SubstituteAll(log, enginectx.jsonContext, entry.APICall.URLPath)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.APICall.URLPath, err)
}
pathStr := path.(string)
jsonData, err := getResource(ctx, pathStr)
jsonData, err := getResource(ctx, enginectx, pathStr)
if err != nil {
return nil, fmt.Errorf("failed to get resource with raw url\n: %s: %v", pathStr, err)
}
@ -314,17 +314,17 @@ func fetchAPIData(log logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCont
return jsonData, nil
}
func getResource(ctx *PolicyContext, p string) ([]byte, error) {
return ctx.client.RawAbsPath(context.TODO(), p)
func getResource(ctx context.Context, enginectx *PolicyContext, p string) ([]byte, error) {
return enginectx.client.RawAbsPath(ctx, p)
}
func loadConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error {
data, err := fetchConfigMap(logger, entry, ctx)
func loadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error {
data, err := fetchConfigMap(ctx, logger, entry, enginectx)
if err != nil {
return fmt.Errorf("failed to retrieve config map for context entry %s: %v", entry.Name, err)
}
err = ctx.jsonContext.AddContextEntry(entry.Name, data)
err = enginectx.jsonContext.AddContextEntry(entry.Name, data)
if err != nil {
return fmt.Errorf("failed to add config map for context entry %s: %v", entry.Name, err)
}
@ -332,15 +332,15 @@ func loadConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Policy
return nil
}
func fetchConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) ([]byte, error) {
func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) ([]byte, error) {
contextData := make(map[string]interface{})
name, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ConfigMap.Name)
name, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ConfigMap.Name)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context %s configMap.name %s: %v", entry.Name, entry.ConfigMap.Name, err)
}
namespace, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ConfigMap.Namespace)
namespace, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ConfigMap.Namespace)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context %s configMap.namespace %s: %v", entry.Name, entry.ConfigMap.Namespace, err)
}
@ -349,7 +349,7 @@ func fetchConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Polic
namespace = "default"
}
obj, err := ctx.informerCacheResolvers.Get(context.TODO(), namespace.(string), name.(string))
obj, err := enginectx.informerCacheResolvers.Get(ctx, namespace.(string), name.(string))
if err != nil {
return nil, fmt.Errorf("failed to get configmap %s/%s : %v", namespace, name, err)
}

View file

@ -1,6 +1,7 @@
package engine
import (
"context"
"fmt"
"reflect"
"time"
@ -18,14 +19,14 @@ import (
)
// Mutate performs mutation. Overlay first and then mutation patches
func Mutate(rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) {
func Mutate(ctx context.Context, rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) {
startTime := time.Now()
policy := policyContext.policy
resp = &response.EngineResponse{
Policy: policy,
}
matchedResource := policyContext.newResource
ctx := policyContext.jsonContext
enginectx := policyContext.jsonContext
var skippedRules []string
logger := logging.WithName("EngineMutate").WithValues("policy", policy.GetName(), "kind", matchedResource.GetKind(),
@ -63,14 +64,14 @@ func Mutate(rclient registryclient.Client, policyContext *PolicyContext) (resp *
resource, err := policyContext.jsonContext.Query("request.object")
policyContext.jsonContext.Reset()
if err == nil && resource != nil {
if err := ctx.AddResource(resource.(map[string]interface{})); err != nil {
if err := enginectx.AddResource(resource.(map[string]interface{})); err != nil {
logger.Error(err, "unable to update resource object")
}
} else {
logger.Error(err, "failed to query resource object")
}
if err := LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
if err := LoadContext(ctx, logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
logger.V(3).Info("failed to load context", "reason", err.Error())
} else {
@ -109,7 +110,7 @@ func Mutate(rclient registryclient.Client, policyContext *PolicyContext) (resp *
logger.V(4).Info("apply rule to resource", "rule", rule.Name, "resource namespace", patchedResource.GetNamespace(), "resource name", patchedResource.GetName())
var ruleResp *response.RuleResponse
if rule.Mutation.ForEachMutation != nil {
ruleResp, patchedResource = mutateForEach(rclient, ruleCopy, policyContext, patchedResource, logger)
ruleResp, patchedResource = mutateForEach(ctx, rclient, ruleCopy, policyContext, patchedResource, logger)
} else {
ruleResp, patchedResource = mutateResource(ruleCopy, policyContext, patchedResource, logger)
}
@ -159,7 +160,7 @@ func mutateResource(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructu
return ruleResp, mutateResp.PatchedResource
}
func mutateForEach(rclient registryclient.Client, rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructured.Unstructured, logger logr.Logger) (*response.RuleResponse, unstructured.Unstructured) {
func mutateForEach(ctx context.Context, rclient registryclient.Client, rule *kyvernov1.Rule, enginectx *PolicyContext, resource unstructured.Unstructured, logger logr.Logger) (*response.RuleResponse, unstructured.Unstructured) {
foreachList := rule.Mutation.ForEachMutation
if foreachList == nil {
return nil, resource
@ -170,12 +171,12 @@ func mutateForEach(rclient registryclient.Client, rule *kyvernov1.Rule, ctx *Pol
allPatches := make([][]byte, 0)
for _, foreach := range foreachList {
if err := LoadContext(logger, rclient, rule.Context, ctx, rule.Name); err != nil {
if err := LoadContext(ctx, logger, rclient, rule.Context, enginectx, rule.Name); err != nil {
logger.Error(err, "failed to load context")
return ruleError(rule, response.Mutation, "failed to load context", err), resource
}
preconditionsPassed, err := checkPreconditions(logger, ctx, rule.GetAnyAllConditions())
preconditionsPassed, err := checkPreconditions(logger, enginectx, rule.GetAnyAllConditions())
if err != nil {
return ruleError(rule, response.Mutation, "failed to evaluate preconditions", err), resource
}
@ -184,13 +185,13 @@ func mutateForEach(rclient registryclient.Client, rule *kyvernov1.Rule, ctx *Pol
return ruleResponse(*rule, response.Mutation, "preconditions not met", response.RuleStatusSkip, &patchedResource), resource
}
elements, err := evaluateList(foreach.List, ctx.jsonContext)
elements, err := evaluateList(foreach.List, enginectx.jsonContext)
if err != nil {
msg := fmt.Sprintf("failed to evaluate list %s", foreach.List)
return ruleError(rule, response.Mutation, msg, err), resource
}
mutateResp := mutateElements(rclient, rule.Name, foreach, ctx, elements, patchedResource, logger)
mutateResp := mutateElements(ctx, rclient, rule.Name, foreach, enginectx, elements, patchedResource, logger)
if mutateResp.Status == response.RuleStatusError {
logger.Error(err, "failed to mutate elements")
return buildRuleResponse(rule, mutateResp, nil), resource
@ -214,9 +215,9 @@ func mutateForEach(rclient registryclient.Client, rule *kyvernov1.Rule, ctx *Pol
return r, patchedResource
}
func mutateElements(rclient registryclient.Client, name string, foreach kyvernov1.ForEachMutation, ctx *PolicyContext, elements []interface{}, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response {
ctx.jsonContext.Checkpoint()
defer ctx.jsonContext.Restore()
func mutateElements(ctx context.Context, rclient registryclient.Client, name string, foreach kyvernov1.ForEachMutation, enginectx *PolicyContext, elements []interface{}, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response {
enginectx.jsonContext.Checkpoint()
defer enginectx.jsonContext.Restore()
patchedResource := resource
var allPatches [][]byte
@ -228,19 +229,19 @@ func mutateElements(rclient registryclient.Client, name string, foreach kyvernov
if e == nil {
continue
}
ctx.jsonContext.Reset()
ctx := ctx.Copy()
enginectx.jsonContext.Reset()
enginectx := enginectx.Copy()
store.SetForeachElement(i)
falseVar := false
if err := addElementToContext(ctx, e, i, &falseVar); err != nil {
if err := addElementToContext(enginectx, e, i, &falseVar); err != nil {
return mutateError(err, fmt.Sprintf("failed to add element to mutate.foreach[%d].context", i))
}
if err := LoadContext(logger, rclient, foreach.Context, ctx, name); err != nil {
if err := LoadContext(ctx, logger, rclient, foreach.Context, enginectx, name); err != nil {
return mutateError(err, fmt.Sprintf("failed to load to mutate.foreach[%d].context", i))
}
preconditionsPassed, err := checkPreconditions(logger, ctx, foreach.AnyAllConditions)
preconditionsPassed, err := checkPreconditions(logger, enginectx, foreach.AnyAllConditions)
if err != nil {
return mutateError(err, fmt.Sprintf("failed to evaluate mutate.foreach[%d].preconditions", i))
}
@ -250,7 +251,7 @@ func mutateElements(rclient registryclient.Client, name string, foreach kyvernov
continue
}
mutateResp := mutate.ForEach(name, foreach, ctx.jsonContext, patchedResource, logger)
mutateResp := mutate.ForEach(name, foreach, enginectx.jsonContext, patchedResource, logger)
if mutateResp.Status == response.RuleStatusFail || mutateResp.Status == response.RuleStatusError {
return mutateResp
}

View file

@ -94,7 +94,7 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) {
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
t.Log(string(expectedPatch))
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
@ -167,7 +167,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path"))
}
@ -264,7 +264,7 @@ func Test_variableSubstitutionCLI(t *testing.T) {
newResource: *resourceUnstructured,
}
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
t.Log(string(expectedPatch))
@ -373,7 +373,7 @@ func Test_chained_rules(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers")
assert.NilError(t, err)
assert.Equal(t, containers[0].(map[string]interface{})["image"], "otherregistry.corp.com/foo/bash:5.0")
@ -461,7 +461,7 @@ func Test_precondition(t *testing.T) {
newResource: *resourceUnstructured,
}
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
t.Log(string(expectedPatch))
t.Log(string(er.PolicyResponse.Rules[0].Patches[0]))
if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) {
@ -558,7 +558,7 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) {
newResource: *resourceUnstructured,
}
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
t.Log(string(expectedPatch))
t.Log(string(er.PolicyResponse.Rules[0].Patches[0]))
if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) {
@ -652,7 +652,7 @@ func Test_foreach(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@ -759,7 +759,7 @@ func Test_foreach_element_mutation(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@ -885,7 +885,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@ -1012,7 +1012,7 @@ func Test_foreach_order_mutation_(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@ -1458,7 +1458,7 @@ func Test_mutate_existing_resources(t *testing.T) {
newResource: *trigger,
}
}
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
for _, rr := range er.PolicyResponse.Rules {
for i, p := range rr.Patches {
@ -1566,7 +1566,7 @@ func Test_RuleSelectorMutate(t *testing.T) {
newResource: *resourceUnstructured,
}
er := Mutate(registryclient.NewOrDie(), policyContext)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 2)
assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches), 1)
@ -1581,7 +1581,7 @@ func Test_RuleSelectorMutate(t *testing.T) {
applyOne := kyverno.ApplyOne
policyContext.policy.GetSpec().ApplyRules = &applyOne
er = Mutate(registryclient.NewOrDie(), policyContext)
er = Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
@ -1948,7 +1948,7 @@ func Test_SpecialCharacters(t *testing.T) {
}
// Mutate and make sure that we got the expected amount of rules.
patches := Mutate(registryclient.NewOrDie(), policyContext).GetPatches()
patches := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext).GetPatches()
if !reflect.DeepEqual(patches, tt.want) {
t.Errorf("Mutate() got patches %s, expected %s", patches, tt.want)
}

View file

@ -1,6 +1,7 @@
package engine
import (
"context"
"encoding/json"
"fmt"
"reflect"
@ -13,7 +14,7 @@ import (
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/engine/common"
"github.com/kyverno/kyverno/pkg/engine/context"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/validate"
"github.com/kyverno/kyverno/pkg/engine/variables"
@ -31,7 +32,7 @@ import (
)
// Validate applies validation rules from policy on the resource
func Validate(rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) {
func Validate(ctx context.Context, rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) {
resp = &response.EngineResponse{}
startTime := time.Now()
@ -42,7 +43,7 @@ func Validate(rclient registryclient.Client, policyContext *PolicyContext) (resp
logger.V(4).Info("finished policy processing", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "validationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
}()
resp = validateResource(logger, rclient, policyContext)
resp = validateResource(ctx, logger, rclient, policyContext)
return
}
@ -89,22 +90,22 @@ func buildResponse(ctx *PolicyContext, resp *response.EngineResponse, startTime
resp.PolicyResponse.PolicyExecutionTimestamp = startTime.Unix()
}
func validateResource(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext) *response.EngineResponse {
func validateResource(ctx context.Context, log logr.Logger, rclient registryclient.Client, enginectx *PolicyContext) *response.EngineResponse {
resp := &response.EngineResponse{}
ctx.jsonContext.Checkpoint()
defer ctx.jsonContext.Restore()
enginectx.jsonContext.Checkpoint()
defer enginectx.jsonContext.Restore()
rules := autogen.ComputeRules(ctx.policy)
rules := autogen.ComputeRules(enginectx.policy)
matchCount := 0
applyRules := ctx.policy.GetSpec().GetApplyRules()
applyRules := enginectx.policy.GetSpec().GetApplyRules()
if ctx.policy.IsNamespaced() {
polNs := ctx.policy.GetNamespace()
if ctx.newResource.Object != nil && (ctx.newResource.GetNamespace() != polNs || ctx.newResource.GetNamespace() == "") {
if enginectx.policy.IsNamespaced() {
polNs := enginectx.policy.GetNamespace()
if enginectx.newResource.Object != nil && (enginectx.newResource.GetNamespace() != polNs || enginectx.newResource.GetNamespace() == "") {
return resp
}
if ctx.oldResource.Object != nil && (ctx.oldResource.GetNamespace() != polNs || ctx.oldResource.GetNamespace() == "") {
if enginectx.oldResource.Object != nil && (enginectx.oldResource.GetNamespace() != polNs || enginectx.oldResource.GetNamespace() == "") {
return resp
}
}
@ -119,21 +120,21 @@ func validateResource(log logr.Logger, rclient registryclient.Client, ctx *Polic
}
log = log.WithValues("rule", rule.Name)
if !matches(log, rule, ctx) {
if !matches(log, rule, enginectx) {
continue
}
log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules)
ctx.jsonContext.Reset()
enginectx.jsonContext.Reset()
startTime := time.Now()
var ruleResp *response.RuleResponse
if hasValidate && !hasYAMLSignatureVerify {
ruleResp = processValidationRule(log, rclient, ctx, rule)
ruleResp = processValidationRule(ctx, log, rclient, enginectx, rule)
} else if hasValidateImage {
ruleResp = processImageValidationRule(log, rclient, ctx, rule)
ruleResp = processImageValidationRule(ctx, log, rclient, enginectx, rule)
} else if hasYAMLSignatureVerify {
ruleResp = processYAMLValidationRule(log, ctx, rule)
ruleResp = processYAMLValidationRule(log, enginectx, rule)
}
if ruleResp != nil {
@ -147,29 +148,29 @@ func validateResource(log logr.Logger, rclient registryclient.Client, ctx *Polic
return resp
}
func validateOldObject(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) (*response.RuleResponse, error) {
ctxCopy := ctx.Copy()
func validateOldObject(ctx context.Context, log logr.Logger, rclient registryclient.Client, enginectx *PolicyContext, rule *kyvernov1.Rule) (*response.RuleResponse, error) {
ctxCopy := enginectx.Copy()
ctxCopy.newResource = *ctxCopy.oldResource.DeepCopy()
ctxCopy.oldResource = unstructured.Unstructured{}
if err := context.ReplaceResource(ctxCopy.jsonContext, ctxCopy.newResource.Object); err != nil {
if err := enginecontext.ReplaceResource(ctxCopy.jsonContext, ctxCopy.newResource.Object); err != nil {
return nil, errors.Wrapf(err, "failed to replace object in the JSON context")
}
if err := context.ReplaceOldResource(ctxCopy.jsonContext, ctxCopy.oldResource.Object); err != nil {
if err := enginecontext.ReplaceOldResource(ctxCopy.jsonContext, ctxCopy.oldResource.Object); err != nil {
return nil, errors.Wrapf(err, "failed to replace old object in the JSON context")
}
return processValidationRule(log, rclient, ctxCopy, rule), nil
return processValidationRule(ctx, log, rclient, ctxCopy, rule), nil
}
func processValidationRule(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse {
v := newValidator(log, rclient, ctx, rule)
func processValidationRule(ctx context.Context, log logr.Logger, rclient registryclient.Client, enginectx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse {
v := newValidator(log, rclient, enginectx, rule)
if rule.Validation.ForEachValidation != nil {
return v.validateForEach()
return v.validateForEach(ctx)
}
return v.validate()
return v.validate(ctx)
}
func addRuleResponse(log logr.Logger, resp *response.EngineResponse, ruleResp *response.RuleResponse, startTime time.Time) {
@ -235,8 +236,8 @@ func newForeachValidator(log logr.Logger, rclient registryclient.Client, foreach
}
}
func (v *validator) validate() *response.RuleResponse {
if err := v.loadContext(); err != nil {
func (v *validator) validate(ctx context.Context) *response.RuleResponse {
if err := v.loadContext(ctx); err != nil {
return ruleError(v.rule, response.Validation, "failed to load context", err)
}
@ -260,7 +261,7 @@ func (v *validator) validate() *response.RuleResponse {
ruleResponse := v.validateResourceWithRule()
if isUpdateRequest(v.ctx) {
priorResp, err := validateOldObject(v.log, v.rclient, v.ctx, v.rule)
priorResp, err := validateOldObject(ctx, v.log, v.rclient, v.ctx, v.rule)
if err != nil {
return ruleError(v.rule, response.Validation, "failed to validate old object", err)
}
@ -285,8 +286,8 @@ func (v *validator) validate() *response.RuleResponse {
return nil
}
func (v *validator) validateForEach() *response.RuleResponse {
if err := v.loadContext(); err != nil {
func (v *validator) validateForEach(ctx context.Context) *response.RuleResponse {
if err := v.loadContext(ctx); err != nil {
return ruleError(v.rule, response.Validation, "failed to load context", err)
}
@ -309,7 +310,7 @@ func (v *validator) validateForEach() *response.RuleResponse {
v.log.V(2).Info("failed to evaluate list", "list", foreach.List, "error", err.Error())
continue
}
resp, count := v.validateElements(foreach, elements, foreach.ElementScope)
resp, count := v.validateElements(ctx, foreach, elements, foreach.ElementScope)
if resp.Status != response.RuleStatusPass {
return resp
}
@ -324,7 +325,7 @@ func (v *validator) validateForEach() *response.RuleResponse {
return ruleResponse(*v.rule, response.Validation, "rule passed", response.RuleStatusPass, nil)
}
func (v *validator) validateElements(foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*response.RuleResponse, int) {
func (v *validator) validateElements(ctx context.Context, foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*response.RuleResponse, int) {
v.ctx.jsonContext.Checkpoint()
defer v.ctx.jsonContext.Restore()
applyCount := 0
@ -336,14 +337,14 @@ func (v *validator) validateElements(foreach kyvernov1.ForEachValidation, elemen
store.SetForeachElement(i)
v.ctx.jsonContext.Reset()
ctx := v.ctx.Copy()
if err := addElementToContext(ctx, e, i, elementScope); err != nil {
enginectx := v.ctx.Copy()
if err := addElementToContext(enginectx, e, i, elementScope); err != nil {
v.log.Error(err, "failed to add element to context")
return ruleError(v.rule, response.Validation, "failed to process foreach", err), applyCount
}
foreachValidator := newForeachValidator(v.log, v.rclient, foreach, v.rule, ctx)
r := foreachValidator.validate()
foreachValidator := newForeachValidator(v.log, v.rclient, foreach, v.rule, enginectx)
r := foreachValidator.validate(ctx)
if r == nil {
v.log.V(2).Info("skip rule due to empty result")
continue
@ -401,8 +402,8 @@ func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, el
return nil
}
func (v *validator) loadContext() error {
if err := LoadContext(v.log, v.rclient, v.contextEntries, v.ctx, v.rule.Name); err != nil {
func (v *validator) loadContext(ctx context.Context) error {
if err := LoadContext(ctx, v.log, v.rclient, v.contextEntries, v.ctx, v.rule.Name); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
v.log.V(3).Info("failed to load context", "reason", err.Error())
} else {

View file

@ -1,6 +1,7 @@
package engine
import (
"context"
"encoding/json"
"strings"
"testing"
@ -8,7 +9,7 @@ import (
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
urkyverno "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
"github.com/kyverno/kyverno/pkg/engine/context"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/registryclient"
@ -132,7 +133,7 @@ func TestValidate_image_tag_fail(t *testing.T) {
"validation error: imagePullPolicy 'Always' required with tag 'latest'. rule validate-latest failed at path /spec/containers/0/imagePullPolicy/",
}
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
for index, r := range er.PolicyResponse.Rules {
assert.Equal(t, r.Message, msgs[index])
}
@ -232,7 +233,7 @@ func TestValidate_image_tag_pass(t *testing.T) {
"validation rule 'validate-tag' passed.",
"validation rule 'validate-latest' passed.",
}
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
for index, r := range er.PolicyResponse.Rules {
assert.Equal(t, r.Message, msgs[index])
}
@ -306,7 +307,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
assert.Assert(t, !er.IsSuccessful())
msgs := []string{"validation error: A namespace is required. rule check-default-namespace[0] failed at path /metadata/namespace/ rule check-default-namespace[1] failed at path /metadata/namespace/"}
@ -389,7 +390,7 @@ func TestValidate_host_network_port(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation error: Host network and port are not allowed. rule validate-host-network-port failed at path /spec/containers/0/ports/0/hostPort/"}
for index, r := range er.PolicyResponse.Rules {
@ -479,7 +480,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation rule 'validate-host-path' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -567,7 +568,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) {
assert.NilError(t, err)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation error: Host path '/var/lib/' is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/path/"}
for index, r := range er.PolicyResponse.Rules {
@ -637,7 +638,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation rule 'pod rule 2' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -710,7 +711,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation rule 'pod rule 2' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -784,7 +785,7 @@ func TestValidate_inequality_List_Processing(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation rule 'pod rule 2' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -864,7 +865,7 @@ func TestValidate_inequality_List_ProcessingBrackets(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation rule 'pod rule 2' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -938,7 +939,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation error: pod: validate run as non root user. rule pod rule 2 failed at path /spec/securityContext/runAsNonRoot/"}
for index, r := range er.PolicyResponse.Rules {
@ -1013,7 +1014,7 @@ func TestValidate_AnchorList_pass(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation rule 'pod image rule' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -1088,7 +1089,7 @@ func TestValidate_AnchorList_fail(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
assert.Assert(t, !er.IsSuccessful())
}
@ -1158,7 +1159,7 @@ func TestValidate_existenceAnchor_fail(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
assert.Assert(t, !er.IsSuccessful())
}
@ -1228,7 +1229,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation rule 'pod image rule' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -1316,7 +1317,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation error: Host path is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/"}
for index, r := range er.PolicyResponse.Rules {
@ -1403,7 +1404,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
msgs := []string{"validation rule 'validate-host-path' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -1471,15 +1472,15 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Validate(registryclient.NewOrDie(), policyContext)
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError)
@ -1564,15 +1565,15 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Validate(registryclient.NewOrDie(), policyContext)
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError)
@ -1625,15 +1626,15 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Validate(registryclient.NewOrDie(), policyContext)
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "validation error: rule not-operator-with-variable-should-alway-fail-validation failed at path /spec/content/")
}
@ -1716,15 +1717,15 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Validate(registryclient.NewOrDie(), policyContext)
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError)
@ -1809,15 +1810,15 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Validate(registryclient.NewOrDie(), policyContext)
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
assert.Equal(t, er.PolicyResponse.Rules[0].Message,
@ -1914,15 +1915,15 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing.
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Validate(registryclient.NewOrDie(), policyContext)
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "The animal cow is not in the allowed list of animals.")
}
@ -1967,15 +1968,15 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(test.resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, test.resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, test.resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Validate(registryclient.NewOrDie(), policyContext)
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
for i, rule := range er.PolicyResponse.Rules {
assert.Equal(t, er.PolicyResponse.Rules[i].Status, test.expectedResults[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedResults[i].String(), er.PolicyResponse.Rules[i].Status.String())
@ -2112,7 +2113,7 @@ func executeTest(t *testing.T, test testCase) {
t.Fatal(err)
}
ctx := context.NewContext()
ctx := enginecontext.NewContext()
err = ctx.AddRequest(request)
if err != nil {
t.Fatal(err)
@ -2141,7 +2142,7 @@ func executeTest(t *testing.T, test testCase) {
jsonContext: ctx,
}
resp := Validate(registryclient.NewOrDie(), pc)
resp := Validate(context.TODO(), registryclient.NewOrDie(), pc)
if resp.IsSuccessful() && test.requestDenied {
t.Errorf("Testcase has failed, policy: %v", policy.Name)
}
@ -2240,7 +2241,7 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) {
msgs := []string{
"restrict pod counts to be no more than 10 on node minikube",
}
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
for index, r := range er.PolicyResponse.Rules {
assert.Equal(t, r.Message, msgs[index])
}
@ -2322,14 +2323,14 @@ func Test_EmptyStringInDenyCondition(t *testing.T) {
err := json.Unmarshal(policyRaw, &policy)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx})
assert.Assert(t, !er.IsSuccessful())
}
@ -2411,14 +2412,14 @@ func Test_StringInDenyCondition(t *testing.T) {
err := json.Unmarshal(policyRaw, &policy)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx})
assert.Assert(t, er.IsSuccessful())
}
@ -2997,15 +2998,15 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string,
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
er := Validate(registryclient.NewOrDie(), policyContext)
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, status)
if msg != "" {
@ -3061,15 +3062,15 @@ func Test_delete_ignore_pattern(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = context.AddResource(ctx, resourceRaw)
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
policyContextCreate := &PolicyContext{
policy: &policy,
jsonContext: ctx,
newResource: *resourceUnstructured}
engineResponseCreate := Validate(registryclient.NewOrDie(), policyContextCreate)
engineResponseCreate := Validate(context.TODO(), registryclient.NewOrDie(), policyContextCreate)
assert.Equal(t, len(engineResponseCreate.PolicyResponse.Rules), 1)
assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
@ -3077,7 +3078,7 @@ func Test_delete_ignore_pattern(t *testing.T) {
policy: &policy,
jsonContext: ctx,
oldResource: *resourceUnstructured}
engineResponseDelete := Validate(registryclient.NewOrDie(), policyContextDelete)
engineResponseDelete := Validate(context.TODO(), registryclient.NewOrDie(), policyContextDelete)
assert.Equal(t, len(engineResponseDelete.PolicyResponse.Rules), 0)
}
@ -3136,7 +3137,7 @@ func Test_ValidatePattern_anyPattern(t *testing.T) {
resourceUnstructured, err := utils.ConvertToUnstructured(tc.rawResource)
assert.NilError(t, err)
er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()})
if tc.expectedFailed {
assert.Assert(t, er.IsFailed())
} else if tc.expectedSkipped {

View file

@ -1,6 +1,7 @@
package policy
import (
"context"
"fmt"
"reflect"
"strings"
@ -11,7 +12,7 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/context"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/registryclient"
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
@ -43,8 +44,8 @@ func applyPolicy(
var engineResponseMutation, engineResponseValidation *response.EngineResponse
var err error
ctx := context.NewContext()
err = context.AddResource(ctx, transformResource(resource))
ctx := enginecontext.NewContext()
err = enginecontext.AddResource(ctx, transformResource(resource))
if err != nil {
logger.Error(err, "failed to add transform resource to ctx")
}
@ -74,7 +75,7 @@ func applyPolicy(
WithClient(client).
WithExcludeGroupRole(excludeGroupRole...)
engineResponseValidation = engine.Validate(rclient, policyCtx)
engineResponseValidation = engine.Validate(context.TODO(), rclient, policyCtx)
engineResponses = append(engineResponses, mergeRuleRespose(engineResponseMutation, engineResponseValidation))
return engineResponses
@ -84,7 +85,7 @@ func mutation(
policy kyvernov1.PolicyInterface,
resource unstructured.Unstructured,
log logr.Logger,
jsonContext context.Interface,
jsonContext enginecontext.Interface,
rclient registryclient.Client,
namespaceLabels map[string]string,
) (*response.EngineResponse, error) {
@ -93,7 +94,7 @@ func mutation(
WithNamespaceLabels(namespaceLabels).
WithNewResource(resource)
engineResponse := engine.Mutate(rclient, policyContext)
engineResponse := engine.Mutate(context.TODO(), rclient, policyContext)
if !engineResponse.IsSuccessful() {
log.V(4).Info("failed to apply mutation rules; reporting them")
return engineResponse, nil

View file

@ -146,7 +146,7 @@ func runTestCase(t *testing.T, tc TestCase) bool {
policyContext := engine.NewPolicyContext().WithPolicy(policy).WithNewResource(*resource)
er := engine.Mutate(registryclient.NewOrDie(), policyContext)
er := engine.Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
t.Log("---Mutation---")
validateResource(t, er.PatchedResource, tc.Expected.Mutation.PatchedResource)
validateResponse(t, er.PolicyResponse, tc.Expected.Mutation.PolicyResponse)
@ -158,7 +158,7 @@ func runTestCase(t *testing.T, tc TestCase) bool {
policyContext = policyContext.WithNewResource(*resource)
er = engine.Validate(registryclient.NewOrDie(), policyContext)
er = engine.Validate(context.TODO(), registryclient.NewOrDie(), policyContext)
t.Log("---Validation---")
validateResponse(t, er.PolicyResponse, tc.Expected.Validation.PolicyResponse)

View file

@ -32,14 +32,8 @@ import (
type GenerationHandler interface {
// TODO: why do we need to expose that ?
HandleUpdatesForGenerateRules(*admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface)
Handle(
metrics.MetricsConfigManager,
*admissionv1.AdmissionRequest,
[]kyvernov1.PolicyInterface,
*engine.PolicyContext,
time.Time,
)
HandleUpdatesForGenerateRules(context.Context, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface)
Handle(context.Context, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext, time.Time)
}
func NewGenerationHandler(
@ -52,6 +46,7 @@ func NewGenerationHandler(
urGenerator webhookgenerate.Generator,
urUpdater webhookutils.UpdateRequestUpdater,
eventGen event.Interface,
metrics metrics.MetricsConfigManager,
) GenerationHandler {
return &generationHandler{
log: log,
@ -63,6 +58,7 @@ func NewGenerationHandler(
urGenerator: urGenerator,
urUpdater: urUpdater,
eventGen: eventGen,
metrics: metrics,
}
}
@ -76,11 +72,12 @@ type generationHandler struct {
urGenerator webhookgenerate.Generator
urUpdater webhookutils.UpdateRequestUpdater
eventGen event.Interface
metrics metrics.MetricsConfigManager
}
// Handle handles admission-requests for policies with generate rules
func (h *generationHandler) Handle(
metricsConfig metrics.MetricsConfigManager,
ctx context.Context,
request *admissionv1.AdmissionRequest,
policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext,
@ -99,7 +96,7 @@ func (h *generationHandler) Handle(
engineResponse := engine.ApplyBackgroundChecks(h.rclient, policyContext)
for _, rule := range engineResponse.PolicyResponse.Rules {
if rule.Status != response.RuleStatusPass {
h.deleteGR(engineResponse)
h.deleteGR(ctx, engineResponse)
continue
}
rules = append(rules, rule)
@ -112,12 +109,12 @@ func (h *generationHandler) Handle(
}
// registering the kyverno_policy_results_total metric concurrently
go webhookutils.RegisterPolicyResultsMetricGeneration(context.TODO(), h.log, metricsConfig, string(request.Operation), policy, *engineResponse)
go webhookutils.RegisterPolicyResultsMetricGeneration(context.TODO(), h.log, h.metrics, string(request.Operation), policy, *engineResponse)
// registering the kyverno_policy_execution_duration_seconds metric concurrently
go webhookutils.RegisterPolicyExecutionDurationMetricGenerate(context.TODO(), h.log, metricsConfig, string(request.Operation), policy, *engineResponse)
go webhookutils.RegisterPolicyExecutionDurationMetricGenerate(context.TODO(), h.log, h.metrics, string(request.Operation), policy, *engineResponse)
}
if failedResponse := applyUpdateRequest(request, kyvernov1beta1.Generate, h.urGenerator, policyContext.AdmissionInfo(), request.Operation, engineResponses...); failedResponse != nil {
if failedResponse := applyUpdateRequest(ctx, request, kyvernov1beta1.Generate, h.urGenerator, policyContext.AdmissionInfo(), request.Operation, engineResponses...); failedResponse != nil {
// report failure event
for _, failedUR := range failedResponse {
err := fmt.Errorf("failed to create Update Request: %v", failedUR.err)
@ -129,12 +126,12 @@ func (h *generationHandler) Handle(
}
if request.Operation == admissionv1.Update {
h.HandleUpdatesForGenerateRules(request, policies)
h.HandleUpdatesForGenerateRules(ctx, request, policies)
}
}
// HandleUpdatesForGenerateRules handles admission-requests for update
func (h *generationHandler) HandleUpdatesForGenerateRules(request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface) {
func (h *generationHandler) HandleUpdatesForGenerateRules(ctx context.Context, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface) {
if request.Operation != admissionv1.Update {
return
}
@ -146,20 +143,20 @@ func (h *generationHandler) HandleUpdatesForGenerateRules(request *admissionv1.A
resLabels := resource.GetLabels()
if resLabels["generate.kyverno.io/clone-policy-name"] != "" {
h.handleUpdateGenerateSourceResource(resLabels)
h.handleUpdateGenerateSourceResource(ctx, resLabels)
}
if resLabels[kyvernov1.LabelAppManagedBy] == kyvernov1.ValueKyvernoApp && resLabels["policy.kyverno.io/synchronize"] == "enable" && request.Operation == admissionv1.Update {
h.handleUpdateGenerateTargetResource(request, policies, resLabels)
h.handleUpdateGenerateTargetResource(ctx, request, policies, resLabels)
}
}
// handleUpdateGenerateSourceResource - handles update of clone source for generate policy
func (h *generationHandler) handleUpdateGenerateSourceResource(resLabels map[string]string) {
func (h *generationHandler) handleUpdateGenerateSourceResource(ctx context.Context, resLabels map[string]string) {
policyNames := strings.Split(resLabels["generate.kyverno.io/clone-policy-name"], ",")
for _, policyName := range policyNames {
// check if the policy exists
_, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(context.TODO(), policyName, metav1.GetOptions{})
_, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(ctx, policyName, metav1.GetOptions{})
if err != nil {
if strings.Contains(err.Error(), "not found") {
h.log.V(4).Info("skipping update of update request as policy is deleted")
@ -185,7 +182,7 @@ func (h *generationHandler) handleUpdateGenerateSourceResource(resLabels map[str
}
// handleUpdateGenerateTargetResource - handles update of target resource for generate policy
func (h *generationHandler) handleUpdateGenerateTargetResource(request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, resLabels map[string]string) {
func (h *generationHandler) handleUpdateGenerateTargetResource(ctx context.Context, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, resLabels map[string]string) {
enqueueBool := false
newRes, err := enginutils.ConvertToUnstructured(request.Object.Raw)
if err != nil {
@ -196,7 +193,7 @@ func (h *generationHandler) handleUpdateGenerateTargetResource(request *admissio
targetSourceName := newRes.GetName()
targetSourceKind := newRes.GetKind()
policy, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(context.TODO(), policyName, metav1.GetOptions{})
policy, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(ctx, policyName, metav1.GetOptions{})
if err != nil {
h.log.Error(err, "failed to get policy from kyverno client.", "policy name", policyName)
return
@ -204,7 +201,7 @@ func (h *generationHandler) handleUpdateGenerateTargetResource(request *admissio
for _, rule := range autogen.ComputeRules(policy) {
if rule.Generation.Kind == targetSourceKind && rule.Generation.Name == targetSourceName {
updatedRule, err := getGeneratedByResource(newRes, resLabels, h.client, rule, h.log)
updatedRule, err := getGeneratedByResource(ctx, newRes, resLabels, h.client, rule, h.log)
if err != nil {
h.log.V(4).Info("skipping generate policy and resource pattern validaton", "error", err)
} else {
@ -218,7 +215,7 @@ func (h *generationHandler) handleUpdateGenerateTargetResource(request *admissio
cloneName := updatedRule.Generation.Clone.Name
if cloneName != "" {
obj, err := h.client.GetResource(context.TODO(), "", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
obj, err := h.client.GetResource(ctx, "", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
if err != nil {
h.log.Error(err, fmt.Sprintf("source resource %s/%s/%s not found.", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name))
continue
@ -246,7 +243,7 @@ func (h *generationHandler) handleUpdateGenerateTargetResource(request *admissio
}
}
func (h *generationHandler) deleteGR(engineResponse *response.EngineResponse) {
func (h *generationHandler) deleteGR(ctx context.Context, engineResponse *response.EngineResponse) {
h.log.V(4).Info("querying all update requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
kyvernov1beta1.URGeneratePolicyLabel: engineResponse.PolicyResponse.Policy.Name,
@ -262,7 +259,7 @@ func (h *generationHandler) deleteGR(engineResponse *response.EngineResponse) {
}
for _, v := range urList {
err := h.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Delete(context.TODO(), v.GetName(), metav1.DeleteOptions{})
err := h.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Delete(ctx, v.GetName(), metav1.DeleteOptions{})
if err != nil {
h.log.Error(err, "failed to update ur")
}

View file

@ -9,7 +9,7 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
enginectx "github.com/kyverno/kyverno/pkg/engine/context"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/webhooks/updaterequest"
@ -17,7 +17,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func getGeneratedByResource(newRes *unstructured.Unstructured, resLabels map[string]string, client dclient.Interface, rule kyvernov1.Rule, logger logr.Logger) (kyvernov1.Rule, error) {
func getGeneratedByResource(ctx context.Context, newRes *unstructured.Unstructured, resLabels map[string]string, client dclient.Interface, rule kyvernov1.Rule, logger logr.Logger) (kyvernov1.Rule, error) {
var apiVersion, kind, name, namespace string
sourceRequest := &admissionv1.AdmissionRequest{}
kind = resLabels["kyverno.io/generated-by-kind"]
@ -25,7 +25,7 @@ func getGeneratedByResource(newRes *unstructured.Unstructured, resLabels map[str
if kind != "Namespace" {
namespace = resLabels["kyverno.io/generated-by-namespace"]
}
obj, err := client.GetResource(context.TODO(), apiVersion, kind, namespace, name)
obj, err := client.GetResource(ctx, apiVersion, kind, namespace, name)
if err != nil {
logger.Error(err, "source resource not found.")
return rule, err
@ -37,12 +37,12 @@ func getGeneratedByResource(newRes *unstructured.Unstructured, resLabels map[str
}
sourceRequest.Object.Raw = rawObj
sourceRequest.Operation = "CREATE"
ctx := enginectx.NewContext()
if err := ctx.AddRequest(sourceRequest); err != nil {
enginectx := enginecontext.NewContext()
if err := enginectx.AddRequest(sourceRequest); err != nil {
logger.Error(err, "failed to load incoming request in context")
return rule, err
}
if rule, err = variables.SubstituteAllInRule(logger, ctx, rule); err != nil {
if rule, err = variables.SubstituteAllInRule(logger, enginectx, rule); err != nil {
logger.Error(err, "variable substitution failed for rule %s", rule.Name)
return rule, err
}
@ -115,8 +115,14 @@ type updateRequestResponse struct {
err error
}
func applyUpdateRequest(request *admissionv1.AdmissionRequest, ruleType kyvernov1beta1.RequestType, grGenerator updaterequest.Generator, userRequestInfo kyvernov1beta1.RequestInfo,
action admissionv1.Operation, engineResponses ...*response.EngineResponse,
func applyUpdateRequest(
ctx context.Context,
request *admissionv1.AdmissionRequest,
ruleType kyvernov1beta1.RequestType,
grGenerator updaterequest.Generator,
userRequestInfo kyvernov1beta1.RequestInfo,
action admissionv1.Operation,
engineResponses ...*response.EngineResponse,
) (failedUpdateRequest []updateRequestResponse) {
admissionRequestInfo := kyvernov1beta1.AdmissionRequestInfoObject{
AdmissionRequest: request,
@ -125,7 +131,7 @@ func applyUpdateRequest(request *admissionv1.AdmissionRequest, ruleType kyvernov
for _, er := range engineResponses {
ur := transform(admissionRequestInfo, userRequestInfo, er, ruleType)
if err := grGenerator.Apply(ur, action); err != nil {
if err := grGenerator.Apply(ctx, ur, action); err != nil {
failedUpdateRequest = append(failedUpdateRequest, updateRequestResponse{ur: ur, err: err})
}
}

View file

@ -117,8 +117,8 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
}
if len(generatePolicies) == 0 && request.Operation == admissionv1.Update {
// handle generate source resource updates
gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen)
go gh.HandleUpdatesForGenerateRules(request, []kyvernov1.PolicyInterface{})
gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
go gh.HandleUpdatesForGenerateRules(context.TODO(), request, []kyvernov1.PolicyInterface{})
}
logger.V(4).Info("processing policies for validate admission request", "validate", len(policies), "mutate", len(mutatePolicies), "generate", len(generatePolicies))
@ -133,9 +133,9 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
namespaceLabels = common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, logger)
}
vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.rclient, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports)
vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.rclient, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports, h.metricsConfig)
ok, msg, warnings := vh.HandleValidation(h.metricsConfig, request, policies, policyContext, namespaceLabels, startTime)
ok, msg, warnings := vh.HandleValidation(ctx, request, policies, policyContext, namespaceLabels, startTime)
if !ok {
logger.Info("admission request denied")
return admissionutils.Response(request.UID, errors.New(msg), warnings...)
@ -167,8 +167,8 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
if err := enginectx.MutateResourceWithImageInfo(request.Object.Raw, policyContext.JSONContext()); err != nil {
logger.Error(err, "failed to patch images info to resource, policies that mutate images may be impacted")
}
mh := mutation.NewMutationHandler(logger, h.rclient, h.eventGen, h.openApiManager, h.nsLister)
mutatePatches, mutateWarnings, err := mh.HandleMutation(h.metricsConfig, request, mutatePolicies, policyContext, startTime)
mh := mutation.NewMutationHandler(logger, h.rclient, h.eventGen, h.openApiManager, h.nsLister, h.metricsConfig)
mutatePatches, mutateWarnings, err := mh.HandleMutation(ctx, request, mutatePolicies, policyContext, startTime)
if err != nil {
logger.Error(err, "mutation failed")
return admissionutils.Response(request.UID, err)
@ -181,7 +181,7 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
return admissionutils.Response(request.UID, err)
}
ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.rclient, h.eventGen, h.admissionReports)
imagePatches, imageVerifyWarnings, err := ivh.Handle(newRequest, verifyImagesPolicies, policyContext)
imagePatches, imageVerifyWarnings, err := ivh.Handle(ctx, newRequest, verifyImagesPolicies, policyContext)
if err != nil {
logger.Error(err, "image verification failed")
return admissionutils.Response(request.UID, err)

View file

@ -23,11 +23,7 @@ import (
)
type ImageVerificationHandler interface {
Handle(
*admissionv1.AdmissionRequest,
[]kyvernov1.PolicyInterface,
*engine.PolicyContext,
) ([]byte, []string, error)
Handle(context.Context, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext) ([]byte, []string, error)
}
func NewImageVerificationHandler(
@ -55,11 +51,12 @@ type imageVerificationHandler struct {
}
func (h *imageVerificationHandler) Handle(
ctx context.Context,
request *admissionv1.AdmissionRequest,
policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext,
) ([]byte, []string, error) {
ok, message, imagePatches, warnings := h.handleVerifyImages(h.log, request, policyContext, policies)
ok, message, imagePatches, warnings := h.handleVerifyImages(ctx, h.log, request, policyContext, policies)
if !ok {
return nil, nil, errors.New(message)
}
@ -67,7 +64,13 @@ func (h *imageVerificationHandler) Handle(
return imagePatches, warnings, nil
}
func (h *imageVerificationHandler) handleVerifyImages(logger logr.Logger, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, policies []kyvernov1.PolicyInterface) (bool, string, []byte, []string) {
func (h *imageVerificationHandler) handleVerifyImages(
ctx context.Context,
logger logr.Logger,
request *admissionv1.AdmissionRequest,
policyContext *engine.PolicyContext,
policies []kyvernov1.PolicyInterface,
) (bool, string, []byte, []string) {
if len(policies) == 0 {
return true, "", nil, nil
}
@ -77,7 +80,7 @@ func (h *imageVerificationHandler) handleVerifyImages(logger logr.Logger, reques
verifiedImageData := &engine.ImageVerificationMetadata{}
for _, p := range policies {
policyContext := policyContext.WithPolicy(p)
resp, ivm := engine.VerifyAndPatchImages(h.rclient, policyContext)
resp, ivm := engine.VerifyAndPatchImages(ctx, h.rclient, policyContext)
engineResponses = append(engineResponses, resp)
patches = append(patches, resp.GetPatches()...)
@ -107,7 +110,7 @@ func (h *imageVerificationHandler) handleVerifyImages(logger logr.Logger, reques
}
}
go h.handleAudit(policyContext.NewResource(), request, nil, engineResponses...)
go h.handleAudit(context.TODO(), policyContext.NewResource(), request, nil, engineResponses...)
warnings := webhookutils.GetWarningMessages(engineResponses)
return true, "", jsonutils.JoinPatches(patches...), warnings
@ -132,6 +135,7 @@ func isResourceDeleted(policyContext *engine.PolicyContext) bool {
}
func (v *imageVerificationHandler) handleAudit(
ctx context.Context,
resource unstructured.Unstructured,
request *admissionv1.AdmissionRequest,
namespaceLabels map[string]string,

View file

@ -30,14 +30,7 @@ type MutationHandler interface {
// HandleMutation handles validating webhook admission request
// If there are no errors in validating rule we apply generation rules
// patchedResource is the (resource + patches) after applying mutation rules
HandleMutation(
metrics.MetricsConfigManager,
*admissionv1.AdmissionRequest,
[]kyvernov1.PolicyInterface,
*engine.PolicyContext,
// map[string]string,
time.Time,
) ([]byte, []string, error)
HandleMutation(context.Context, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext, time.Time) ([]byte, []string, error)
}
func NewMutationHandler(
@ -46,6 +39,7 @@ func NewMutationHandler(
eventGen event.Interface,
openApiManager openapi.ValidateInterface,
nsLister corev1listers.NamespaceLister,
metrics metrics.MetricsConfigManager,
) MutationHandler {
return &mutationHandler{
log: log,
@ -53,6 +47,7 @@ func NewMutationHandler(
eventGen: eventGen,
openApiManager: openApiManager,
nsLister: nsLister,
metrics: metrics,
}
}
@ -62,16 +57,17 @@ type mutationHandler struct {
eventGen event.Interface
openApiManager openapi.ValidateInterface
nsLister corev1listers.NamespaceLister
metrics metrics.MetricsConfigManager
}
func (h *mutationHandler) HandleMutation(
metricsConfig metrics.MetricsConfigManager,
ctx context.Context,
request *admissionv1.AdmissionRequest,
policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext,
admissionRequestTimestamp time.Time,
) ([]byte, []string, error) {
mutatePatches, mutateEngineResponses, err := h.applyMutations(metricsConfig, request, policies, policyContext)
mutatePatches, mutateEngineResponses, err := h.applyMutations(ctx, request, policies, policyContext)
if err != nil {
return nil, nil, err
}
@ -82,7 +78,7 @@ func (h *mutationHandler) HandleMutation(
// applyMutations handles mutating webhook admission request
// return value: generated patches, triggered policies, engine responses correspdonding to the triggered policies
func (v *mutationHandler) applyMutations(
metricsConfig metrics.MetricsConfigManager,
ctx context.Context,
request *admissionv1.AdmissionRequest,
policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext,
@ -105,7 +101,7 @@ func (v *mutationHandler) applyMutations(
}
v.log.V(3).Info("applying policy mutate rules", "policy", policy.GetName())
currentContext := policyContext.WithPolicy(policy)
engineResponse, policyPatches, err := v.applyMutation(request, currentContext)
engineResponse, policyPatches, err := v.applyMutation(ctx, request, currentContext)
if err != nil {
return nil, nil, fmt.Errorf("mutation policy %s error: %v", policy.GetName(), err)
}
@ -122,9 +118,9 @@ func (v *mutationHandler) applyMutations(
engineResponses = append(engineResponses, engineResponse)
// registering the kyverno_policy_results_total metric concurrently
go webhookutils.RegisterPolicyResultsMetricMutation(context.TODO(), v.log, metricsConfig, string(request.Operation), policy, *engineResponse)
go webhookutils.RegisterPolicyResultsMetricMutation(context.TODO(), v.log, v.metrics, string(request.Operation), policy, *engineResponse)
// registering the kyverno_policy_execution_duration_seconds metric concurrently
go webhookutils.RegisterPolicyExecutionDurationMetricMutate(context.TODO(), v.log, metricsConfig, string(request.Operation), policy, *engineResponse)
go webhookutils.RegisterPolicyExecutionDurationMetricMutate(context.TODO(), v.log, v.metrics, string(request.Operation), policy, *engineResponse)
}
// generate annotations
@ -143,12 +139,12 @@ func (v *mutationHandler) applyMutations(
return jsonutils.JoinPatches(patches...), engineResponses, nil
}
func (h *mutationHandler) applyMutation(request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext) (*response.EngineResponse, [][]byte, error) {
func (h *mutationHandler) applyMutation(ctx context.Context, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext) (*response.EngineResponse, [][]byte, error) {
if request.Kind.Kind != "Namespace" && request.Namespace != "" {
policyContext = policyContext.WithNamespaceLabels(common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
}
engineResponse := engine.Mutate(h.rclient, policyContext)
engineResponse := engine.Mutate(ctx, h.rclient, policyContext)
policyPatches := engineResponse.GetPatches()
if !engineResponse.IsSuccessful() {

View file

@ -18,12 +18,12 @@ import (
// createUpdateRequests applies generate and mutateExisting policies, and creates update requests for background reconcile
func (h *handlers) createUpdateRequests(logger logr.Logger, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, generatePolicies, mutatePolicies []kyvernov1.PolicyInterface, ts time.Time) {
gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen)
go h.handleMutateExisting(logger, request, mutatePolicies, policyContext, ts)
go gh.Handle(h.metricsConfig, request, generatePolicies, policyContext, ts)
gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
go h.handleMutateExisting(context.TODO(), logger, request, mutatePolicies, policyContext, ts)
go gh.Handle(context.TODO(), request, generatePolicies, policyContext, ts)
}
func (h *handlers) handleMutateExisting(logger logr.Logger, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, policyContext *engine.PolicyContext, admissionRequestTimestamp time.Time) {
func (h *handlers) handleMutateExisting(ctx context.Context, logger logr.Logger, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, policyContext *engine.PolicyContext, admissionRequestTimestamp time.Time) {
if request.Operation == admissionv1.Delete {
policyContext = policyContext.WithNewResource(policyContext.OldResource())
}
@ -62,7 +62,7 @@ func (h *handlers) handleMutateExisting(logger logr.Logger, request *admissionv1
go webhookutils.RegisterPolicyExecutionDurationMetricMutate(context.TODO(), logger, h.metricsConfig, string(request.Operation), policy, *engineResponse)
}
if failedResponse := applyUpdateRequest(request, kyvernov1beta1.Mutate, h.urGenerator, policyContext.AdmissionInfo(), request.Operation, engineResponses...); failedResponse != nil {
if failedResponse := applyUpdateRequest(ctx, request, kyvernov1beta1.Mutate, h.urGenerator, policyContext.AdmissionInfo(), request.Operation, engineResponses...); failedResponse != nil {
for _, failedUR := range failedResponse {
err := fmt.Errorf("failed to create update request: %v", failedUR.err)
resource := policyContext.NewResource()

View file

@ -1,6 +1,7 @@
package resource
import (
"context"
"errors"
"github.com/go-logr/logr"
@ -44,8 +45,14 @@ func processResourceWithPatches(patch []byte, resource []byte, log logr.Logger)
return resource
}
func applyUpdateRequest(request *admissionv1.AdmissionRequest, ruleType kyvernov1beta1.RequestType, grGenerator updaterequest.Generator, userRequestInfo kyvernov1beta1.RequestInfo,
action admissionv1.Operation, engineResponses ...*response.EngineResponse,
func applyUpdateRequest(
ctx context.Context,
request *admissionv1.AdmissionRequest,
ruleType kyvernov1beta1.RequestType,
grGenerator updaterequest.Generator,
userRequestInfo kyvernov1beta1.RequestInfo,
action admissionv1.Operation,
engineResponses ...*response.EngineResponse,
) (failedUpdateRequest []updateRequestResponse) {
admissionRequestInfo := kyvernov1beta1.AdmissionRequestInfoObject{
AdmissionRequest: request,
@ -54,7 +61,7 @@ func applyUpdateRequest(request *admissionv1.AdmissionRequest, ruleType kyvernov
for _, er := range engineResponses {
ur := transform(admissionRequestInfo, userRequestInfo, er, ruleType)
if err := grGenerator.Apply(ur, action); err != nil {
if err := grGenerator.Apply(ctx, ur, action); err != nil {
failedUpdateRequest = append(failedUpdateRequest, updateRequestResponse{ur: ur, err: err})
}
}

View file

@ -28,7 +28,7 @@ type ValidationHandler interface {
// HandleValidation handles validating webhook admission request
// If there are no errors in validating rule we apply generation rules
// patchedResource is the (resource + patches) after applying mutation rules
HandleValidation(metrics.MetricsConfigManager, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext, map[string]string, time.Time) (bool, string, []string)
HandleValidation(context.Context, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext, map[string]string, time.Time) (bool, string, []string)
}
func NewValidationHandler(
@ -39,6 +39,7 @@ func NewValidationHandler(
pcBuilder webhookutils.PolicyContextBuilder,
eventGen event.Interface,
admissionReports bool,
metrics metrics.MetricsConfigManager,
) ValidationHandler {
return &validationHandler{
log: log,
@ -48,6 +49,7 @@ func NewValidationHandler(
pcBuilder: pcBuilder,
eventGen: eventGen,
admissionReports: admissionReports,
metrics: metrics,
}
}
@ -59,10 +61,11 @@ type validationHandler struct {
pcBuilder webhookutils.PolicyContextBuilder
eventGen event.Interface
admissionReports bool
metrics metrics.MetricsConfigManager
}
func (v *validationHandler) HandleValidation(
metricsConfig metrics.MetricsConfigManager,
ctx context.Context,
request *admissionv1.AdmissionRequest,
policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext,
@ -71,7 +74,7 @@ func (v *validationHandler) HandleValidation(
) (bool, string, []string) {
if len(policies) == 0 {
// invoke handleAudit as we may have some policies in audit mode to consider
go v.handleAudit(policyContext.NewResource(), request, namespaceLabels)
go v.handleAudit(context.TODO(), policyContext.NewResource(), request, namespaceLabels)
return true, "", nil
}
@ -99,15 +102,15 @@ func (v *validationHandler) HandleValidation(
failurePolicy = kyvernov1.Fail
}
engineResponse := engine.Validate(v.rclient, policyContext)
engineResponse := engine.Validate(ctx, v.rclient, policyContext)
if engineResponse.IsNil() {
// we get an empty response if old and new resources created the same response
// allow updates if resource update doesnt change the policy evaluation
continue
}
go webhookutils.RegisterPolicyResultsMetricValidation(context.TODO(), logger, metricsConfig, string(request.Operation), policyContext.Policy(), *engineResponse)
go webhookutils.RegisterPolicyExecutionDurationMetricValidate(context.TODO(), logger, metricsConfig, string(request.Operation), policyContext.Policy(), *engineResponse)
go webhookutils.RegisterPolicyResultsMetricValidation(context.TODO(), logger, v.metrics, string(request.Operation), policyContext.Policy(), *engineResponse)
go webhookutils.RegisterPolicyExecutionDurationMetricValidate(context.TODO(), logger, v.metrics, string(request.Operation), policyContext.Policy(), *engineResponse)
engineResponses = append(engineResponses, engineResponse)
if !engineResponse.IsSuccessful() {
@ -131,13 +134,18 @@ func (v *validationHandler) HandleValidation(
return false, webhookutils.GetBlockedMessages(engineResponses), nil
}
go v.handleAudit(policyContext.NewResource(), request, namespaceLabels, engineResponses...)
go v.handleAudit(context.TODO(), policyContext.NewResource(), request, namespaceLabels, engineResponses...)
warnings := webhookutils.GetWarningMessages(engineResponses)
return true, "", warnings
}
func (v *validationHandler) buildAuditResponses(resource unstructured.Unstructured, request *admissionv1.AdmissionRequest, namespaceLabels map[string]string) ([]*response.EngineResponse, error) {
func (v *validationHandler) buildAuditResponses(
ctx context.Context,
resource unstructured.Unstructured,
request *admissionv1.AdmissionRequest,
namespaceLabels map[string]string,
) ([]*response.EngineResponse, error) {
policies := v.pCache.GetPolicies(policycache.ValidateAudit, request.Kind.Kind, request.Namespace)
policyContext, err := v.pcBuilder.Build(request)
if err != nil {
@ -146,12 +154,13 @@ func (v *validationHandler) buildAuditResponses(resource unstructured.Unstructur
var responses []*response.EngineResponse
for _, policy := range policies {
policyContext := policyContext.WithPolicy(policy).WithNamespaceLabels(namespaceLabels)
responses = append(responses, engine.Validate(v.rclient, policyContext))
responses = append(responses, engine.Validate(ctx, v.rclient, policyContext))
}
return responses, nil
}
func (v *validationHandler) handleAudit(
ctx context.Context,
resource unstructured.Unstructured,
request *admissionv1.AdmissionRequest,
namespaceLabels map[string]string,
@ -171,7 +180,7 @@ func (v *validationHandler) handleAudit(
if !reportutils.IsGvkSupported(schema.GroupVersionKind(request.Kind)) {
return
}
responses, err := v.buildAuditResponses(resource, request, namespaceLabels)
responses, err := v.buildAuditResponses(ctx, resource, request, namespaceLabels)
if err != nil {
v.log.Error(err, "failed to build audit responses")
}

View file

@ -1,6 +1,7 @@
package resource
import (
"context"
"encoding/json"
"fmt"
"testing"
@ -532,6 +533,7 @@ func TestValidate_failure_action_overrides(t *testing.T) {
assert.NilError(t, err)
er := engine.Validate(
context.TODO(),
registryclient.NewOrDie(),
engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured),
)
@ -594,7 +596,7 @@ func Test_RuleSelector(t *testing.T) {
ctx := engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured)
resp := engine.Validate(registryclient.NewOrDie(), ctx)
resp := engine.Validate(context.TODO(), registryclient.NewOrDie(), ctx)
assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 2)
assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)
@ -605,7 +607,7 @@ func Test_RuleSelector(t *testing.T) {
applyOne := kyvernov1.ApplyOne
policy.Spec.ApplyRules = &applyOne
resp = engine.Validate(registryclient.NewOrDie(), ctx)
resp = engine.Validate(context.TODO(), registryclient.NewOrDie(), ctx)
assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 1)
assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)

View file

@ -1,6 +1,8 @@
package updaterequest
import (
"context"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
admissionv1 "k8s.io/api/admission/v1"
)
@ -11,6 +13,6 @@ func NewFake() Generator {
type fakeGenerator struct{}
func (f *fakeGenerator) Apply(gr kyvernov1beta1.UpdateRequestSpec, action admissionv1.Operation) error {
func (f *fakeGenerator) Apply(ctx context.Context, gr kyvernov1beta1.UpdateRequestSpec, action admissionv1.Operation) error {
return nil
}

View file

@ -18,7 +18,7 @@ import (
// Generator provides interface to manage update requests
type Generator interface {
Apply(gr kyvernov1beta1.UpdateRequestSpec, action admissionv1.Operation) error
Apply(context.Context, kyvernov1beta1.UpdateRequestSpec, admissionv1.Operation) error
}
// generator defines the implementation to manage update request resource
@ -39,16 +39,16 @@ func NewGenerator(client versioned.Interface, urInformer kyvernov1beta1informers
}
// Apply creates update request resource
func (g *generator) Apply(ur kyvernov1beta1.UpdateRequestSpec, action admissionv1.Operation) error {
func (g *generator) Apply(ctx context.Context, ur kyvernov1beta1.UpdateRequestSpec, action admissionv1.Operation) error {
logger.V(4).Info("reconcile Update Request", "request", ur)
if action == admissionv1.Delete && ur.Type == kyvernov1beta1.Generate {
return nil
}
go g.applyResource(ur)
go g.applyResource(context.TODO(), ur)
return nil
}
func (g *generator) applyResource(urSpec kyvernov1beta1.UpdateRequestSpec) {
func (g *generator) applyResource(ctx context.Context, urSpec kyvernov1beta1.UpdateRequestSpec) {
exbackoff := &backoff.ExponentialBackOff{
InitialInterval: 500 * time.Millisecond,
RandomizationFactor: 0.5,
@ -58,12 +58,12 @@ func (g *generator) applyResource(urSpec kyvernov1beta1.UpdateRequestSpec) {
Clock: backoff.SystemClock,
}
exbackoff.Reset()
if err := backoff.Retry(func() error { return g.tryApplyResource(urSpec) }, exbackoff); err != nil {
if err := backoff.Retry(func() error { return g.tryApplyResource(ctx, urSpec) }, exbackoff); err != nil {
logger.Error(err, "failed to update request CR")
}
}
func (g *generator) tryApplyResource(urSpec kyvernov1beta1.UpdateRequestSpec) error {
func (g *generator) tryApplyResource(ctx context.Context, urSpec kyvernov1beta1.UpdateRequestSpec) error {
l := logger.WithValues("ruleType", urSpec.Type, "kind", urSpec.Resource.Kind, "name", urSpec.Resource.Name, "namespace", urSpec.Resource.Namespace)
var queryLabels labels.Set
@ -103,7 +103,7 @@ func (g *generator) tryApplyResource(urSpec kyvernov1beta1.UpdateRequestSpec) er
},
Spec: urSpec,
}
if new, err := g.client.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Create(context.TODO(), &ur, metav1.CreateOptions{}); err != nil {
if new, err := g.client.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Create(ctx, &ur, metav1.CreateOptions{}); err != nil {
l.V(4).Error(err, "failed to create UpdateRequest, retrying", "name", ur.GetGenerateName(), "namespace", ur.GetNamespace())
return err
} else {