1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-13 19:28:55 +00:00

Jmespath notfound error (#1907)

* return err, if variable path could not be resolved

Signed-off-by: Max Goncharenko <kacejot@fex.net>

* fixed {{@}} behavior

Signed-off-by: Max Goncharenko <kacejot@fex.net>

* fix json merge logic

Signed-off-by: Max Goncharenko <kacejot@fex.net>

* add e2e tests for Flux use case

Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>
This commit is contained in:
Max Goncharenko 2021-07-02 08:56:50 +03:00 committed by GitHub
parent b72a3d4a8c
commit 6d0ad5598e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 719 additions and 401 deletions

5
go.mod
View file

@ -8,7 +8,7 @@ require (
github.com/dchest/siphash v1.2.1 // indirect
github.com/distribution/distribution v2.7.1+incompatible
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.2.0
github.com/evanphx/json-patch/v5 v5.3.0
github.com/fatih/color v1.12.0
github.com/gardener/controller-manager-library v0.2.0
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
@ -59,8 +59,9 @@ require (
// Added for go1.13 migration https://github.com/golang/go/issues/32805
replace (
github.com/evanphx/json-patch/v5 => github.com/kacejot/json-patch/v5 v5.3.1-0.20210513152033-7395b4a9e87f
github.com/gorilla/rpc v1.2.0+incompatible => github.com/gorilla/rpc v1.2.0
github.com/jmespath/go-jmespath => github.com/kyverno/go-jmespath v0.4.1-0.20210302163943-f30eab0a3ed6
github.com/jmespath/go-jmespath => github.com/kyverno/go-jmespath v0.4.1-0.20210511164400-a1d46efa2ed6
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20200306081859-6a048a382944
k8s.io/component-base => k8s.io/component-base v0.0.0-20190612130303-4062e14deebe
)

369
go.sum

File diff suppressed because it is too large Load diff

View file

@ -77,7 +77,8 @@ func (ctx *Context) AddJSON(dataRaw []byte) error {
ctx.mutex.Lock()
defer ctx.mutex.Unlock()
// merge json
ctx.jsonRaw, err = jsonpatch.MergePatch(ctx.jsonRaw, dataRaw)
ctx.jsonRaw, err = jsonpatch.MergeMergePatches(ctx.jsonRaw, dataRaw)
if err != nil {
ctx.log.Error(err, "failed to merge JSON data")
return err

View file

@ -43,7 +43,7 @@ func (ctx *Context) Query(query string) (interface{}, error) {
result, err := queryPath.Search(data)
if err != nil {
ctx.log.Error(err, "failed to search query", "query", query)
return emptyResult, fmt.Errorf("failed to search query %s: %v", query, err)
return emptyResult, err
}
return result, nil
}

View file

@ -161,8 +161,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
JSONContext: ctx,
NewResource: *resourceUnstructured}
er := Mutate(policyContext)
expectedErrorStr := "variable substitution failed for rule test-path-not-exist: NotFoundVariableErr, variable request.object.metadata.name1 not resolved at path /mutate/overlay/spec/name"
t.Log(er.PolicyResponse.Rules[0].Message)
expectedErrorStr := "variable substitution failed for rule test-path-not-exist: Unknown key \"name1\" in path"
assert.Equal(t, er.PolicyResponse.Rules[0].Message, expectedErrorStr)
}

View file

@ -1476,7 +1476,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) {
er := Validate(policyContext)
assert.Assert(t, er.PolicyResponse.Rules[0].Success)
assert.Equal(t, er.PolicyResponse.Rules[0].Message,
"variable substitution failed for rule test-path-not-exist: NotFoundVariableErr, variable request.object.metadata.name1 not resolved at path /validate/pattern/spec/containers/0/name")
"variable substitution failed for rule test-path-not-exist: Unknown key \"name1\" in path")
}
func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSubstitutionFails(t *testing.T) {
@ -1567,7 +1567,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu
NewResource: *resourceUnstructured}
er := Validate(policyContext)
assert.Assert(t, er.PolicyResponse.Rules[0].Success)
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "variable substitution failed for rule test-path-not-exist: NotFoundVariableErr, variable request.object.metadata.name1 not resolved at path /validate/anyPattern/0/spec/template/spec/containers/0/name")
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "variable substitution failed for rule test-path-not-exist: Unknown key \"name1\" in path")
}
func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) {
@ -1717,7 +1717,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test
NewResource: *resourceUnstructured}
er := Validate(policyContext)
assert.Assert(t, er.PolicyResponse.Rules[0].Success)
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "variable substitution failed for rule test-path-not-exist: NotFoundVariableErr, variable request.object.metadata.name1 not resolved at path /validate/anyPattern/0/spec/template/spec/containers/0/name")
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "variable substitution failed for rule test-path-not-exist: Unknown key \"name1\" in path")
}
func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatternSatisfy(t *testing.T) {
@ -1930,7 +1930,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
// referred variable path not present
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team"},"prune":true,"validation":"client"}}`),
expectedResult: true,
expectedMessage: "variable substitution failed for rule sourceRefNamespace: NotFoundVariableErr, variable request.object.spec.sourceRef.namespace not resolved at path /validate/deny/conditions/0/key",
expectedMessage: "variable substitution failed for rule sourceRefNamespace: Unknown key \"namespace\" in path",
},
{
name: "resource-with-violation",

View file

@ -9,6 +9,7 @@ import (
"strings"
"github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/anchor/common"
"github.com/kyverno/kyverno/pkg/engine/context"
@ -101,6 +102,8 @@ func validateBackgroundModeVars(log logr.Logger, ctx context.EvalInterface) json
_, err := ctx.Query(variable)
if err != nil {
switch err.(type) {
case gojmespath.NotFoundError:
return nil, nil
case context.InvalidVariableErr:
return nil, err
default:
@ -112,16 +115,6 @@ func validateBackgroundModeVars(log logr.Logger, ctx context.EvalInterface) json
})
}
// NotFoundVariableErr is returned when it is impossible to resolve the variable
type NotFoundVariableErr struct {
variable string
path string
}
func (n NotFoundVariableErr) Error() string {
return fmt.Sprintf("NotFoundVariableErr, variable %s not resolved at path %s", n.variable, n.path)
}
// NotResolvedReferenceErr is returned when it is impossible to resolve the variable
type NotResolvedReferenceErr struct {
reference string
@ -197,7 +190,7 @@ func substituteVariablesIfAny(log logr.Logger, ctx context.EvalInterface) jsonUt
substitutedVar, err := ctx.Query(variable)
if err != nil {
switch err.(type) {
case context.InvalidVariableErr:
case context.InvalidVariableErr, gojmespath.NotFoundError:
return nil, err
default:
return nil, fmt.Errorf("failed to resolve %v at path %s: %v", variable, data.Path, err)
@ -206,22 +199,15 @@ func substituteVariablesIfAny(log logr.Logger, ctx context.EvalInterface) jsonUt
log.V(3).Info("variable substituted", "variable", v, "value", substitutedVar, "path", data.Path)
if substitutedVar != nil {
if originalPattern == v {
return substitutedVar, nil
}
if value, err = substituteVarInPattern(originalPattern, v, substitutedVar); err != nil {
return nil, fmt.Errorf("failed to resolve %v at path %s: %s", variable, data.Path, err.Error())
}
continue
if originalPattern == v {
return substitutedVar, nil
}
return nil, NotFoundVariableErr{
variable: variable,
path: data.Path,
if value, err = substituteVarInPattern(originalPattern, v, substitutedVar); err != nil {
return nil, fmt.Errorf("failed to resolve %v at path %s: %s", variable, data.Path, err.Error())
}
continue
}
// check for nested variables in strings

View file

@ -286,7 +286,6 @@ func Test_subVars_withRegexMatch(t *testing.T) {
assert.NilError(t, err)
out, err := json.Marshal(output)
assert.NilError(t, err)
fmt.Print(string(out))
assert.Equal(t, string(out), expected.String())
}
@ -634,6 +633,65 @@ var variableObject = []byte(`
}
`)
func Test_SubstituteNull(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "{{ request.object.simple_object_null }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext()
ctx.AddResource(variableObject)
resolved, err := SubstituteAll(log.Log, ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
var expected interface{}
expected = nil
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteNullInString(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "content = {{ request.object.simple_object_null }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext()
ctx.AddResource(variableObject)
resolved, err := SubstituteAll(log.Log, ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := "content = null"
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteArray(t *testing.T) {
patternRaw := []byte(`
{

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
gojmespath "github.com/jmespath/go-jmespath"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/variables"
@ -95,7 +96,7 @@ func validateDenyConditions(idx int, ctx context.EvalInterface, denyConditions a
func checkNotFoundErr(err error) bool {
if err != nil {
switch err.(type) {
case variables.NotFoundVariableErr:
case gojmespath.NotFoundError:
return true
case context.InvalidVariableErr:
// non-white-listed variable is found

View file

@ -0,0 +1,17 @@
package validate
// ValidateTests is E2E Test Config for validation
var ValidateTests = []struct {
//TestName - Name of the Test
TestName string
// Data - The Yaml file of the ClusterPolicy
Data []byte
// ResourceNamespace - Namespace of the Resource
ResourceNamespace string
}{
{
TestName: "test-validate-with-flux-and-variable-substitution",
Data: kyverno_2043_policy,
ResourceNamespace: "test-validate",
},
}

View file

@ -0,0 +1,499 @@
package validate
// Namespace Description
var namespaceYaml = []byte(`
apiVersion: v1
kind: Namespace
metadata:
name: test-validate
`)
// Regression: https://github.com/kyverno/kyverno/issues/2043
// Policy: https://github.com/fluxcd/flux2-multi-tenancy/blob/main/infrastructure/kyverno-policies/flux-multi-tenancy.yaml
var kyverno_2043_policy = []byte(`
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: flux-multi-tenancy
spec:
validationFailureAction: enforce
rules:
- name: serviceAccountName
exclude:
resources:
namespaces:
- flux-system
match:
resources:
kinds:
- Kustomization
- HelmRelease
validate:
message: ".spec.serviceAccountName is required"
pattern:
spec:
serviceAccountName: "?*"
- name: sourceRefNamespace
exclude:
resources:
namespaces:
- flux-system
match:
resources:
kinds:
- Kustomization
- HelmRelease
validate:
message: "spec.sourceRef.namespace must be the same as metadata.namespace"
deny:
conditions:
- key: "{{request.object.spec.sourceRef.namespace}}"
operator: NotEquals
value: "{{request.object.metadata.namespace}}"
`)
var kyverno_2043_FluxCRD = []byte(`
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.5.0
creationTimestamp: null
name: kustomizations.kustomize.toolkit.fluxcd.io
spec:
group: kustomize.toolkit.fluxcd.io
names:
kind: Kustomization
listKind: KustomizationList
plural: kustomizations
shortNames:
- ks
singular: kustomization
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1beta1
schema:
openAPIV3Schema:
description: Kustomization is the Schema for the kustomizations API.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: KustomizationSpec defines the desired state of a kustomization.
properties:
decryption:
description: Decrypt Kubernetes secrets before applying them on the cluster.
properties:
provider:
description: Provider is the name of the decryption engine.
enum:
- sops
type: string
secretRef:
description: The secret name containing the private OpenPGP keys used for decryption.
properties:
name:
description: Name of the referent
type: string
required:
- name
type: object
required:
- provider
type: object
dependsOn:
description: DependsOn may contain a dependency.CrossNamespaceDependencyReference slice with references to Kustomization resources that must be ready before this Kustomization can be reconciled.
items:
description: CrossNamespaceDependencyReference holds the reference to a dependency.
properties:
name:
description: Name holds the name reference of a dependency.
type: string
namespace:
description: Namespace holds the namespace reference of a dependency.
type: string
required:
- name
type: object
type: array
force:
default: false
description: Force instructs the controller to recreate resources when patching fails due to an immutable field change.
type: boolean
healthChecks:
description: A list of resources to be included in the health assessment.
items:
description: NamespacedObjectKindReference contains enough information to let you locate the typed referenced object in any namespace
properties:
apiVersion:
description: API version of the referent, if not specified the Kubernetes preferred version will be used
type: string
kind:
description: Kind of the referent
type: string
name:
description: Name of the referent
type: string
namespace:
description: Namespace of the referent, when not specified it acts as LocalObjectReference
type: string
required:
- kind
- name
type: object
type: array
images:
description: Images is a list of (image name, new name, new tag or digest) for changing image names, tags or digests. This can also be achieved with a patch, but this operator is simpler to specify.
items:
description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag.
properties:
digest:
description: Digest is the value used to replace the original image tag. If digest is present NewTag value is ignored.
type: string
name:
description: Name is a tag-less image name.
type: string
newName:
description: NewName is the value used to replace the original name.
type: string
newTag:
description: NewTag is the value used to replace the original tag.
type: string
required:
- name
type: object
type: array
interval:
description: The interval at which to reconcile the Kustomization.
type: string
kubeConfig:
description: The KubeConfig for reconciling the Kustomization on a remote cluster. When specified, KubeConfig takes precedence over ServiceAccountName.
properties:
secretRef:
description: SecretRef holds the name to a secret that contains a 'value' key with the kubeconfig file as the value. It must be in the same namespace as the Kustomization. It is recommended that the kubeconfig is self-contained, and the secret is regularly updated if credentials such as a cloud-access-token expire.
properties:
name:
description: Name of the referent
type: string
required:
- name
type: object
type: object
patches:
description: Patches (also called overlays), defined as inline YAML objects.
items:
description: Patch contains either a StrategicMerge or a JSON6902 patch, either a file or inline, and the target the patch should be applied to.
properties:
patch:
description: Patch contains the JSON6902 patch document with an array of operation objects.
type: string
target:
description: Target points to the resources that the patch document should be applied to.
properties:
annotationSelector:
description: AnnotationSelector is a string that follows the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api It matches with the resource annotations.
type: string
group:
description: Group is the API group to select resources from. Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
kind:
description: Kind of the API Group to select resources from. Together with Group and Version it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
labelSelector:
description: LabelSelector is a string that follows the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api It matches with the resource labels.
type: string
name:
description: Name to match resources with.
type: string
namespace:
description: Namespace to select resources from.
type: string
version:
description: Version of the API Group to select resources from. Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
type: object
type: object
type: array
patchesJson6902:
description: JSON 6902 patches, defined as inline YAML objects.
items:
description: JSON6902Patch contains a JSON6902 patch and the target the patch should be applied to.
properties:
patch:
description: Patch contains the JSON6902 patch document with an array of operation objects.
items:
description: JSON6902 is a JSON6902 operation object. https://tools.ietf.org/html/rfc6902#section-4
properties:
from:
type: string
op:
enum:
- test
- remove
- add
- replace
- move
- copy
type: string
path:
type: string
value:
x-kubernetes-preserve-unknown-fields: true
required:
- op
- path
type: object
type: array
target:
description: Target points to the resources that the patch document should be applied to.
properties:
annotationSelector:
description: AnnotationSelector is a string that follows the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api It matches with the resource annotations.
type: string
group:
description: Group is the API group to select resources from. Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
kind:
description: Kind of the API Group to select resources from. Together with Group and Version it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
labelSelector:
description: LabelSelector is a string that follows the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api It matches with the resource labels.
type: string
name:
description: Name to match resources with.
type: string
namespace:
description: Namespace to select resources from.
type: string
version:
description: Version of the API Group to select resources from. Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
type: object
required:
- patch
- target
type: object
type: array
patchesStrategicMerge:
description: Strategic merge patches, defined as inline YAML objects.
items:
x-kubernetes-preserve-unknown-fields: true
type: array
path:
description: Path to the directory containing the kustomization.yaml file, or the set of plain YAMLs a kustomization.yaml should be generated for. Defaults to 'None', which translates to the root path of the SourceRef.
type: string
postBuild:
description: PostBuild describes which actions to perform on the YAML manifest generated by building the kustomize overlay.
properties:
substitute:
additionalProperties:
type: string
description: Substitute holds a map of key/value pairs. The variables defined in your YAML manifests that match any of the keys defined in the map will be substituted with the set value. Includes support for bash string replacement functions e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}.
type: object
substituteFrom:
description: SubstituteFrom holds references to ConfigMaps and Secrets containing the variables and their values to be substituted in the YAML manifests. The ConfigMap and the Secret data keys represent the var names and they must match the vars declared in the manifests for the substitution to happen.
items:
description: SubstituteReference contains a reference to a resource containing the variables name and value.
properties:
kind:
description: Kind of the values referent, valid values are ('Secret', 'ConfigMap').
enum:
- Secret
- ConfigMap
type: string
name:
description: Name of the values referent. Should reside in the same namespace as the referring resource.
maxLength: 253
minLength: 1
type: string
required:
- kind
- name
type: object
type: array
type: object
prune:
description: Prune enables garbage collection.
type: boolean
retryInterval:
description: The interval at which to retry a previously failed reconciliation. When not specified, the controller uses the KustomizationSpec.Interval value to retry failures.
type: string
serviceAccountName:
description: The name of the Kubernetes service account to impersonate when reconciling this Kustomization.
type: string
sourceRef:
description: Reference of the source where the kustomization file is.
properties:
apiVersion:
description: API version of the referent
type: string
kind:
description: Kind of the referent
enum:
- GitRepository
- Bucket
type: string
name:
description: Name of the referent
type: string
namespace:
description: Namespace of the referent, defaults to the Kustomization namespace
type: string
required:
- kind
- name
type: object
suspend:
description: This flag tells the controller to suspend subsequent kustomize executions, it does not apply to already started executions. Defaults to false.
type: boolean
targetNamespace:
description: TargetNamespace sets or overrides the namespace in the kustomization.yaml file.
maxLength: 63
minLength: 1
type: string
timeout:
description: Timeout for validation, apply and health checking operations. Defaults to 'Interval' duration.
type: string
validation:
description: Validate the Kubernetes objects before applying them on the cluster. The validation strategy can be 'client' (local dry-run), 'server' (APIServer dry-run) or 'none'. When 'Force' is 'true', validation will fallback to 'client' if set to 'server' because server-side validation is not supported in this scenario.
enum:
- none
- client
- server
type: string
required:
- interval
- prune
- sourceRef
type: object
status:
description: KustomizationStatus defines the observed state of a kustomization.
properties:
conditions:
items:
description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions."
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
lastAppliedRevision:
description: The last successfully applied revision. The revision format for Git sources is <branch|tag>/<commit-sha>.
type: string
lastAttemptedRevision:
description: LastAttemptedRevision is the revision of the last reconciliation attempt.
type: string
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected.
type: string
observedGeneration:
description: ObservedGeneration is the last reconciled generation.
format: int64
type: integer
snapshot:
description: The last successfully applied revision metadata.
properties:
checksum:
description: The manifests sha1 checksum.
type: string
entries:
description: A list of Kubernetes kinds grouped by namespace.
items:
description: Snapshot holds the metadata of namespaced Kubernetes objects
properties:
kinds:
additionalProperties:
type: string
description: The list of Kubernetes kinds.
type: object
namespace:
description: The namespace of this entry.
type: string
required:
- kinds
type: object
type: array
required:
- checksum
- entries
type: object
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
`)
var kyverno_2043_FluxKustomization = []byte(`
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
name: dev-team
namespace: test-validate
spec:
serviceAccountName: dev-team
interval: 5m
sourceRef:
kind: GitRepository
name: dev-team
prune: true
validation: client
`)

View file

@ -0,0 +1,117 @@
package validate
import (
"errors"
"fmt"
"os"
"testing"
"time"
"github.com/kyverno/kyverno/test/e2e"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var (
// Cluster Polict GVR
clPolGVR = e2e.GetGVR("kyverno.io", "v1", "clusterpolicies")
// Namespace GVR
nsGVR = e2e.GetGVR("", "v1", "namespaces")
// ConfigMap GVR
cmGVR = e2e.GetGVR("", "v1", "configmaps")
crdGVR = e2e.GetGVR("apiextensions.k8s.io", "v1", "customresourcedefinitions")
// ClusterPolicy Namespace
clPolNS = ""
// Namespace Name
// Hardcoded in YAML Definition
nspace = "test-validate"
crdName = "kustomizations.kustomize.toolkit.fluxcd.io"
)
func Test_Validate_Sets(t *testing.T) {
RegisterTestingT(t)
if os.Getenv("E2E") == "" {
t.Skip("Skipping E2E Test")
}
// Generate E2E Client
e2eClient, err := e2e.NewE2EClient()
Expect(err).To(BeNil())
for _, test := range ValidateTests {
By(fmt.Sprintf("Test to validate objects: \"%s\"", test.TestName))
// Clean up Resources
By(fmt.Sprintf("Cleaning Cluster Policies"))
e2eClient.CleanClusterPolicies(clPolGVR)
// Clear Namespace
By(fmt.Sprintf("Deleting Namespace: \"%s\"", nspace))
e2eClient.DeleteClusteredResource(nsGVR, nspace)
//CleanUp CRDs
e2eClient.DeleteClusteredResource(crdGVR, crdName)
// Wait Till Deletion of Namespace
e2e.GetWithRetry(time.Duration(1), 15, func() error {
_, err := e2eClient.GetClusteredResource(nsGVR, nspace)
if err != nil {
return nil
}
return errors.New("Deleting Namespace")
})
// Create Namespace
By(fmt.Sprintf("Creating namespace \"%s\"", nspace))
_, err = e2eClient.CreateClusteredResourceYaml(nsGVR, namespaceYaml)
Expect(err).NotTo(HaveOccurred())
// Create policy
By(fmt.Sprintf("Creating policy in \"%s\"", clPolNS))
_, err = e2eClient.CreateNamespacedResourceYaml(clPolGVR, clPolNS, test.Data)
Expect(err).NotTo(HaveOccurred())
// Create Flux CRD
By(fmt.Sprintf("Creating Flux CRD in \"%s\"", nspace))
_, err = e2eClient.CreateClusteredResourceYaml(crdGVR, kyverno_2043_FluxCRD)
Expect(err).NotTo(HaveOccurred())
// Wait till CRD is created
e2e.GetWithRetry(time.Duration(1), 15, func() error {
_, err := e2eClient.GetClusteredResource(crdGVR, crdName)
if err == nil {
return nil
}
return errors.New("Waiting for CRD to be created...")
})
// Created CRD is not a garantee that we already can create new resources
time.Sleep(3 * time.Second)
// Create Kustomize resource
kustomizeGVR := e2e.GetGVR("kustomize.toolkit.fluxcd.io", "v1beta1", "kustomizations")
By(fmt.Sprintf("Creating Kustomize resource in \"%s\"", nspace))
_, err = e2eClient.CreateNamespacedResourceYaml(kustomizeGVR, nspace, kyverno_2043_FluxKustomization)
Expect(err).NotTo(HaveOccurred())
//CleanUp Resources
e2eClient.CleanClusterPolicies(clPolGVR)
//CleanUp CRDs
e2eClient.DeleteClusteredResource(crdGVR, crdName)
// Clear Namespace
e2eClient.DeleteClusteredResource(nsGVR, nspace)
// Wait Till Deletion of Namespace
e2e.GetWithRetry(time.Duration(1), 15, func() error {
_, err := e2eClient.GetClusteredResource(nsGVR, nspace)
if err != nil {
return nil
}
return errors.New("Deleting Namespace")
})
By(fmt.Sprintf("Test %s Completed \n\n\n", test.TestName))
}
}