mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
feat: support foreach
for generate.data
(#10875)
* chore: refactor Signed-off-by: ShutingZhao <shuting@nirmata.com> * feat: add foreach for generate.daya to api Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: refactor generator Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: linter Signed-off-by: ShutingZhao <shuting@nirmata.com> * feat: update rule validation Signed-off-by: ShutingZhao <shuting@nirmata.com> * feat: update rule validation -2 Signed-off-by: ShutingZhao <shuting@nirmata.com> * feat: support foreach.data Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: policy validation Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: context variables Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: add a chainsaw test Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: sync on policy deletion Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: enable new chainsaw tests in CI Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: update code-gen Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: validate targets scope for ns-policies Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: add missing files Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: remove unreasonable test Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: update docs Signed-off-by: ShutingZhao <shuting@nirmata.com> * chore: update install.yaml Signed-off-by: ShutingZhao <shuting@nirmata.com> --------- Signed-off-by: ShutingZhao <shuting@nirmata.com> Co-authored-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
This commit is contained in:
parent
c96f224e8e
commit
bd71af3291
40 changed files with 14450 additions and 337 deletions
1
.github/workflows/conformance.yaml
vendored
1
.github/workflows/conformance.yaml
vendored
|
@ -105,6 +105,7 @@ jobs:
|
|||
- ^generate$/^clusterpolicy$
|
||||
- ^generate$/^policy$
|
||||
- ^generate$/^validation$
|
||||
- ^generate$/^foreach$
|
||||
- ^globalcontext$
|
||||
- ^lease$
|
||||
- ^mutate$
|
||||
|
|
|
@ -749,9 +749,6 @@ type Generation struct {
|
|||
// +optional
|
||||
GenerateExisting *bool `json:"generateExisting,omitempty" yaml:"generateExisting,omitempty"`
|
||||
|
||||
// ResourceSpec contains information to select the resource.
|
||||
ResourceSpec `json:",omitempty" yaml:",omitempty"`
|
||||
|
||||
// Synchronize controls if generated resources should be kept in-sync with their source resource.
|
||||
// If Synchronize is set to "true" changes to generated resources will be overwritten with resource
|
||||
// data from Data or the resource specified in the Clone declaration.
|
||||
|
@ -766,6 +763,19 @@ type Generation struct {
|
|||
// +optional
|
||||
OrphanDownstreamOnPolicyDelete bool `json:"orphanDownstreamOnPolicyDelete,omitempty" yaml:"orphanDownstreamOnPolicyDelete,omitempty"`
|
||||
|
||||
// +optional
|
||||
GeneratePatterns `json:",omitempty" yaml:",omitempty"`
|
||||
|
||||
// ForEach applies generate rules to a list of sub-elements by creating a context for each entry in the list and looping over it to apply the specified logic.
|
||||
// +optional
|
||||
ForEachGeneration []ForEachGeneration `json:"foreach,omitempty" yaml:"foreach,omitempty"`
|
||||
}
|
||||
|
||||
type GeneratePatterns struct {
|
||||
// ResourceSpec contains information to select the resource.
|
||||
// +kubebuilder:validation:Optional
|
||||
ResourceSpec `json:",omitempty" yaml:",omitempty"`
|
||||
|
||||
// Data provides the resource declaration used to populate each generated resource.
|
||||
// At most one of Data or Clone must be specified. If neither are provided, the generated
|
||||
// resource will be created with default data only.
|
||||
|
@ -783,6 +793,25 @@ type Generation struct {
|
|||
CloneList CloneList `json:"cloneList,omitempty" yaml:"cloneList,omitempty"`
|
||||
}
|
||||
|
||||
type ForEachGeneration struct {
|
||||
// List specifies a JMESPath expression that results in one or more elements
|
||||
// to which the validation logic is applied.
|
||||
List string `json:"list,omitempty" yaml:"list,omitempty"`
|
||||
|
||||
// Context defines variables and data sources that can be used during rule execution.
|
||||
// +optional
|
||||
Context []ContextEntry `json:"context,omitempty" yaml:"context,omitempty"`
|
||||
|
||||
// AnyAllConditions are used to determine if a policy rule should be applied by evaluating a
|
||||
// set of conditions. The declaration can contain nested `any` or `all` statements.
|
||||
// See: https://kyverno.io/docs/writing-policies/preconditions/
|
||||
// +kubebuilder:validation:XPreserveUnknownFields
|
||||
// +optional
|
||||
AnyAllConditions *AnyAllConditions `json:"preconditions,omitempty" yaml:"preconditions,omitempty"`
|
||||
|
||||
GeneratePatterns `json:",omitempty" yaml:",omitempty"`
|
||||
}
|
||||
|
||||
type CloneList struct {
|
||||
// Namespace specifies source resource namespace.
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
|
@ -797,30 +826,55 @@ type CloneList struct {
|
|||
}
|
||||
|
||||
func (g *Generation) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
count := 0
|
||||
if g.GetData() != nil {
|
||||
count++
|
||||
}
|
||||
if g.Clone != (CloneFrom{}) {
|
||||
count++
|
||||
}
|
||||
if g.CloneList.Kinds != nil {
|
||||
count++
|
||||
}
|
||||
if g.ForEachGeneration != nil {
|
||||
count++
|
||||
}
|
||||
if count > 1 {
|
||||
errs = append(errs, field.Forbidden(path, "only one of generate patterns(data, clone, cloneList and foreach) can be specified"))
|
||||
return errs
|
||||
}
|
||||
|
||||
if g.ForEachGeneration != nil {
|
||||
for i, foreach := range g.ForEachGeneration {
|
||||
err := foreach.GeneratePatterns.Validate(path.Child("foreach").Index(i), namespaced, policyNamespace, clusterResources)
|
||||
errs = append(errs, err...)
|
||||
}
|
||||
return errs
|
||||
} else {
|
||||
return g.GeneratePatterns.Validate(path, namespaced, policyNamespace, clusterResources)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GeneratePatterns) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if namespaced {
|
||||
if err := g.validateNamespacedTargetsScope(clusterResources, policyNamespace); err != nil {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("namespace"), fmt.Sprintf("target resource scope mismatched: %v ", err)))
|
||||
errs = append(errs, field.Forbidden(path.Child("namespace"), fmt.Sprintf("target resource scope mismatched: %v ", err)))
|
||||
}
|
||||
}
|
||||
|
||||
if g.GetKind() != "" {
|
||||
if !clusterResources.Has(g.GetAPIVersion() + "/" + g.GetKind()) {
|
||||
if g.GetNamespace() == "" {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("namespace"), "target namespace must be set for a namespaced resource"))
|
||||
errs = append(errs, field.Forbidden(path.Child("namespace"), "target namespace must be set for a namespaced resource"))
|
||||
}
|
||||
} else {
|
||||
if g.GetNamespace() != "" {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("namespace"), "target namespace must not be set for a cluster-wide resource"))
|
||||
errs = append(errs, field.Forbidden(path.Child("namespace"), "target namespace must not be set for a cluster-wide resource"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generateType, _, _ := g.GetTypeAndSyncAndOrphanDownstream()
|
||||
if generateType == Data {
|
||||
return errs
|
||||
}
|
||||
|
||||
newGeneration := Generation{
|
||||
newGeneration := GeneratePatterns{
|
||||
ResourceSpec: ResourceSpec{
|
||||
Kind: g.ResourceSpec.GetKind(),
|
||||
APIVersion: g.ResourceSpec.GetAPIVersion(),
|
||||
|
@ -830,23 +884,25 @@ func (g *Generation) Validate(path *field.Path, namespaced bool, policyNamespace
|
|||
}
|
||||
|
||||
if err := regex.ObjectHasVariables(newGeneration); err != nil {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("clone/cloneList"), "Generation Rule Clone/CloneList should not have variables"))
|
||||
errs = append(errs, field.Forbidden(path.Child("clone/cloneList"), "Generation Rule Clone/CloneList should not have variables"))
|
||||
}
|
||||
|
||||
if len(g.CloneList.Kinds) == 0 {
|
||||
if g.Kind == "" {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("kind"), "kind can not be empty"))
|
||||
errs = append(errs, field.Forbidden(path.Child("kind"), "kind can not be empty"))
|
||||
}
|
||||
if g.Name == "" {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("name"), "name can not be empty"))
|
||||
errs = append(errs, field.Forbidden(path.Child("name"), "name can not be empty"))
|
||||
}
|
||||
if g.APIVersion == "" {
|
||||
errs = append(errs, field.Forbidden(path.Child("apiVersion"), "apiVersion can not be empty"))
|
||||
}
|
||||
}
|
||||
|
||||
errs = append(errs, g.ValidateCloneList(path.Child("generate"), namespaced, policyNamespace, clusterResources)...)
|
||||
return errs
|
||||
return append(errs, g.ValidateCloneList(path, namespaced, policyNamespace, clusterResources)...)
|
||||
}
|
||||
|
||||
func (g *Generation) ValidateCloneList(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
func (g *GeneratePatterns) ValidateCloneList(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if len(g.CloneList.Kinds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -883,15 +939,23 @@ func (g *Generation) ValidateCloneList(path *field.Path, namespaced bool, policy
|
|||
return errs
|
||||
}
|
||||
|
||||
func (g *Generation) GetData() apiextensions.JSON {
|
||||
func (g *GeneratePatterns) GetType() GenerateType {
|
||||
if g.RawData != nil {
|
||||
return Data
|
||||
}
|
||||
|
||||
return Clone
|
||||
}
|
||||
|
||||
func (g *GeneratePatterns) GetData() apiextensions.JSON {
|
||||
return FromJSON(g.RawData)
|
||||
}
|
||||
|
||||
func (g *Generation) SetData(in apiextensions.JSON) {
|
||||
func (g *GeneratePatterns) SetData(in apiextensions.JSON) {
|
||||
g.RawData = ToJSON(in)
|
||||
}
|
||||
|
||||
func (g *Generation) validateNamespacedTargetsScope(clusterResources sets.Set[string], policyNamespace string) error {
|
||||
func (g *GeneratePatterns) validateNamespacedTargetsScope(clusterResources sets.Set[string], policyNamespace string) error {
|
||||
target := g.ResourceSpec
|
||||
if clusterResources.Has(target.GetAPIVersion() + "/" + target.GetKind()) {
|
||||
return fmt.Errorf("the target must be a namespaced resource: %v/%v", target.GetAPIVersion(), target.GetKind())
|
||||
|
@ -916,13 +980,6 @@ const (
|
|||
Clone GenerateType = "Clone"
|
||||
)
|
||||
|
||||
func (g *Generation) GetTypeAndSyncAndOrphanDownstream() (GenerateType, bool, bool) {
|
||||
if g.RawData != nil {
|
||||
return Data, g.Synchronize, g.OrphanDownstreamOnPolicyDelete
|
||||
}
|
||||
return Clone, g.Synchronize, g.OrphanDownstreamOnPolicyDelete
|
||||
}
|
||||
|
||||
// CloneFrom provides the location of the source resource used to generate target resources.
|
||||
// The resource kind is derived from the match criteria.
|
||||
type CloneFrom struct {
|
||||
|
|
|
@ -179,11 +179,11 @@ func (r *Rule) IsPodSecurity() bool {
|
|||
return r.Validation.PodSecurity != nil
|
||||
}
|
||||
|
||||
func (r *Rule) GetTypeAndSyncAndOrphanDownstream() (_ GenerateType, sync bool, orphanDownstream bool) {
|
||||
func (r *Rule) GetSyncAndOrphanDownstream() (sync bool, orphanDownstream bool) {
|
||||
if !r.HasGenerate() {
|
||||
return
|
||||
}
|
||||
return r.Generation.GetTypeAndSyncAndOrphanDownstream()
|
||||
return r.Generation.Synchronize, r.Generation.OrphanDownstreamOnPolicyDelete
|
||||
}
|
||||
|
||||
func (r *Rule) GetAnyAllConditions() any {
|
||||
|
|
|
@ -537,6 +537,35 @@ func (in *DryRunOption) DeepCopy() *DryRunOption {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ForEachGeneration) DeepCopyInto(out *ForEachGeneration) {
|
||||
*out = *in
|
||||
if in.Context != nil {
|
||||
in, out := &in.Context, &out.Context
|
||||
*out = make([]ContextEntry, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.AnyAllConditions != nil {
|
||||
in, out := &in.AnyAllConditions, &out.AnyAllConditions
|
||||
*out = new(AnyAllConditions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.GeneratePatterns.DeepCopyInto(&out.GeneratePatterns)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForEachGeneration.
|
||||
func (in *ForEachGeneration) DeepCopy() *ForEachGeneration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ForEachGeneration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ForEachMutation) DeepCopyInto(out *ForEachMutation) {
|
||||
*out = *in
|
||||
|
@ -631,13 +660,8 @@ func (in *ForEachValidation) DeepCopy() *ForEachValidation {
|
|||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Generation) DeepCopyInto(out *Generation) {
|
||||
func (in *GeneratePatterns) DeepCopyInto(out *GeneratePatterns) {
|
||||
*out = *in
|
||||
if in.GenerateExisting != nil {
|
||||
in, out := &in.GenerateExisting, &out.GenerateExisting
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
out.ResourceSpec = in.ResourceSpec
|
||||
if in.RawData != nil {
|
||||
in, out := &in.RawData, &out.RawData
|
||||
|
@ -649,6 +673,35 @@ func (in *Generation) DeepCopyInto(out *Generation) {
|
|||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GeneratePatterns.
|
||||
func (in *GeneratePatterns) DeepCopy() *GeneratePatterns {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GeneratePatterns)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Generation) DeepCopyInto(out *Generation) {
|
||||
*out = *in
|
||||
if in.GenerateExisting != nil {
|
||||
in, out := &in.GenerateExisting, &out.GenerateExisting
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
in.GeneratePatterns.DeepCopyInto(&out.GeneratePatterns)
|
||||
if in.ForEachGeneration != nil {
|
||||
in, out := &in.ForEachGeneration, &out.ForEachGeneration
|
||||
*out = make([]ForEachGeneration, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Generation.
|
||||
func (in *Generation) DeepCopy() *Generation {
|
||||
if in == nil {
|
||||
|
|
|
@ -137,13 +137,6 @@ func (r *Rule) HasGenerate() bool {
|
|||
return !datautils.DeepEqual(r.Generation, kyvernov1.Generation{})
|
||||
}
|
||||
|
||||
func (r *Rule) GetGenerateTypeAndSync() (_ kyvernov1.GenerateType, sync bool, orphanDownstream bool) {
|
||||
if !r.HasGenerate() {
|
||||
return
|
||||
}
|
||||
return r.Generation.GetTypeAndSyncAndOrphanDownstream()
|
||||
}
|
||||
|
||||
// ValidateRuleType checks only one type of rule is defined per rule
|
||||
func (r *Rule) ValidateRuleType(path *field.Path) (errs field.ErrorList) {
|
||||
ruleTypes := []bool{r.HasMutate(), r.HasValidate(), r.HasGenerate(), r.HasVerifyImages()}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -664,6 +664,7 @@ It’s mutually exclusive with the URLPath field.</p>
|
|||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.Attestation">Attestation</a>,
|
||||
<a href="#kyverno.io/v1.ForEachGeneration">ForEachGeneration</a>,
|
||||
<a href="#kyverno.io/v1.ForEachMutation">ForEachMutation</a>,
|
||||
<a href="#kyverno.io/v1.ForEachValidation">ForEachValidation</a>)
|
||||
</p>
|
||||
|
@ -1200,7 +1201,7 @@ Timestamps (SCTs). If the value is unset, the default behavior by Cosign is used
|
|||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.Generation">Generation</a>)
|
||||
<a href="#kyverno.io/v1.GeneratePatterns">GeneratePatterns</a>)
|
||||
</p>
|
||||
<p>
|
||||
<p>CloneFrom provides the location of the source resource used to generate target resources.
|
||||
|
@ -1453,6 +1454,7 @@ of deployments across all namespaces.</p>
|
|||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.ForEachGeneration">ForEachGeneration</a>,
|
||||
<a href="#kyverno.io/v1.ForEachMutation">ForEachMutation</a>,
|
||||
<a href="#kyverno.io/v1.ForEachValidation">ForEachValidation</a>,
|
||||
<a href="#kyverno.io/v1.Rule">Rule</a>,
|
||||
|
@ -1645,6 +1647,79 @@ string
|
|||
<p>
|
||||
<p>FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled.</p>
|
||||
</p>
|
||||
<h3 id="kyverno.io/v1.ForEachGeneration">ForEachGeneration
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.Generation">Generation</a>)
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>list</code><br/>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>List specifies a JMESPath expression that results in one or more elements
|
||||
to which the validation logic is applied.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>context</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ContextEntry">
|
||||
[]ContextEntry
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Context defines variables and data sources that can be used during rule execution.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>preconditions</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.AnyAllConditions">
|
||||
AnyAllConditions
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>AnyAllConditions are used to determine if a policy rule should be applied by evaluating a
|
||||
set of conditions. The declaration can contain nested <code>any</code> or <code>all</code> statements.
|
||||
See: <a href="https://kyverno.io/docs/writing-policies/preconditions/">https://kyverno.io/docs/writing-policies/preconditions/</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>GeneratePatterns</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GeneratePatterns">
|
||||
GeneratePatterns
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.ForEachMutation">ForEachMutation
|
||||
</h3>
|
||||
<p>
|
||||
|
@ -1976,19 +2051,14 @@ ForEachValidationWrapper
|
|||
<p>
|
||||
<p>ForeachOrder specifies the iteration order in foreach statements.</p>
|
||||
</p>
|
||||
<h3 id="kyverno.io/v1.GenerateType">GenerateType
|
||||
(<code>string</code> alias)</p></h3>
|
||||
<p>
|
||||
</p>
|
||||
<h3 id="kyverno.io/v1.Generation">Generation
|
||||
<h3 id="kyverno.io/v1.GeneratePatterns">GeneratePatterns
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.Rule">Rule</a>,
|
||||
<a href="#kyverno.io/v2beta1.Rule">Rule</a>)
|
||||
<a href="#kyverno.io/v1.ForEachGeneration">ForEachGeneration</a>,
|
||||
<a href="#kyverno.io/v1.Generation">Generation</a>)
|
||||
</p>
|
||||
<p>
|
||||
<p>Generation defines how new resources should be created and managed.</p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
<thead class="thead-dark">
|
||||
|
@ -2000,19 +2070,6 @@ ForEachValidationWrapper
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>generateExisting</code><br/>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>GenerateExisting controls whether to trigger the rule in existing resources
|
||||
If is set to “true” the rule will be triggered and applied to existing matched resources.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>ResourceSpec</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ResourceSpec">
|
||||
|
@ -2026,36 +2083,6 @@ ResourceSpec
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>synchronize</code><br/>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Synchronize controls if generated resources should be kept in-sync with their source resource.
|
||||
If Synchronize is set to “true” changes to generated resources will be overwritten with resource
|
||||
data from Data or the resource specified in the Clone declaration.
|
||||
Optional. Defaults to “false” if not specified.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>orphanDownstreamOnPolicyDelete</code><br/>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>OrphanDownstreamOnPolicyDelete controls whether generated resources should be deleted when the rule that generated
|
||||
them is deleted with synchronization enabled. This option is only applicable to generate rules of the data type.
|
||||
See <a href="https://kyverno.io/docs/writing-policies/generate/#data-examples">https://kyverno.io/docs/writing-policies/generate/#data-examples</a>.
|
||||
Defaults to “false” if not specified.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>data</code><br/>
|
||||
<em>
|
||||
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#json-v1-apiextensions">
|
||||
|
@ -2103,6 +2130,101 @@ CloneList
|
|||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.GenerateType">GenerateType
|
||||
(<code>string</code> alias)</p></h3>
|
||||
<p>
|
||||
</p>
|
||||
<h3 id="kyverno.io/v1.Generation">Generation
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.Rule">Rule</a>,
|
||||
<a href="#kyverno.io/v2beta1.Rule">Rule</a>)
|
||||
</p>
|
||||
<p>
|
||||
<p>Generation defines how new resources should be created and managed.</p>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>generateExisting</code><br/>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>GenerateExisting controls whether to trigger the rule in existing resources
|
||||
If is set to “true” the rule will be triggered and applied to existing matched resources.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>synchronize</code><br/>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Synchronize controls if generated resources should be kept in-sync with their source resource.
|
||||
If Synchronize is set to “true” changes to generated resources will be overwritten with resource
|
||||
data from Data or the resource specified in the Clone declaration.
|
||||
Optional. Defaults to “false” if not specified.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>orphanDownstreamOnPolicyDelete</code><br/>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>OrphanDownstreamOnPolicyDelete controls whether generated resources should be deleted when the rule that generated
|
||||
them is deleted with synchronization enabled. This option is only applicable to generate rules of the data type.
|
||||
See <a href="https://kyverno.io/docs/writing-policies/generate/#data-examples">https://kyverno.io/docs/writing-policies/generate/#data-examples</a>.
|
||||
Defaults to “false” if not specified.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>GeneratePatterns</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.GeneratePatterns">
|
||||
GeneratePatterns
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>foreach</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ForEachGeneration">
|
||||
[]ForEachGeneration
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ForEach applies generate rules to a list of sub-elements by creating a context for each entry in the list and looping over it to apply the specified logic.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.GlobalContextEntryReference">GlobalContextEntryReference
|
||||
</h3>
|
||||
<p>
|
||||
|
@ -3577,7 +3699,7 @@ ResourceDescription
|
|||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.Generation">Generation</a>,
|
||||
<a href="#kyverno.io/v1.GeneratePatterns">GeneratePatterns</a>,
|
||||
<a href="#kyverno.io/v1.TargetResourceSpec">TargetResourceSpec</a>,
|
||||
<a href="#kyverno.io/v1beta1.UpdateRequestSpec">UpdateRequestSpec</a>,
|
||||
<a href="#kyverno.io/v1beta1.UpdateRequestStatus">UpdateRequestStatus</a>,
|
||||
|
|
|
@ -1332,6 +1332,7 @@ It's mutually exclusive with the URLPath field.</p>
|
|||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-Attestation">Attestation</a>,
|
||||
<a href="#kyverno-io-v1-ForEachGeneration">ForEachGeneration</a>,
|
||||
<a href="#kyverno-io-v1-ForEachMutation">ForEachMutation</a>,
|
||||
<a href="#kyverno-io-v1-ForEachValidation">ForEachValidation</a>)
|
||||
</p>
|
||||
|
@ -2454,7 +2455,7 @@ Timestamps (SCTs). If the value is unset, the default behavior by Cosign is used
|
|||
|
||||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-Generation">Generation</a>)
|
||||
<a href="#kyverno-io-v1-GeneratePatterns">GeneratePatterns</a>)
|
||||
</p>
|
||||
|
||||
|
||||
|
@ -2977,6 +2978,7 @@ of deployments across all namespaces.</p>
|
|||
|
||||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-ForEachGeneration">ForEachGeneration</a>,
|
||||
<a href="#kyverno-io-v1-ForEachMutation">ForEachMutation</a>,
|
||||
<a href="#kyverno-io-v1-ForEachValidation">ForEachValidation</a>,
|
||||
<a href="#kyverno-io-v1-Rule">Rule</a>,
|
||||
|
@ -3379,6 +3381,161 @@ Dryrun requires additional permissions. See config/dryrun/dryrun_rbac.yaml</p>
|
|||
|
||||
|
||||
|
||||
<H3 id="kyverno-io-v1-ForEachGeneration">ForEachGeneration
|
||||
</H3>
|
||||
|
||||
|
||||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-Generation">Generation</a>)
|
||||
</p>
|
||||
|
||||
|
||||
<p></p>
|
||||
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>list</code>
|
||||
|
||||
<span style="color:blue;"> *</span>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<span style="font-family: monospace">string</span>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>List specifies a JMESPath expression that results in one or more elements
|
||||
to which the validation logic is applied.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>context</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="#kyverno-io-v1-ContextEntry">
|
||||
<span style="font-family: monospace">[]ContextEntry</span>
|
||||
</a>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>Context defines variables and data sources that can be used during rule execution.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>preconditions</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="#kyverno-io-v1-AnyAllConditions">
|
||||
<span style="font-family: monospace">AnyAllConditions</span>
|
||||
</a>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>AnyAllConditions are used to determine if a policy rule should be applied by evaluating a
|
||||
set of conditions. The declaration can contain nested <code>any</code> or <code>all</code> statements.
|
||||
See: https://kyverno.io/docs/writing-policies/preconditions/</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>GeneratePatterns</code>
|
||||
|
||||
<span style="color:blue;"> *</span>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="#kyverno-io-v1-GeneratePatterns">
|
||||
<span style="font-family: monospace">GeneratePatterns</span>
|
||||
</a>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<H3 id="kyverno-io-v1-ForEachMutation">ForEachMutation
|
||||
</H3>
|
||||
|
||||
|
@ -4036,18 +4193,18 @@ must be satisfied for the validation rule to succeed.</p>
|
|||
|
||||
|
||||
|
||||
<H3 id="kyverno-io-v1-Generation">Generation
|
||||
<H3 id="kyverno-io-v1-GeneratePatterns">GeneratePatterns
|
||||
</H3>
|
||||
|
||||
|
||||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-Rule">Rule</a>)
|
||||
<a href="#kyverno-io-v1-ForEachGeneration">ForEachGeneration</a>,
|
||||
<a href="#kyverno-io-v1-Generation">Generation</a>)
|
||||
</p>
|
||||
|
||||
|
||||
<p><p>Generation defines how new resources should be created and managed.</p>
|
||||
</p>
|
||||
<p></p>
|
||||
|
||||
|
||||
<table class="table table-striped">
|
||||
|
@ -4068,34 +4225,6 @@ must be satisfied for the validation rule to succeed.</p>
|
|||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>generateExisting</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<span style="font-family: monospace">bool</span>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>GenerateExisting controls whether to trigger the rule in existing resources
|
||||
If is set to "true" the rule will be triggered and applied to existing matched resources.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>ResourceSpec</code>
|
||||
|
||||
|
@ -4127,66 +4256,6 @@ If is set to "true" the rule will be triggered and applied to existing
|
|||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>synchronize</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<span style="font-family: monospace">bool</span>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>Synchronize controls if generated resources should be kept in-sync with their source resource.
|
||||
If Synchronize is set to "true" changes to generated resources will be overwritten with resource
|
||||
data from Data or the resource specified in the Clone declaration.
|
||||
Optional. Defaults to "false" if not specified.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>orphanDownstreamOnPolicyDelete</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<span style="font-family: monospace">bool</span>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>OrphanDownstreamOnPolicyDelete controls whether generated resources should be deleted when the rule that generated
|
||||
them is deleted with synchronization enabled. This option is only applicable to generate rules of the data type.
|
||||
See https://kyverno.io/docs/writing-policies/generate/#data-examples.
|
||||
Defaults to "false" if not specified.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>data</code>
|
||||
|
||||
|
@ -4276,6 +4345,187 @@ resource will be created with default data only.</p>
|
|||
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<H3 id="kyverno-io-v1-Generation">Generation
|
||||
</H3>
|
||||
|
||||
|
||||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-Rule">Rule</a>)
|
||||
</p>
|
||||
|
||||
|
||||
<p><p>Generation defines how new resources should be created and managed.</p>
|
||||
</p>
|
||||
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>generateExisting</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<span style="font-family: monospace">bool</span>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>GenerateExisting controls whether to trigger the rule in existing resources
|
||||
If is set to "true" the rule will be triggered and applied to existing matched resources.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>synchronize</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<span style="font-family: monospace">bool</span>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>Synchronize controls if generated resources should be kept in-sync with their source resource.
|
||||
If Synchronize is set to "true" changes to generated resources will be overwritten with resource
|
||||
data from Data or the resource specified in the Clone declaration.
|
||||
Optional. Defaults to "false" if not specified.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>orphanDownstreamOnPolicyDelete</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<span style="font-family: monospace">bool</span>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>OrphanDownstreamOnPolicyDelete controls whether generated resources should be deleted when the rule that generated
|
||||
them is deleted with synchronization enabled. This option is only applicable to generate rules of the data type.
|
||||
See https://kyverno.io/docs/writing-policies/generate/#data-examples.
|
||||
Defaults to "false" if not specified.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>GeneratePatterns</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="#kyverno-io-v1-GeneratePatterns">
|
||||
<span style="font-family: monospace">GeneratePatterns</span>
|
||||
</a>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>foreach</code>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="#kyverno-io-v1-ForEachGeneration">
|
||||
<span style="font-family: monospace">[]ForEachGeneration</span>
|
||||
</a>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>ForEach applies generate rules to a list of sub-elements by creating a context for each entry in the list and looping over it to apply the specified logic.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -7080,7 +7330,7 @@ does not match an empty label set.</p>
|
|||
|
||||
<p>
|
||||
(<em>Appears in:</em>
|
||||
<a href="#kyverno-io-v1-Generation">Generation</a>,
|
||||
<a href="#kyverno-io-v1-GeneratePatterns">GeneratePatterns</a>,
|
||||
<a href="#kyverno-io-v1-TargetResourceSpec">TargetResourceSpec</a>)
|
||||
</p>
|
||||
|
||||
|
|
|
@ -13,15 +13,14 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, severSideApply bool, rule kyvernov1.Rule, client dclient.Interface) generateResponse {
|
||||
func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, severSideApply bool, pattern kyvernov1.GeneratePatterns, client dclient.Interface) generateResponse {
|
||||
source := sourceSpec
|
||||
clone := rule.Generation
|
||||
if clone.Clone.Name != "" {
|
||||
if pattern.Clone.Name != "" {
|
||||
source = kyvernov1.ResourceSpec{
|
||||
APIVersion: target.GetAPIVersion(),
|
||||
Kind: target.GetKind(),
|
||||
Namespace: clone.Clone.Namespace,
|
||||
Name: clone.Clone.Name,
|
||||
Namespace: pattern.Clone.Namespace,
|
||||
Name: pattern.Clone.Name,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,14 +79,13 @@ func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, sev
|
|||
return newCreateGenerateResponse(sourceObjCopy.UnstructuredContent(), target, nil)
|
||||
}
|
||||
|
||||
func manageCloneList(log logr.Logger, targetNamespace string, severSideApply bool, rule kyvernov1.Rule, client dclient.Interface) []generateResponse {
|
||||
func manageCloneList(log logr.Logger, targetNamespace string, severSideApply bool, pattern kyvernov1.GeneratePatterns, client dclient.Interface) []generateResponse {
|
||||
var responses []generateResponse
|
||||
cloneList := rule.Generation.CloneList
|
||||
sourceNamespace := cloneList.Namespace
|
||||
kinds := cloneList.Kinds
|
||||
sourceNamespace := pattern.CloneList.Namespace
|
||||
kinds := pattern.CloneList.Kinds
|
||||
for _, kind := range kinds {
|
||||
apiVersion, kind := kubeutils.GetKindFromGVK(kind)
|
||||
sources, err := client.ListResource(context.TODO(), apiVersion, kind, sourceNamespace, cloneList.Selector)
|
||||
sources, err := client.ListResource(context.TODO(), apiVersion, kind, sourceNamespace, pattern.CloneList.Selector)
|
||||
if err != nil {
|
||||
responses = append(responses,
|
||||
newSkipGenerateResponse(
|
||||
|
@ -101,13 +99,13 @@ func manageCloneList(log logr.Logger, targetNamespace string, severSideApply boo
|
|||
for _, source := range sources.Items {
|
||||
target := newResourceSpec(source.GetAPIVersion(), source.GetKind(), targetNamespace, source.GetName())
|
||||
|
||||
if (cloneList.Kinds != nil) && (source.GetNamespace() == target.GetNamespace()) {
|
||||
if (pattern.CloneList.Kinds != nil) && (source.GetNamespace() == target.GetNamespace()) {
|
||||
log.V(4).Info("skip resource self-clone")
|
||||
responses = append(responses, newSkipGenerateResponse(nil, target, nil))
|
||||
continue
|
||||
}
|
||||
responses = append(responses,
|
||||
manageClone(log, target, newResourceSpec(source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName()), severSideApply, rule, client))
|
||||
manageClone(log, target, newResourceSpec(source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName()), severSideApply, pattern, client))
|
||||
}
|
||||
}
|
||||
return responses
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
gojmespath "github.com/kyverno/go-jmespath"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
|
||||
"github.com/kyverno/kyverno/pkg/background/common"
|
||||
|
@ -21,7 +22,6 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||
"github.com/kyverno/kyverno/pkg/engine/variables"
|
||||
regex "github.com/kyverno/kyverno/pkg/engine/variables/regex"
|
||||
"github.com/kyverno/kyverno/pkg/event"
|
||||
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
||||
|
@ -278,6 +278,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
|
|||
ruleNameToProcessingTime := make(map[string]time.Duration)
|
||||
applyRules := policy.GetSpec().GetApplyRules()
|
||||
applyCount := 0
|
||||
log = log.WithValues("policy", policy.GetName(), "trigger", resource.GetNamespace()+"/"+resource.GetName())
|
||||
|
||||
for _, rule := range policy.GetSpec().Rules {
|
||||
var err error
|
||||
|
@ -310,21 +311,26 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
|
|||
break
|
||||
}
|
||||
logger := log.WithValues("rule", rule.Name)
|
||||
// add configmap json data to context
|
||||
if err := c.engine.ContextLoader(policy, rule)(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil {
|
||||
log.Error(err, "cannot add configmaps to context")
|
||||
return nil, err
|
||||
contextLoader := c.engine.ContextLoader(policy, rule)
|
||||
if err := contextLoader(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil {
|
||||
if _, ok := err.(gojmespath.NotFoundError); ok {
|
||||
logger.V(3).Info("failed to load rule level context", "reason", err.Error())
|
||||
} else {
|
||||
logger.Error(err, "failed to load rule level context")
|
||||
}
|
||||
return nil, fmt.Errorf("failed to load rule level context: %v", err)
|
||||
}
|
||||
|
||||
if rule, err = variables.SubstituteAllInRule(log, policyContext.JSONContext(), rule); err != nil {
|
||||
log.Error(err, "variable substitution failed for rule", "rule", rule.Name)
|
||||
return nil, err
|
||||
if rule.Generation.ForEachGeneration != nil {
|
||||
g := newForeachGenerator(c.client, logger, policyContext, policy, rule, rule.Context, rule.GetAnyAllConditions(), policyContext.NewResource(), rule.Generation.ForEachGeneration, contextLoader)
|
||||
genResource, err = g.generateForeach()
|
||||
} else {
|
||||
g := newGenerator(c.client, logger, policyContext, policy, rule, rule.Context, rule.GetAnyAllConditions(), policyContext.NewResource(), rule.Generation.GeneratePatterns, contextLoader)
|
||||
genResource, err = g.generate()
|
||||
}
|
||||
|
||||
g := newGenerator(c.client, logger, policy, rule, resource)
|
||||
genResource, err = g.generate()
|
||||
if err != nil {
|
||||
log.Error(err, "failed to apply generate rule", "policy", policy.GetName(), "rule", rule.Name, "resource", resource.GetName())
|
||||
log.Error(err, "failed to apply generate rule")
|
||||
return nil, err
|
||||
}
|
||||
ruleNameToProcessingTime[rule.Name] = time.Since(startTime)
|
||||
|
|
|
@ -2,31 +2,83 @@ package generate
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
gojmespath "github.com/kyverno/go-jmespath"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/background/common"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
|
||||
"github.com/kyverno/kyverno/pkg/engine/validate"
|
||||
"github.com/kyverno/kyverno/pkg/engine/variables"
|
||||
"go.uber.org/multierr"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
type generator struct {
|
||||
client dclient.Interface
|
||||
logger logr.Logger
|
||||
policy kyvernov1.PolicyInterface
|
||||
rule kyvernov1.Rule
|
||||
trigger unstructured.Unstructured
|
||||
client dclient.Interface
|
||||
logger logr.Logger
|
||||
policyContext engineapi.PolicyContext
|
||||
policy kyvernov1.PolicyInterface
|
||||
rule kyvernov1.Rule
|
||||
contextEntries []kyvernov1.ContextEntry
|
||||
anyAllConditions any
|
||||
trigger unstructured.Unstructured
|
||||
forEach []kyvernov1.ForEachGeneration
|
||||
pattern kyvernov1.GeneratePatterns
|
||||
contextLoader engineapi.EngineContextLoader
|
||||
}
|
||||
|
||||
func newGenerator(client dclient.Interface, logger logr.Logger, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, trigger unstructured.Unstructured) *generator {
|
||||
func newGenerator(client dclient.Interface,
|
||||
logger logr.Logger,
|
||||
policyContext engineapi.PolicyContext,
|
||||
policy kyvernov1.PolicyInterface,
|
||||
rule kyvernov1.Rule,
|
||||
contextEntries []kyvernov1.ContextEntry,
|
||||
anyAllConditions any,
|
||||
trigger unstructured.Unstructured,
|
||||
pattern kyvernov1.GeneratePatterns,
|
||||
contextLoader engineapi.EngineContextLoader,
|
||||
) *generator {
|
||||
return &generator{
|
||||
client: client,
|
||||
logger: logger,
|
||||
policy: policy,
|
||||
rule: rule,
|
||||
trigger: trigger,
|
||||
client: client,
|
||||
logger: logger,
|
||||
policyContext: policyContext,
|
||||
policy: policy,
|
||||
rule: rule,
|
||||
contextEntries: contextEntries,
|
||||
anyAllConditions: anyAllConditions,
|
||||
trigger: trigger,
|
||||
pattern: pattern,
|
||||
contextLoader: contextLoader,
|
||||
}
|
||||
}
|
||||
|
||||
func newForeachGenerator(client dclient.Interface,
|
||||
logger logr.Logger,
|
||||
policyContext engineapi.PolicyContext,
|
||||
policy kyvernov1.PolicyInterface,
|
||||
rule kyvernov1.Rule,
|
||||
contextEntries []kyvernov1.ContextEntry,
|
||||
anyAllConditions any,
|
||||
trigger unstructured.Unstructured,
|
||||
forEach []kyvernov1.ForEachGeneration,
|
||||
contextLoader engineapi.EngineContextLoader,
|
||||
) *generator {
|
||||
return &generator{
|
||||
client: client,
|
||||
logger: logger,
|
||||
policyContext: policyContext,
|
||||
policy: policy,
|
||||
rule: rule,
|
||||
contextEntries: contextEntries,
|
||||
anyAllConditions: anyAllConditions,
|
||||
trigger: trigger,
|
||||
forEach: forEach,
|
||||
contextLoader: contextLoader,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,16 +87,41 @@ func (g *generator) generate() ([]kyvernov1.ResourceSpec, error) {
|
|||
var err error
|
||||
var newGenResources []kyvernov1.ResourceSpec
|
||||
|
||||
target := g.rule.Generation.ResourceSpec
|
||||
if err := g.loadContext(context.TODO()); err != nil {
|
||||
return newGenResources, fmt.Errorf("failed to load context: %v", err)
|
||||
}
|
||||
|
||||
typeConditions, err := engineutils.TransformConditions(g.anyAllConditions)
|
||||
if err != nil {
|
||||
return newGenResources, fmt.Errorf("failed to parse preconditions: %v", err)
|
||||
}
|
||||
|
||||
preconditionsPassed, msg, err := variables.EvaluateConditions(g.logger, g.policyContext.JSONContext(), typeConditions)
|
||||
if err != nil {
|
||||
return newGenResources, fmt.Errorf("failed to evaluate preconditions: %v", err)
|
||||
}
|
||||
|
||||
if !preconditionsPassed {
|
||||
g.logger.V(2).Info("preconditions not met", "msg", msg)
|
||||
return newGenResources, nil
|
||||
}
|
||||
|
||||
pattern, err := variables.SubstituteAllInType(g.logger, g.policyContext.JSONContext(), &g.pattern)
|
||||
if err != nil {
|
||||
g.logger.Error(err, "variable substitution failed for rule", "rule", g.rule.Name)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
target := pattern.ResourceSpec
|
||||
logger := g.logger.WithValues("target", target.String())
|
||||
|
||||
if g.rule.Generation.Clone.Name != "" {
|
||||
resp := manageClone(logger.WithValues("type", "clone"), target, kyvernov1.ResourceSpec{}, g.policy.GetSpec().UseServerSideApply, g.rule, g.client)
|
||||
if pattern.Clone.Name != "" {
|
||||
resp := manageClone(logger.WithValues("type", "clone"), target, kyvernov1.ResourceSpec{}, g.policy.GetSpec().UseServerSideApply, *pattern, g.client)
|
||||
responses = append(responses, resp)
|
||||
} else if len(g.rule.Generation.CloneList.Kinds) != 0 {
|
||||
responses = manageCloneList(logger.WithValues("type", "cloneList"), target.GetNamespace(), g.policy.GetSpec().UseServerSideApply, g.rule, g.client)
|
||||
} else if len(pattern.CloneList.Kinds) != 0 {
|
||||
responses = manageCloneList(logger.WithValues("type", "cloneList"), target.GetNamespace(), g.policy.GetSpec().UseServerSideApply, *pattern, g.client)
|
||||
} else {
|
||||
resp := manageData(logger.WithValues("type", "data"), target, g.rule.Generation.RawData, g.rule.Generation.Synchronize, g.client)
|
||||
resp := manageData(logger.WithValues("type", "data"), target, pattern.RawData, g.rule.Generation.Synchronize, g.client)
|
||||
responses = append(responses, resp)
|
||||
}
|
||||
|
||||
|
@ -138,3 +215,76 @@ func (g *generator) generate() ([]kyvernov1.ResourceSpec, error) {
|
|||
}
|
||||
return newGenResources, nil
|
||||
}
|
||||
|
||||
func (g *generator) generateForeach() ([]kyvernov1.ResourceSpec, error) {
|
||||
var errors []error
|
||||
var genResources []kyvernov1.ResourceSpec
|
||||
|
||||
for i, foreach := range g.forEach {
|
||||
elements, err := engineutils.EvaluateList(foreach.List, g.policyContext.JSONContext())
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("failed to evaluate %v foreach list: %v", i, err))
|
||||
continue
|
||||
}
|
||||
gen, err := g.generateElements(foreach, elements, nil)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("failed to process %v foreach in rule %s: %v", i, g.rule.Name, err))
|
||||
}
|
||||
if gen != nil {
|
||||
genResources = append(genResources, gen...)
|
||||
}
|
||||
}
|
||||
return genResources, multierr.Combine(errors...)
|
||||
}
|
||||
|
||||
func (g *generator) generateElements(foreach kyvernov1.ForEachGeneration, elements []interface{}, elementScope *bool) ([]kyvernov1.ResourceSpec, error) {
|
||||
var errors []error
|
||||
var genResources []kyvernov1.ResourceSpec
|
||||
g.policyContext.JSONContext().Checkpoint()
|
||||
defer g.policyContext.JSONContext().Restore()
|
||||
|
||||
for index, element := range elements {
|
||||
if element == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
g.policyContext.JSONContext().Reset()
|
||||
policyContext := g.policyContext.Copy()
|
||||
if err := engineutils.AddElementToContext(policyContext, element, index, 0, elementScope); err != nil {
|
||||
g.logger.Error(err, "")
|
||||
errors = append(errors, fmt.Errorf("failed to add %v element to context: %v", index, err))
|
||||
continue
|
||||
}
|
||||
|
||||
gen, err := newGenerator(g.client,
|
||||
g.logger,
|
||||
policyContext,
|
||||
g.policy,
|
||||
g.rule,
|
||||
foreach.Context,
|
||||
foreach.AnyAllConditions,
|
||||
g.trigger,
|
||||
foreach.GeneratePatterns,
|
||||
g.contextLoader).
|
||||
generate()
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("failed to process %v element: %v", index, err))
|
||||
}
|
||||
if gen != nil {
|
||||
genResources = append(genResources, gen...)
|
||||
}
|
||||
}
|
||||
return genResources, multierr.Combine(errors...)
|
||||
}
|
||||
|
||||
func (g *generator) loadContext(ctx context.Context) error {
|
||||
if err := g.contextLoader(ctx, g.contextEntries, g.policyContext.JSONContext()); err != nil {
|
||||
if _, ok := err.(gojmespath.NotFoundError); ok {
|
||||
g.logger.V(3).Info("failed to load context", "reason", err.Error())
|
||||
} else {
|
||||
g.logger.Error(err, "failed to load context")
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
152
pkg/client/applyconfigurations/kyverno/v1/foreachgeneration.go
Normal file
152
pkg/client/applyconfigurations/kyverno/v1/foreachgeneration.go
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// ForEachGenerationApplyConfiguration represents an declarative configuration of the ForEachGeneration type for use
|
||||
// with apply.
|
||||
type ForEachGenerationApplyConfiguration struct {
|
||||
List *string `json:"list,omitempty"`
|
||||
Context []ContextEntryApplyConfiguration `json:"context,omitempty"`
|
||||
AnyAllConditions *AnyAllConditionsApplyConfiguration `json:"preconditions,omitempty"`
|
||||
*GeneratePatternsApplyConfiguration `json:"GeneratePatterns,omitempty"`
|
||||
}
|
||||
|
||||
// ForEachGenerationApplyConfiguration constructs an declarative configuration of the ForEachGeneration type for use with
|
||||
// apply.
|
||||
func ForEachGeneration() *ForEachGenerationApplyConfiguration {
|
||||
return &ForEachGenerationApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithList sets the List field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the List field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithList(value string) *ForEachGenerationApplyConfiguration {
|
||||
b.List = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithContext adds the given value to the Context field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Context field.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithContext(values ...*ContextEntryApplyConfiguration) *ForEachGenerationApplyConfiguration {
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithContext")
|
||||
}
|
||||
b.Context = append(b.Context, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAnyAllConditions sets the AnyAllConditions field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the AnyAllConditions field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithAnyAllConditions(value *AnyAllConditionsApplyConfiguration) *ForEachGenerationApplyConfiguration {
|
||||
b.AnyAllConditions = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the APIVersion field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithAPIVersion(value string) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.APIVersion = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithKind sets the Kind field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Kind field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithKind(value string) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Kind = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNamespace sets the Namespace field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Namespace field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithNamespace(value string) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Namespace = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithName sets the Name field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Name field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithName(value string) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Name = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithUID sets the UID field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the UID field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithUID(value types.UID) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.UID = &value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *ForEachGenerationApplyConfiguration) ensureResourceSpecApplyConfigurationExists() {
|
||||
if b.ResourceSpecApplyConfiguration == nil {
|
||||
b.ResourceSpecApplyConfiguration = &ResourceSpecApplyConfiguration{}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRawData sets the RawData field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the RawData field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.RawData = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithClone sets the Clone field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Clone field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithClone(value *CloneFromApplyConfiguration) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.Clone = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithCloneList sets the CloneList field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the CloneList field is set to the value of the last call.
|
||||
func (b *ForEachGenerationApplyConfiguration) WithCloneList(value *CloneListApplyConfiguration) *ForEachGenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.CloneList = value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *ForEachGenerationApplyConfiguration) ensureGeneratePatternsApplyConfigurationExists() {
|
||||
if b.GeneratePatternsApplyConfiguration == nil {
|
||||
b.GeneratePatternsApplyConfiguration = &GeneratePatternsApplyConfiguration{}
|
||||
}
|
||||
}
|
114
pkg/client/applyconfigurations/kyverno/v1/generatepatterns.go
Normal file
114
pkg/client/applyconfigurations/kyverno/v1/generatepatterns.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// GeneratePatternsApplyConfiguration represents an declarative configuration of the GeneratePatterns type for use
|
||||
// with apply.
|
||||
type GeneratePatternsApplyConfiguration struct {
|
||||
*ResourceSpecApplyConfiguration `json:"ResourceSpec,omitempty"`
|
||||
RawData *apiextensionsv1.JSON `json:"data,omitempty"`
|
||||
Clone *CloneFromApplyConfiguration `json:"clone,omitempty"`
|
||||
CloneList *CloneListApplyConfiguration `json:"cloneList,omitempty"`
|
||||
}
|
||||
|
||||
// GeneratePatternsApplyConfiguration constructs an declarative configuration of the GeneratePatterns type for use with
|
||||
// apply.
|
||||
func GeneratePatterns() *GeneratePatternsApplyConfiguration {
|
||||
return &GeneratePatternsApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the APIVersion field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithAPIVersion(value string) *GeneratePatternsApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.APIVersion = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithKind sets the Kind field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Kind field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithKind(value string) *GeneratePatternsApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Kind = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNamespace sets the Namespace field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Namespace field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithNamespace(value string) *GeneratePatternsApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Namespace = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithName sets the Name field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Name field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithName(value string) *GeneratePatternsApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.Name = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithUID sets the UID field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the UID field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithUID(value types.UID) *GeneratePatternsApplyConfiguration {
|
||||
b.ensureResourceSpecApplyConfigurationExists()
|
||||
b.UID = &value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *GeneratePatternsApplyConfiguration) ensureResourceSpecApplyConfigurationExists() {
|
||||
if b.ResourceSpecApplyConfiguration == nil {
|
||||
b.ResourceSpecApplyConfiguration = &ResourceSpecApplyConfiguration{}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRawData sets the RawData field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the RawData field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *GeneratePatternsApplyConfiguration {
|
||||
b.RawData = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithClone sets the Clone field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Clone field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithClone(value *CloneFromApplyConfiguration) *GeneratePatternsApplyConfiguration {
|
||||
b.Clone = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithCloneList sets the CloneList field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the CloneList field is set to the value of the last call.
|
||||
func (b *GeneratePatternsApplyConfiguration) WithCloneList(value *CloneListApplyConfiguration) *GeneratePatternsApplyConfiguration {
|
||||
b.CloneList = value
|
||||
return b
|
||||
}
|
|
@ -26,13 +26,11 @@ import (
|
|||
// GenerationApplyConfiguration represents an declarative configuration of the Generation type for use
|
||||
// with apply.
|
||||
type GenerationApplyConfiguration struct {
|
||||
GenerateExisting *bool `json:"generateExisting,omitempty"`
|
||||
*ResourceSpecApplyConfiguration `json:"ResourceSpec,omitempty"`
|
||||
Synchronize *bool `json:"synchronize,omitempty"`
|
||||
OrphanDownstreamOnPolicyDelete *bool `json:"orphanDownstreamOnPolicyDelete,omitempty"`
|
||||
RawData *apiextensionsv1.JSON `json:"data,omitempty"`
|
||||
Clone *CloneFromApplyConfiguration `json:"clone,omitempty"`
|
||||
CloneList *CloneListApplyConfiguration `json:"cloneList,omitempty"`
|
||||
GenerateExisting *bool `json:"generateExisting,omitempty"`
|
||||
Synchronize *bool `json:"synchronize,omitempty"`
|
||||
OrphanDownstreamOnPolicyDelete *bool `json:"orphanDownstreamOnPolicyDelete,omitempty"`
|
||||
*GeneratePatternsApplyConfiguration `json:"GeneratePatterns,omitempty"`
|
||||
ForEachGeneration []ForEachGenerationApplyConfiguration `json:"foreach,omitempty"`
|
||||
}
|
||||
|
||||
// GenerationApplyConfiguration constructs an declarative configuration of the Generation type for use with
|
||||
|
@ -49,6 +47,22 @@ func (b *GenerationApplyConfiguration) WithGenerateExisting(value bool) *Generat
|
|||
return b
|
||||
}
|
||||
|
||||
// WithSynchronize sets the Synchronize field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Synchronize field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithSynchronize(value bool) *GenerationApplyConfiguration {
|
||||
b.Synchronize = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithOrphanDownstreamOnPolicyDelete sets the OrphanDownstreamOnPolicyDelete field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the OrphanDownstreamOnPolicyDelete field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithOrphanDownstreamOnPolicyDelete(value bool) *GenerationApplyConfiguration {
|
||||
b.OrphanDownstreamOnPolicyDelete = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the APIVersion field is set to the value of the last call.
|
||||
|
@ -100,26 +114,11 @@ func (b *GenerationApplyConfiguration) ensureResourceSpecApplyConfigurationExist
|
|||
}
|
||||
}
|
||||
|
||||
// WithSynchronize sets the Synchronize field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Synchronize field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithSynchronize(value bool) *GenerationApplyConfiguration {
|
||||
b.Synchronize = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithOrphanDownstreamOnPolicyDelete sets the OrphanDownstreamOnPolicyDelete field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the OrphanDownstreamOnPolicyDelete field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithOrphanDownstreamOnPolicyDelete(value bool) *GenerationApplyConfiguration {
|
||||
b.OrphanDownstreamOnPolicyDelete = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithRawData sets the RawData field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the RawData field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *GenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.RawData = &value
|
||||
return b
|
||||
}
|
||||
|
@ -128,6 +127,7 @@ func (b *GenerationApplyConfiguration) WithRawData(value apiextensionsv1.JSON) *
|
|||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Clone field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithClone(value *CloneFromApplyConfiguration) *GenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.Clone = value
|
||||
return b
|
||||
}
|
||||
|
@ -136,6 +136,26 @@ func (b *GenerationApplyConfiguration) WithClone(value *CloneFromApplyConfigurat
|
|||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the CloneList field is set to the value of the last call.
|
||||
func (b *GenerationApplyConfiguration) WithCloneList(value *CloneListApplyConfiguration) *GenerationApplyConfiguration {
|
||||
b.ensureGeneratePatternsApplyConfigurationExists()
|
||||
b.CloneList = value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *GenerationApplyConfiguration) ensureGeneratePatternsApplyConfigurationExists() {
|
||||
if b.GeneratePatternsApplyConfiguration == nil {
|
||||
b.GeneratePatternsApplyConfiguration = &GeneratePatternsApplyConfiguration{}
|
||||
}
|
||||
}
|
||||
|
||||
// WithForEachGeneration adds the given value to the ForEachGeneration field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the ForEachGeneration field.
|
||||
func (b *GenerationApplyConfiguration) WithForEachGeneration(values ...*ForEachGenerationApplyConfiguration) *GenerationApplyConfiguration {
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithForEachGeneration")
|
||||
}
|
||||
b.ForEachGeneration = append(b.ForEachGeneration, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
|
|
@ -77,10 +77,14 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
|
|||
return &kyvernov1.DenyApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("DryRunOption"):
|
||||
return &kyvernov1.DryRunOptionApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("ForEachGeneration"):
|
||||
return &kyvernov1.ForEachGenerationApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("ForEachMutation"):
|
||||
return &kyvernov1.ForEachMutationApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("ForEachValidation"):
|
||||
return &kyvernov1.ForEachValidationApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("GeneratePatterns"):
|
||||
return &kyvernov1.GeneratePatternsApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("Generation"):
|
||||
return &kyvernov1.GenerationApplyConfiguration{}
|
||||
case v1.SchemeGroupVersion.WithKind("GlobalContextEntryReference"):
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||
)
|
||||
|
||||
// EngineContextLoader provides a function to load context entries from the various clients initialised with the engine ones
|
||||
// EngineContextLoader provides a function to load context entries from the various clients initialized with the engine ones
|
||||
type EngineContextLoader = func(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error
|
||||
|
||||
// EngineContextLoaderFactory provides an EngineContextLoader given a policy and rule name
|
||||
|
|
|
@ -36,18 +36,25 @@ func (pc *policyController) handleGenerate(policyKey string, policy kyvernov1.Po
|
|||
|
||||
func (pc *policyController) syncDataPolicyChanges(policy kyvernov1.PolicyInterface, deleteDownstream bool) error {
|
||||
var errs []error
|
||||
var err error
|
||||
ur := newGenerateUR(policy)
|
||||
for _, rule := range policy.GetSpec().Rules {
|
||||
generate := rule.Generation
|
||||
if !generate.Synchronize {
|
||||
continue
|
||||
}
|
||||
if generate.GetData() == nil {
|
||||
continue
|
||||
if generate.GetData() != nil {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, rule.Name, generate.GeneratePatterns, deleteDownstream, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
var err error
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, rule, deleteDownstream, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
|
||||
for _, foreach := range generate.ForEachGeneration {
|
||||
if foreach.GetData() != nil {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, rule.Name, foreach.GeneratePatterns, deleteDownstream, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,6 +144,7 @@ func (pc *policyController) handleGenerateForExisting(policy kyvernov1.PolicyInt
|
|||
|
||||
func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.PolicyInterface) error {
|
||||
var errs []error
|
||||
var err error
|
||||
rules := autogen.ComputeRules(policy, "")
|
||||
ur := newGenerateUR(policy)
|
||||
for _, r := range rules {
|
||||
|
@ -144,14 +152,23 @@ func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.Polic
|
|||
if !generate.Synchronize {
|
||||
continue
|
||||
}
|
||||
if generate.GetData() == nil {
|
||||
continue
|
||||
|
||||
sync, orphanDownstreamOnPolicyDelete := r.GetSyncAndOrphanDownstream()
|
||||
if generate.GetData() != nil {
|
||||
if sync && (generate.GetType() == kyvernov1.Data) && !orphanDownstreamOnPolicyDelete {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, r.Name, r.Generation.GeneratePatterns, true, true); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
generateType, sync, orphanDownstreamOnPolicyDelete := r.GetTypeAndSyncAndOrphanDownstream()
|
||||
if sync && (generateType == kyvernov1.Data) && !orphanDownstreamOnPolicyDelete {
|
||||
var err error
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, r, true, true); err != nil {
|
||||
errs = append(errs, err)
|
||||
|
||||
for _, foreach := range generate.ForEachGeneration {
|
||||
if foreach.GetData() != nil {
|
||||
if sync && (foreach.GetType() == kyvernov1.Data) && !orphanDownstreamOnPolicyDelete {
|
||||
if ur, err = pc.buildUrForDataRuleChanges(policy, ur, r.Name, foreach.GeneratePatterns, true, true); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,15 +194,15 @@ func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.Polic
|
|||
return multierr.Combine(errs...)
|
||||
}
|
||||
|
||||
func (pc *policyController) buildUrForDataRuleChanges(policy kyvernov1.PolicyInterface, ur *kyvernov2.UpdateRequest, rule kyvernov1.Rule, deleteDownstream, policyDeletion bool) (*kyvernov2.UpdateRequest, error) {
|
||||
func (pc *policyController) buildUrForDataRuleChanges(policy kyvernov1.PolicyInterface, ur *kyvernov2.UpdateRequest, ruleName string, pattern kyvernov1.GeneratePatterns, deleteDownstream, policyDeletion bool) (*kyvernov2.UpdateRequest, error) {
|
||||
labels := map[string]string{
|
||||
common.GeneratePolicyLabel: policy.GetName(),
|
||||
common.GeneratePolicyNamespaceLabel: policy.GetNamespace(),
|
||||
common.GenerateRuleLabel: rule.Name,
|
||||
common.GenerateRuleLabel: ruleName,
|
||||
kyverno.LabelAppManagedBy: kyverno.ValueKyvernoApp,
|
||||
}
|
||||
|
||||
downstreams, err := common.FindDownstream(pc.client, rule.Generation.GetAPIVersion(), rule.Generation.GetKind(), labels)
|
||||
downstreams, err := common.FindDownstream(pc.client, pattern.GetAPIVersion(), pattern.GetKind(), labels)
|
||||
if err != nil {
|
||||
return ur, err
|
||||
}
|
||||
|
@ -198,7 +215,7 @@ func (pc *policyController) buildUrForDataRuleChanges(policy kyvernov1.PolicyInt
|
|||
for _, downstream := range downstreams.Items {
|
||||
labels := downstream.GetLabels()
|
||||
trigger := generateutils.TriggerFromLabels(labels)
|
||||
addRuleContext(ur, rule.Name, trigger, deleteDownstream)
|
||||
addRuleContext(ur, ruleName, trigger, deleteDownstream)
|
||||
if policyDeletion {
|
||||
addGeneratedResources(ur, downstream)
|
||||
}
|
||||
|
|
|
@ -41,35 +41,6 @@ func NewGenerateFactory(client dclient.Interface, rule kyvernov1.Generation, use
|
|||
// Validate validates the 'generate' rule
|
||||
func (g *Generate) Validate(ctx context.Context) (string, error) {
|
||||
rule := g.rule
|
||||
if rule.GetData() != nil && rule.Clone != (kyvernov1.CloneFrom{}) {
|
||||
return "", fmt.Errorf("only one of data or clone can be specified")
|
||||
}
|
||||
|
||||
if rule.Clone != (kyvernov1.CloneFrom{}) && len(rule.CloneList.Kinds) != 0 {
|
||||
return "", fmt.Errorf("only one of clone or cloneList can be specified")
|
||||
}
|
||||
|
||||
apiVersion, kind, name, namespace := rule.ResourceSpec.GetAPIVersion(), rule.ResourceSpec.GetKind(), rule.ResourceSpec.GetName(), rule.ResourceSpec.GetNamespace()
|
||||
|
||||
if len(rule.CloneList.Kinds) == 0 {
|
||||
if name == "" {
|
||||
return "name", fmt.Errorf("name cannot be empty")
|
||||
}
|
||||
if kind == "" {
|
||||
return "kind", fmt.Errorf("kind cannot be empty")
|
||||
}
|
||||
if apiVersion == "" {
|
||||
return "apiVersion", fmt.Errorf("apiVersion cannot be empty")
|
||||
}
|
||||
} else {
|
||||
if name != "" {
|
||||
return "name", fmt.Errorf("with cloneList, generate.name. should not be specified")
|
||||
}
|
||||
if kind != "" {
|
||||
return "kind", fmt.Errorf("with cloneList, generate.kind. should not be specified")
|
||||
}
|
||||
}
|
||||
|
||||
if rule.CloneList.Selector != nil {
|
||||
if wildcard.ContainsWildcard(rule.CloneList.Selector.String()) {
|
||||
return "selector", fmt.Errorf("wildcard characters `*/?` not supported")
|
||||
|
@ -89,22 +60,33 @@ func (g *Generate) Validate(ctx context.Context) (string, error) {
|
|||
// instructions to modify the RBAC for kyverno are mentioned at https://github.com/kyverno/kyverno/blob/master/documentation/installation.md
|
||||
// - operations required: create/update/delete/get
|
||||
// If kind and namespace contain variables, then we cannot resolve then so we skip the processing
|
||||
if len(rule.CloneList.Kinds) != 0 {
|
||||
for _, kind = range rule.CloneList.Kinds {
|
||||
gvk, sub := parseCloneKind(kind)
|
||||
if err := g.canIGenerate(ctx, gvk, namespace, sub); err != nil {
|
||||
return "", err
|
||||
if rule.ForEachGeneration != nil {
|
||||
for _, forEach := range rule.ForEachGeneration {
|
||||
if err := g.canIGeneratePatterns(ctx, forEach.GeneratePatterns); err != nil {
|
||||
return "foreach", err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
k, sub := kubeutils.SplitSubresource(kind)
|
||||
if err := g.canIGenerate(ctx, strings.Join([]string{apiVersion, k}, "/"), namespace, sub); err != nil {
|
||||
if err := g.canIGeneratePatterns(ctx, rule.GeneratePatterns); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (g *Generate) canIGeneratePatterns(ctx context.Context, generate kyvernov1.GeneratePatterns) error {
|
||||
if len(generate.CloneList.Kinds) != 0 {
|
||||
for _, kind := range generate.CloneList.Kinds {
|
||||
gvk, sub := parseCloneKind(kind)
|
||||
return g.canIGenerate(ctx, gvk, generate.Namespace, sub)
|
||||
}
|
||||
} else {
|
||||
k, sub := kubeutils.SplitSubresource(generate.Kind)
|
||||
return g.canIGenerate(ctx, strings.Join([]string{generate.APIVersion, k}, "/"), generate.Namespace, sub)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// canIGenerate returns a error if kyverno cannot perform operations
|
||||
func (g *Generate) canIGenerate(ctx context.Context, gvk, namespace, subresource string) error {
|
||||
// Skip if there is variable defined
|
||||
|
|
|
@ -9,37 +9,6 @@ import (
|
|||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func Test_Validate_Generate(t *testing.T) {
|
||||
rawGenerate := []byte(`
|
||||
{
|
||||
"kind": "NetworkPolicy",
|
||||
"name": "defaultnetworkpolicy",
|
||||
"data": {
|
||||
"spec": {
|
||||
"podSelector": {},
|
||||
"policyTypes": [
|
||||
"Ingress",
|
||||
"Egress"
|
||||
],
|
||||
"ingress": [
|
||||
{}
|
||||
],
|
||||
"egress": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
var genRule kyverno.Generation
|
||||
err := json.Unmarshal(rawGenerate, &genRule)
|
||||
assert.NilError(t, err)
|
||||
checker := NewFakeGenerate(genRule)
|
||||
_, err = checker.Validate(context.TODO())
|
||||
t.Log(err)
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
func Test_Validate_Generate_HasAnchors(t *testing.T) {
|
||||
var err error
|
||||
rawGenerate := []byte(`
|
||||
|
|
|
@ -726,6 +726,9 @@ func buildContext(rule *kyvernov1.Rule, background bool, target bool) *enginecon
|
|||
for _, fe := range rule.Mutation.Targets {
|
||||
addContextVariables(fe.Context, ctx)
|
||||
}
|
||||
for _, fe := range rule.Generation.ForEachGeneration {
|
||||
addContextVariables(fe.Context, ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: zk-kafka-address-foreach
|
||||
spec:
|
||||
rules:
|
||||
- match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- ConfigMap
|
||||
name: k-kafka-address
|
||||
context:
|
||||
- name: configmapns
|
||||
variable:
|
||||
jmesPath: request.object.metadata.namespace
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{configmapns}}'
|
||||
operator: Equals
|
||||
value: 'default'
|
||||
generate:
|
||||
generateExisting: false
|
||||
synchronize: true
|
||||
foreach:
|
||||
- list: request.object.data.namespaces | split(@, ',')
|
||||
context:
|
||||
- name: ns
|
||||
variable:
|
||||
jmesPath: element
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{ ns }}'
|
||||
operator: AnyIn
|
||||
value:
|
||||
- foreach-ns-1
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
name: my-networkpolicy-{{ elementIndex }}-{{ ns }}
|
||||
namespace: '{{ ns }}'
|
||||
data:
|
||||
metadata:
|
||||
labels:
|
||||
request.namespace: '{{ request.object.metadata.name }}'
|
||||
element.namespace: '{{ ns }}'
|
||||
element.name: '{{ element }}'
|
||||
elementIndex: '{{ elementIndex }}'
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: zk-kafka-address-foreach
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-2
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: default
|
||||
data:
|
||||
namespaces: foreach-ns-1,foreach-ns-2
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: my-networkpolicy-0-foreach-ns-1
|
||||
namespace: foreach-ns-1
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: my-networkpolicy-0-foreach-ns-2
|
||||
namespace: foreach-ns-2
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
|
@ -0,0 +1,11 @@
|
|||
## Description
|
||||
|
||||
This is a basic creation test for a "generate foreach data" policy with preconditions and context variables. It checks that the basic functionality works whereby installation of the policy causes correct evaluation of the match and preconditions blocks.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
If only the `foreach-ns-1` Namespace receives a generated NetworkPolicy, the test passes. If either it does not or `foreach-ns-2` receives a NetworkPolicy, the test fails.
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/3542
|
|
@ -0,0 +1,21 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-data-sync-create
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: 1-1-policy.yaml
|
||||
- assert:
|
||||
file: 1-2-policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: 2-1-trigger.yaml
|
||||
- assert:
|
||||
file: 2-2-netpol.yaml
|
||||
- error:
|
||||
file: 2-3-netpol.yaml
|
|
@ -0,0 +1,52 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-data-sync-delete-policy
|
||||
spec:
|
||||
rules:
|
||||
- match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- ConfigMap
|
||||
name: k-kafka-address
|
||||
context:
|
||||
- name: configmapns
|
||||
variable:
|
||||
jmesPath: request.object.metadata.namespace
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{configmapns}}'
|
||||
operator: Equals
|
||||
value: 'default'
|
||||
generate:
|
||||
generateExisting: false
|
||||
synchronize: true
|
||||
foreach:
|
||||
- list: request.object.data.namespaces | split(@, ',')
|
||||
context:
|
||||
- name: ns
|
||||
variable:
|
||||
jmesPath: element
|
||||
preconditions:
|
||||
any:
|
||||
- key: '{{ ns }}'
|
||||
operator: AnyIn
|
||||
value:
|
||||
- foreach-ns-1
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
name: my-networkpolicy-{{ elementIndex }}-{{ ns }}
|
||||
namespace: '{{ ns }}'
|
||||
data:
|
||||
metadata:
|
||||
labels:
|
||||
request.namespace: '{{ request.object.metadata.name }}'
|
||||
element.namespace: '{{ ns }}'
|
||||
element.name: '{{ element }}'
|
||||
elementIndex: '{{ elementIndex }}'
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: foreach-cpol-data-sync-delete-policy
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: foreach-ns-1
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: default
|
||||
data:
|
||||
namespaces: foreach-ns-1
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: my-networkpolicy-0-foreach-ns-1
|
||||
namespace: foreach-ns-1
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: my-networkpolicy-0-foreach-ns-2
|
||||
namespace: foreach-ns-2
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
|
@ -0,0 +1,11 @@
|
|||
## Description
|
||||
|
||||
This test checks the synchronize behavior for a "generate foreach data" policy.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The test passes if the generated NetworkPolicy in `foreach-ns-1` Namespace is deleted upon policy deletion.
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/3542
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-data-sync-create
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: 1-1-policy.yaml
|
||||
- assert:
|
||||
file: 1-2-policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: 2-1-trigger.yaml
|
||||
- assert:
|
||||
file: 2-2-netpol.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- delete:
|
||||
ref:
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: foreach-cpol-data-sync-delete-policy
|
||||
- error:
|
||||
file: 2-2-netpol.yaml
|
Loading…
Reference in a new issue