1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 07:26:55 +00:00

feat: allow generate pattern changes (#11202)

* chore: remove duplicate test steps

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: remove validation checks upon generate policy changes

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: return nil

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: chainsaw tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: restrict rule spec changes except for generate pattern

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: rename tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: unit tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: upadte e2e matrix

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2024-09-24 22:11:14 +08:00 committed by GitHub
parent e3d7f32146
commit 2d601a0830
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
78 changed files with 448 additions and 329 deletions

View file

@ -9,12 +9,15 @@ import (
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2" kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
"github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/background/common" "github.com/kyverno/kyverno/pkg/background/common"
backgroundcommon "github.com/kyverno/kyverno/pkg/background/common"
generateutils "github.com/kyverno/kyverno/pkg/background/generate" generateutils "github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/config"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
engineutils "github.com/kyverno/kyverno/pkg/utils/engine" engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
"go.uber.org/multierr" "go.uber.org/multierr"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
) )
func (pc *policyController) handleGenerate(policyKey string, policy kyvernov1.PolicyInterface) error { func (pc *policyController) handleGenerate(policyKey string, policy kyvernov1.PolicyInterface) error {
@ -225,28 +228,99 @@ func (pc *policyController) buildUrForDataRuleChanges(policy kyvernov1.PolicyInt
return ur, nil return ur, nil
} }
func (pc *policyController) unlabelDownstream(selector updatedResource) {
for _, ruleSelector := range selector.ruleResources {
for _, kind := range ruleSelector.kinds {
updated, err := pc.client.ListResource(context.TODO(), "", kind, "", &metav1.LabelSelector{
MatchLabels: map[string]string{
backgroundcommon.GeneratePolicyLabel: selector.policy,
backgroundcommon.GeneratePolicyNamespaceLabel: selector.policyNamespace,
backgroundcommon.GenerateRuleLabel: ruleSelector.rule,
},
},
)
if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to list old targets: %v", err))
continue
}
for _, obj := range updated.Items {
labels := obj.GetLabels()
delete(labels, backgroundcommon.GeneratePolicyLabel)
delete(labels, backgroundcommon.GeneratePolicyNamespaceLabel)
delete(labels, backgroundcommon.GenerateRuleLabel)
obj.SetLabels(labels)
_, err = pc.client.UpdateResource(context.TODO(), obj.GetAPIVersion(), obj.GetKind(), obj.GetNamespace(), &obj, false)
if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to un-label old targets %s/%s/%s/%s: %v", obj.GetAPIVersion(), obj.GetKind(), obj.GetNamespace(), obj.GetName(), err))
continue
}
}
}
}
}
type updatedResource struct {
policy string
policyNamespace string
ruleResources []ruleResource
}
type ruleResource struct {
rule string
kinds []string
}
// ruleDeletion returns true if any rule is deleted, along with deleted rules // ruleDeletion returns true if any rule is deleted, along with deleted rules
func ruleDeletion(old, new kyvernov1.PolicyInterface) (_ kyvernov1.PolicyInterface, ruleDeleted bool) { func ruleChange(old, new kyvernov1.PolicyInterface) (_ kyvernov1.PolicyInterface, ruleDeleted bool, _ updatedResource) {
if !new.GetDeletionTimestamp().IsZero() { if !new.GetDeletionTimestamp().IsZero() {
return nil, false return nil, false, updatedResource{}
} }
newRules := new.GetSpec().Rules newRules := new.GetSpec().Rules
oldRules := old.GetSpec().Rules oldRules := old.GetSpec().Rules
newRulesMap := make(map[string]bool, len(newRules)) newRulesMap := make(map[string]kyvernov1.Rule, len(newRules))
var deletedRules []kyvernov1.Rule var deletedRules []kyvernov1.Rule
updatedResources := updatedResource{
policy: new.GetName(),
policyNamespace: new.GetNamespace(),
}
for _, r := range newRules { for _, r := range newRules {
newRulesMap[r.Name] = true newRulesMap[r.Name] = r
} }
for _, r := range oldRules { for _, oldRule := range oldRules {
if exist := newRulesMap[r.Name]; !exist { if newRule, exist := newRulesMap[oldRule.Name]; !exist {
deletedRules = append(deletedRules, r) deletedRules = append(deletedRules, oldRule)
ruleDeleted = true ruleDeleted = true
} else {
ruleRsrc := ruleResource{rule: oldRule.Name}
old, new := oldRule.Generation, newRule.Generation
if old.ResourceSpec != new.ResourceSpec || old.Clone != new.Clone {
ruleRsrc.kinds = append(ruleRsrc.kinds, old.ResourceSpec.GetKind())
}
if !datautils.DeepEqual(old.CloneList, new.CloneList) {
ruleRsrc.kinds = append(ruleRsrc.kinds, old.CloneList.Kinds...)
}
for _, oldForeach := range old.ForEachGeneration {
for _, newForeach := range new.ForEachGeneration {
if oldForeach.List == newForeach.List {
if oldForeach.ResourceSpec != newForeach.ResourceSpec || oldForeach.Clone != newForeach.Clone {
ruleRsrc.kinds = append(ruleRsrc.kinds, old.ResourceSpec.GetKind())
}
if !datautils.DeepEqual(oldForeach.CloneList, newForeach.CloneList) {
ruleRsrc.kinds = append(ruleRsrc.kinds, old.CloneList.Kinds...)
}
}
}
}
updatedResources.ruleResources = append(updatedResources.ruleResources, ruleRsrc)
} }
} }
return buildPolicyWithDeletedRules(old, deletedRules), ruleDeleted return buildPolicyWithDeletedRules(old, deletedRules), ruleDeleted, updatedResources
} }
func buildPolicyWithDeletedRules(policy kyvernov1.PolicyInterface, deletedRules []kyvernov1.Rule) kyvernov1.PolicyInterface { func buildPolicyWithDeletedRules(policy kyvernov1.PolicyInterface, deletedRules []kyvernov1.Rule) kyvernov1.PolicyInterface {

View file

@ -201,11 +201,13 @@ func (pc *policyController) updatePolicy(old, cur interface{}) {
} }
logger.V(2).Info("updating policy", "name", oldP.GetName()) logger.V(2).Info("updating policy", "name", oldP.GetName())
if deleted, ok := ruleDeletion(oldP, curP); ok { if deleted, ok, selector := ruleChange(oldP, curP); ok {
err := pc.createURForDownstreamDeletion(deleted) err := pc.createURForDownstreamDeletion(deleted)
if err != nil { if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to create UR on rule deletion, clean up downstream resource may be failed: %v", err)) utilruntime.HandleError(fmt.Errorf("failed to create UR on rule deletion, clean up downstream resource may be failed: %v", err))
} }
} else {
pc.unlabelDownstream(selector)
} }
pc.enqueuePolicy(curP) pc.enqueuePolicy(curP)

View file

@ -11,61 +11,66 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
) )
func immutableGenerateFields(new, old kyvernov1.PolicyInterface) error { func immutableGenerateFields(new, old kyvernov1.PolicyInterface) (string, error) {
if new == nil || old == nil { if new == nil || old == nil {
return nil return "", nil
} }
if !new.GetSpec().HasGenerate() { oldRuleHashes, oldGenerationHashes, err := buildHashes(old.GetSpec().Rules)
return nil
}
oldRuleHashes, err := buildHashes(old.GetSpec().Rules)
if err != nil { if err != nil {
return err return "", err
} }
newRuleHashes, err := buildHashes(new.GetSpec().Rules) newRuleHashes, newGenerationHashes, err := buildHashes(new.GetSpec().Rules)
if err != nil { if err != nil {
return err return "", err
} }
switch len(old.GetSpec().Rules) <= len(new.GetSpec().Rules) { if !newGenerationHashes.IsSuperset(oldGenerationHashes) {
case true: return "changes in the generate rule pattern could result in stale targets", nil
if newRuleHashes.IsSuperset(oldRuleHashes) {
return nil
} else {
return errors.New("change of immutable fields for a generate rule is disallowed")
}
case false:
if oldRuleHashes.IsSuperset(newRuleHashes) {
return nil
} else {
return errors.New("rule deletion - change of immutable fields for a generate rule is disallowed")
}
} }
return nil
if !newRuleHashes.IsSuperset(oldRuleHashes) {
return "", errors.New("changes of the rule spec in a generate rule is disallowed")
}
return "", nil
} }
func resetMutableFields(rule kyvernov1.Rule) *kyvernov1.Rule { func resetMutableFields(rule kyvernov1.Rule) (*kyvernov1.Rule, *kyvernov1.Generation) {
new := new(kyvernov1.Rule) new := new(kyvernov1.Rule)
rule.DeepCopyInto(new) rule.DeepCopyInto(new)
new.Generation.Synchronize = true generation := new.Generation
new.Generation.SetData(nil) new.Generation = nil
new.Generation.ForEachGeneration = nil generation.Synchronize = true
new.Generation.OrphanDownstreamOnPolicyDelete = true generation.SetData(nil)
return new generation.ForEachGeneration = nil
generation.OrphanDownstreamOnPolicyDelete = true
generation.GenerateExisting = nil
return new, generation
} }
func buildHashes(rules []kyvernov1.Rule) (sets.Set[string], error) { func buildHashes(rules []kyvernov1.Rule) (ruleHashes sets.Set[string], generationHashes sets.Set[string], _ error) {
ruleHashes := sets.New[string]() ruleHashes, generationHashes = sets.New[string](), sets.New[string]()
for _, rule := range rules { for _, rule := range rules {
r := resetMutableFields(rule) if !rule.HasGenerate() {
data, err := json.Marshal(r) continue
}
r, generation := resetMutableFields(rule)
data, err := json.Marshal(generation)
if err != nil { if err != nil {
return ruleHashes, fmt.Errorf("failed to create hash from the generate rule %v", err) return ruleHashes, generationHashes, fmt.Errorf("failed to create hash from the generate rule %v", err)
} }
hash := md5.Sum(data) //nolint:gosec hash := md5.Sum(data) //nolint:gosec
generationHashes.Insert(hex.EncodeToString(hash[:]))
data, err = json.Marshal(r)
if err != nil {
return ruleHashes, generationHashes, fmt.Errorf("failed to create hash from the generate rule %v", err)
}
hash = md5.Sum(data) //nolint:gosec
ruleHashes.Insert(hex.EncodeToString(hash[:])) ruleHashes.Insert(hex.EncodeToString(hash[:]))
} }
return ruleHashes, nil return ruleHashes, generationHashes, nil
} }

View file

@ -227,7 +227,8 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
} }
} }
if err := immutableGenerateFields(policy, oldPolicy); err != nil { if warning, err := immutableGenerateFields(policy, oldPolicy); warning != "" || err != nil {
warnings = append(warnings, fmt.Sprintf("no synchronization will be performed to the old target resource upon policy updates: %s", warning))
return warnings, err return warnings, err
} }

View file

@ -2209,12 +2209,13 @@ func Test_Validate_RuleImageExtractorsJMESPath(t *testing.T) {
assert.Equal(t, expectedErr.Error(), actualErr.Error()) assert.Equal(t, expectedErr.Error(), actualErr.Error())
} }
func Test_ImmutableGenerateFields(t *testing.T) { func Test_GenerateFieldsUpdates(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
oldPolicy []byte oldPolicy []byte
newPolicy []byte newPolicy []byte
expectedErr bool expectedErr bool
expectWarning bool
}{ }{
{ {
name: "update-apiVersion", name: "update-apiVersion",
@ -2292,7 +2293,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-kind", name: "update-kind",
@ -2370,7 +2372,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-namespace", name: "update-namespace",
@ -2448,7 +2451,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-name", name: "update-name",
@ -2526,7 +2530,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-sync-flag", name: "update-sync-flag",
@ -2604,7 +2609,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: false, expectedErr: false,
expectWarning: false,
}, },
{ {
name: "update-clone-name", name: "update-clone-name",
@ -2682,7 +2688,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-clone-namespace", name: "update-clone-namespace",
@ -2760,7 +2767,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-clone-namespace-unset-new", name: "update-clone-namespace-unset-new",
@ -2837,7 +2845,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-cloneList-kinds", name: "update-cloneList-kinds",
@ -2916,7 +2925,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-cloneList-namespace", name: "update-cloneList-namespace",
@ -2995,7 +3005,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-cloneList-selector", name: "update-cloneList-selector",
@ -3085,7 +3096,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-clone-List-selector-unset", name: "update-clone-List-selector-unset",
@ -3170,7 +3182,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: true, expectedErr: false,
expectWarning: true,
}, },
{ {
name: "update-cloneList-selector-nochange", name: "update-cloneList-selector-nochange",
@ -3260,7 +3273,8 @@ func Test_ImmutableGenerateFields(t *testing.T) {
] ]
} }
}`), }`),
expectedErr: false, expectedErr: false,
expectWarning: false,
}, },
} }
@ -3271,8 +3285,10 @@ func Test_ImmutableGenerateFields(t *testing.T) {
err = json.Unmarshal(test.newPolicy, &new) err = json.Unmarshal(test.newPolicy, &new)
assert.Nil(t, err) assert.Nil(t, err)
err = immutableGenerateFields(new, old) warning, err := immutableGenerateFields(new, old)
golangassert.Assert(t, (warning != "") == test.expectWarning, test.name, err)
golangassert.Assert(t, (err != nil) == test.expectedErr, test.name, err) golangassert.Assert(t, (err != nil) == test.expectedErr, test.name, err)
} }
} }

View file

@ -61,9 +61,9 @@
"^generate$/^policy$/^standard$/^data$/^nosync$/^(pol-data-nosync-create-policy-invalid|pol-data-nosync-delete-downstream|pol-data-nosync-delete-policy|pol-data-nosync-delete-rule|pol-data-nosync-delete-rule-deprecated|pol-data-nosync-delete-trigger|pol-data-nosync-modify-downstream|pol-data-nosync-modify-rule|pol-data-nosync-update-trigger-no-match)\\[.*\\]$", "^generate$/^policy$/^standard$/^data$/^nosync$/^(pol-data-nosync-create-policy-invalid|pol-data-nosync-delete-downstream|pol-data-nosync-delete-policy|pol-data-nosync-delete-rule|pol-data-nosync-delete-rule-deprecated|pol-data-nosync-delete-trigger|pol-data-nosync-modify-downstream|pol-data-nosync-modify-rule|pol-data-nosync-update-trigger-no-match)\\[.*\\]$",
"^generate$/^policy$/^standard$/^data$/^sync$/^(pol-data-sync-create-policy-invalid|pol-data-sync-create-policy-valid|pol-data-sync-delete-downstream|pol-data-sync-delete-policy|pol-data-sync-delete-rule|pol-data-sync-delete-rule-deprecated|pol-data-sync-delete-trigger|pol-data-sync-modify-downstream|pol-data-sync-modify-rule|pol-data-sync-modify-rule-deprecated|pol-data-sync-update-trigger-no-match)\\[.*\\]$", "^generate$/^policy$/^standard$/^data$/^sync$/^(pol-data-sync-create-policy-invalid|pol-data-sync-create-policy-valid|pol-data-sync-delete-downstream|pol-data-sync-delete-policy|pol-data-sync-delete-rule|pol-data-sync-delete-rule-deprecated|pol-data-sync-delete-trigger|pol-data-sync-modify-downstream|pol-data-sync-modify-rule|pol-data-sync-modify-rule-deprecated|pol-data-sync-update-trigger-no-match)\\[.*\\]$",
"^generate$/^policy$/^standard$/^existing$/^(match-trigger-namespace|match-trigger-namespace-deprecated|non-match-trigger-namespace|non-match-trigger-namespace-deprecated)\\[.*\\]$", "^generate$/^policy$/^standard$/^existing$/^(match-trigger-namespace|match-trigger-namespace-deprecated|non-match-trigger-namespace|non-match-trigger-namespace-deprecated)\\[.*\\]$",
"^generate$/^validation$/^clusterpolicy$/^(cloneList|immutable-clone|immutable-clonelist|immutable-downstream|immutable-rule-spec|orphan|prevent-loop|target-namespace-scope|use-generate-existing-on-policy-update)\\[.*\\]$", "^generate$/^validation$/^clusterpolicy$/^(cloneList|immutable-rule-spec|orphan|prevent-loop|target-namespace-scope|use-generate-existing-on-policy-update|warn-clone-change|warn-clonelist-change|warn-downstream-change)\\[.*\\]$",
"^generate$/^validation$/^clusterpolicy$/^permissions$/^(no-permission|same-kind)\\[.*\\]$", "^generate$/^validation$/^clusterpolicy$/^permissions$/^(no-permission|same-kind)\\[.*\\]$",
"^generate$/^validation$/^policy$/^(cloneList|immutable-clone|immutable-clonelist|immutable-downstream|immutable-rule-spec|permissions|prevent-loop|target-namespace-scope|use-generate-existing-on-policy-update)\\[.*\\]$" "^generate$/^validation$/^policy$/^(cloneList|immutable-rule-spec|permissions|prevent-loop|target-namespace-scope|use-generate-existing-on-policy-update|warn-clone-change|warn-clonelist-change|warn-downstream-change)\\[.*\\]$"
], ],
"generate-validating-admission-policy": [ "generate-validating-admission-policy": [
"^generate-validating-admission-policy$/^clusterpolicy$/^standard$/^generate$/^(block-ephemeral-containers|block-exec-in-pods|cpol-all-match-resource|cpol-any-exclude-namespace-match-resource|cpol-any-exclude-resource|cpol-any-exclude-resource-match-with-namespace-selector|cpol-any-exclude-resource-match-with-object-selector|cpol-any-match-multiple-resources|cpol-any-match-resource|cpol-any-match-resources-by-names|cpol-match-all-exclude-one|cpol-match-kind-with-wildcard|cpol-match-resource-in-specific-namespace|cpol-with-an-exception|cpol-with-an-exception-excluding-namespaces|cpol-with-two-exceptions)\\[.*\\]$", "^generate-validating-admission-policy$/^clusterpolicy$/^standard$/^generate$/^(block-ephemeral-containers|block-exec-in-pods|cpol-all-match-resource|cpol-any-exclude-namespace-match-resource|cpol-any-exclude-resource|cpol-any-exclude-resource-match-with-namespace-selector|cpol-any-exclude-resource-match-with-object-selector|cpol-any-match-multiple-resources|cpol-any-match-resource|cpol-any-match-resources-by-names|cpol-match-all-exclude-one|cpol-match-kind-with-wildcard|cpol-match-resource-in-specific-namespace|cpol-with-an-exception|cpol-with-an-exception-excluding-namespaces|cpol-with-two-exceptions)\\[.*\\]$",

View file

@ -0,0 +1,31 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: sync-with-multi-clone-update
spec:
rules:
- name: sync-secret
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
generateExisting: false
namespace: "{{request.object.metadata.name}}"
synchronize : true
cloneList:
namespace: default
kinds:
- v1/Secret
selector:
matchLabels:
allowedToBeCloned: "true"

View file

@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: bootstrap-config
namespace: default
labels:
allowedToBeCloned: "true"
data:
initial_lives: "100"

View file

@ -4,7 +4,8 @@ This test verifies the synchronize behavior of generated resource, if the select
## Expected Behavior ## Expected Behavior
This test ensures that update of source resource(ConfigMap) match selected using `allowedToBeCloned: "true"` label get synchronized with target resource created by a ClusterPolicy `generate.cloneList` rule, otherwise the test fails. 1. update source resource (configmap) match selected using `allowedToBeCloned: "true"` label, the change should be synced to the target configmap.
2. remove configmap from the `cloneList.kinds` in the policy, update the source configmap, the change should not be synced to the previous cloned configmap
## Reference Issue(s) ## Reference Issue(s)

View file

@ -7,28 +7,34 @@ spec:
- name: step-00 - name: step-00
try: try:
- apply: - apply:
file: permissions.yaml file: 0-0-permissions.yaml
- apply: - apply:
file: manifests.yaml file: 0-1-manifests.yaml
- apply: - apply:
file: cluster-policy.yaml file: 0-2-cluster-policy.yaml
- assert: - assert:
file: cluster-policy-ready.yaml file: 0-3-cluster-policy-ready.yaml
- name: step-01 - name: step-01
try: try:
- apply: - apply:
file: ns.yaml file: 1-0-ns.yaml
- assert: - assert:
file: resource-assert.yaml file: 1-1-resource-assert.yaml
- name: step-02 - name: step-02
try: try:
- apply: - apply:
file: ns.yaml file: 2-0-update-source.yaml
- assert: - assert:
file: resource-assert.yaml file: 2-1-synchronized-target.yaml
- name: step-03 - name: step-03
try: try:
- apply: - apply:
file: update-source.yaml file: 3-0-update-cluster-policy.yaml
- assert: - assert:
file: synchronized-target.yaml file: 0-3-cluster-policy-ready.yaml
- name: step-04
try:
- apply:
file: 4-0-update-source.yaml
- assert:
file: 2-1-synchronized-target.yaml

View file

@ -30,6 +30,7 @@ spec:
metadata: metadata:
labels: labels:
somekey: somevalue somekey: somevalue
foo: bar
data: data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181" ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092" KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,65 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: multiple-gens
spec:
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
generateExisting: false
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address-new
namespace: "{{request.object.metadata.name}}"
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
foo: bar
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"
- name: super-secret
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
generateExisting: false
synchronize: true
apiVersion: v1
kind: Secret
name: supersecret
namespace: "{{request.object.metadata.name}}"
data:
kind: Secret
type: Opaque
metadata:
labels:
somekey: somesecretvalue
data:
mysupersecretkey: bXlzdXBlcnNlY3JldHZhbHVl

View file

@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: cpol-data-sync-delete-rule
labels:
foo: bar

View file

@ -0,0 +1,10 @@
apiVersion: v1
data:
KAFKA_ADDRESS: 192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092
ZK_ADDRESS: 192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181
kind: ConfigMap
metadata:
labels:
somekey: somevalue
name: zk-kafka-address-new
namespace: cpol-data-sync-delete-rule

View file

@ -4,7 +4,9 @@ This test checks to ensure that deletion of a rule in a ClusterPolicy generate r
## Expected Behavior ## Expected Behavior
The downstream (generated) resource is expected to be deleted if the corresponding rule within a ClusterPolicy is deleted. If it is not deleted, the test fails. If it is deleted, the test passes. 1. create the namespace that triggers the policy, two rules should be applied and generate a configmap and a secret correspondingly.
2. update the configmap rule and trigger the policy again, a new configmap should be generated.
3. delete the newly updated configmap rule, the new configmap should be deleted while the old configmap preserves.
## Reference Issue(s) ## Reference Issue(s)

View file

@ -7,34 +7,42 @@ spec:
- name: step-01 - name: step-01
try: try:
- apply: - apply:
file: permissions.yaml file: 1-1-permissions.yaml
- apply: - apply:
file: policy.yaml file: 1-2-policy.yaml
- assert: - assert:
file: policy-ready.yaml file: 1-3-policy-ready.yaml
- name: step-02 - name: step-02
try: try:
- apply: - apply:
file: chainsaw-step-02-apply-1-1.yaml file: 2-1-namespace.yaml
- name: step-03 - name: step-03
try: try:
- assert: - assert:
file: secret.yaml file: 3-1-secret.yaml
- assert: - assert:
file: configmap.yaml file: 3-2-configmap.yaml
- name: step-04 - name: step-04
try: try:
- apply: - apply:
file: delete-rule.yaml file: 4-1-rule-update.yaml
- assert: - assert:
file: policy-ready.yaml file: 1-3-policy-ready.yaml
- name: step-05 - name: step-05
try: try:
- sleep: - apply:
duration: 3s file: 5-1-namespace-update.yaml
- assert:
file: 5-2-configmap-new.yaml
- name: step-06 - name: step-06
try: try:
- apply:
file: 6-1-delete-rule.yaml
- assert: - assert:
file: secret.yaml file: 1-3-policy-ready.yaml
- name: step-07
try:
- assert:
file: 3-2-configmap.yaml
- error: - error:
file: configmap.yaml file: 5-2-configmap-new.yaml

View file

@ -1,26 +0,0 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-clone
spec:
steps:
- name: step-01
try:
- apply:
file: permissions.yaml
- apply:
file: policy.yaml
- assert:
file: policy-ready.yaml
- name: step-02
try:
- apply:
expect:
- check:
($error != null): true
file: update-name.yaml
- apply:
expect:
- check:
($error != null): true
file: update-namespace.yaml

View file

@ -1,29 +0,0 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-clonelist
spec:
steps:
- name: step-01
try:
- apply:
file: cluster-policy.yaml
- assert:
file: cluster-policy-ready.yaml
- name: step-02
try:
- apply:
expect:
- check:
($error != null): true
file: update-ns.yaml
- apply:
expect:
- check:
($error != null): true
file: update-kinds.yaml
- apply:
expect:
- check:
($error != null): true
file: update-selector.yaml

View file

@ -1,34 +0,0 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-downstream
spec:
steps:
- name: step-01
try:
- apply:
file: chainsaw-step-01-apply-1-1.yaml
- assert:
file: chainsaw-step-01-assert-1-1.yaml
- name: step-02
try:
- apply:
expect:
- check:
($error != null): true
file: update-name.yaml
- apply:
expect:
- check:
($error != null): true
file: update-apiversion.yaml
- apply:
expect:
- check:
($error != null): true
file: update-namespace.yaml
- apply:
expect:
- check:
($error != null): true
file: update-kind.yaml

View file

@ -0,0 +1,22 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-clone
spec:
steps:
- name: step-01
try:
- apply:
file: permissions.yaml
- apply:
file: policy.yaml
- assert:
file: policy-ready.yaml
- name: step-02
try:
- script:
content: >
kubectl apply -f update-name.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- script:
content: >
kubectl apply -f update-namespace.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"

View file

@ -0,0 +1,23 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-clonelist
spec:
steps:
- name: step-01
try:
- apply:
file: cluster-policy.yaml
- assert:
file: cluster-policy-ready.yaml
- name: step-02
try:
- script:
content: >
kubectl apply -f update-ns.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- script:
content: >
kubectl apply -f update-kinds.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- script:
content: >
kubectl apply -f update-selector.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"

View file

@ -0,0 +1,26 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-downstream
spec:
steps:
- name: step-01
try:
- apply:
file: chainsaw-step-01-apply-1-1.yaml
- assert:
file: chainsaw-step-01-assert-1-1.yaml
- name: step-02
try:
- script:
content: >
kubectl apply -f update-name.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- script:
content: >
kubectl apply -f update-namespace.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- script:
content: >
kubectl apply -f update-apiversion.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- script:
content: >
kubectl apply -f update-kind.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"

View file

@ -1,22 +0,0 @@
apiVersion: kyverno.io/v2beta1
kind: Policy
metadata:
name: generate-update-clone
namespace: default
spec:
rules:
- name: clone-secret
match:
any:
- resources:
kinds:
- ConfigMap
generate:
apiVersion: v1
kind: Secret
name: regcred
namespace: default
synchronize: true
clone:
namespace: ichangethis
name: regcred

View file

@ -1,29 +0,0 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-clonelist
spec:
steps:
- name: step-01
try:
- apply:
file: policy.yaml
- assert:
file: policy-ready.yaml
- name: step-02
try:
- apply:
expect:
- check:
($error != null): true
file: update-ns.yaml
- apply:
expect:
- check:
($error != null): true
file: update-kinds.yaml
- apply:
expect:
- check:
($error != null): true
file: update-selector.yaml

View file

@ -1,25 +0,0 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-clonelist
namespace: default
spec:
generateExisting: false
rules:
- name: sync-secret
match:
any:
- resources:
kinds:
- ConfigMap
generate:
namespace: default
synchronize : true
cloneList:
namespace: update-clonelist-ns
kinds:
- v1/Secret
- v1/ConfigMap
selector:
matchLabels:
allowedToBeCloned: "true"

View file

@ -1,34 +0,0 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-downstream
spec:
steps:
- name: step-01
try:
- apply:
file: chainsaw-step-01-apply-1-1.yaml
- assert:
file: chainsaw-step-01-assert-1-1.yaml
- name: step-02
try:
- apply:
expect:
- check:
($error != null): true
file: update-name.yaml
- apply:
expect:
- check:
($error != null): true
file: update-apiversion.yaml
- apply:
expect:
- check:
($error != null): true
file: update-namespace.yaml
- apply:
expect:
- check:
($error != null): true
file: update-kind.yaml

View file

@ -1,28 +0,0 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-downstream-rule
namespace: default
spec:
generateExisting: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- ConfigMap
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: ichangedthis
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -12,13 +12,6 @@ spec:
file: policy-ready.yaml file: policy-ready.yaml
- name: step-02 - name: step-02
try: try:
- apply: - script:
expect: content: >
- check: kubectl apply -f update-name.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
($error != null): true
file: update-namespace.yaml
- apply:
expect:
- check:
($error != null): true
file: update-name.yaml

View file

@ -0,0 +1,22 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-clonelist
spec:
steps:
- name: step-01
try:
- apply:
file: policy.yaml
- assert:
file: policy-ready.yaml
- name: step-02
try:
- script:
content: >
kubectl apply -f update-kinds.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- name: step-03
try:
- script:
content: >
kubectl apply -f update-selector.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"

View file

@ -11,7 +11,7 @@ spec:
any: any:
- resources: - resources:
kinds: kinds:
- - ConfigMap
generate: generate:
namespace: default namespace: default
synchronize : true synchronize : true

View file

@ -0,0 +1,23 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: immutable-downstream
spec:
steps:
- name: step-01
try:
- apply:
file: chainsaw-step-01-apply-1-1.yaml
- assert:
file: chainsaw-step-01-assert-1-1.yaml
- name: step-02
try:
- script:
content: >
kubectl apply -f update-name.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- script:
content: >
kubectl apply -f update-apiversion.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"
- script:
content: >
kubectl apply -f update-kind.yaml 2>&1 | grep -q "Warning: no synchronization will be performed to the old target resource upon policy updates"