2023-02-22 10:49:09 +00:00
package policy
import (
"context"
"fmt"
2023-07-06 08:00:36 +00:00
"github.com/kyverno/kyverno/api/kyverno"
2023-02-22 10:49:09 +00:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-06-20 09:44:43 +00:00
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
2024-10-16 13:24:37 +00:00
"github.com/kyverno/kyverno/pkg/autogen"
2023-02-22 10:49:09 +00:00
"github.com/kyverno/kyverno/pkg/background/common"
2024-09-24 14:11:14 +00:00
backgroundcommon "github.com/kyverno/kyverno/pkg/background/common"
2023-02-22 10:49:09 +00:00
generateutils "github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/config"
2024-09-24 14:11:14 +00:00
datautils "github.com/kyverno/kyverno/pkg/utils/data"
2024-08-13 17:14:06 +00:00
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
2023-02-22 10:49:09 +00:00
"go.uber.org/multierr"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2024-06-12 15:23:53 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2024-09-24 14:11:14 +00:00
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2023-02-22 10:49:09 +00:00
)
2023-04-13 11:29:40 +00:00
func ( pc * policyController ) handleGenerate ( policyKey string , policy kyvernov1 . PolicyInterface ) error {
2023-02-22 10:49:09 +00:00
logger := pc . log . WithName ( "handleGenerate" ) . WithName ( policyKey )
logger . Info ( "update URs on policy event" )
2023-06-14 13:52:27 +00:00
if err := pc . syncDataPolicyChanges ( policy , false ) ; err != nil {
logger . Error ( err , "failed to create UR on policy event" )
return err
}
2023-03-06 15:40:49 +00:00
2023-06-15 15:32:19 +00:00
logger . V ( 4 ) . Info ( "reconcile policy with generateExisting enabled" )
2023-06-14 13:52:27 +00:00
if err := pc . handleGenerateForExisting ( policy ) ; err != nil {
logger . Error ( err , "failed to create UR for generateExisting" )
return err
2023-02-22 10:49:09 +00:00
}
return nil
}
2024-08-13 17:14:06 +00:00
func ( pc * policyController ) syncDataPolicyChanges ( policy kyvernov1 . PolicyInterface , deleteDownstream bool ) error {
var errs [ ] error
2024-08-19 06:55:19 +00:00
var err error
2024-08-13 17:14:06 +00:00
ur := newGenerateUR ( policy )
for _ , rule := range policy . GetSpec ( ) . Rules {
2024-09-09 13:10:02 +00:00
if ! rule . HasGenerate ( ) {
continue
}
2024-08-13 17:14:06 +00:00
generate := rule . Generation
if ! generate . Synchronize {
continue
}
2024-08-19 06:55:19 +00:00
if generate . GetData ( ) != nil {
2024-08-29 11:59:22 +00:00
if ur , err = pc . buildUrForDataRuleChanges ( policy , ur , rule . Name , generate . GeneratePattern , deleteDownstream , false ) ; err != nil {
2024-08-19 06:55:19 +00:00
errs = append ( errs , err )
}
2024-08-13 17:14:06 +00:00
}
2024-08-19 06:55:19 +00:00
for _ , foreach := range generate . ForEachGeneration {
if foreach . GetData ( ) != nil {
2024-08-29 11:59:22 +00:00
if ur , err = pc . buildUrForDataRuleChanges ( policy , ur , rule . Name , foreach . GeneratePattern , deleteDownstream , false ) ; err != nil {
2024-08-19 06:55:19 +00:00
errs = append ( errs , err )
}
}
2024-08-13 17:14:06 +00:00
}
}
if len ( ur . Spec . RuleContext ) == 0 {
return multierr . Combine ( errs ... )
}
pc . log . V ( 2 ) . WithName ( "syncDataPolicyChanges" ) . Info ( "creating new UR for generate" )
created , err := pc . urGenerator . Generate ( context . TODO ( ) , pc . kyvernoClient , ur , pc . log )
if err != nil {
errs = append ( errs , err )
}
if created != nil {
updated := created . DeepCopy ( )
updated . Status . State = kyvernov2 . Pending
_ , err = pc . kyvernoClient . KyvernoV2 ( ) . UpdateRequests ( config . KyvernoNamespace ( ) ) . UpdateStatus ( context . TODO ( ) , updated , metav1 . UpdateOptions { } )
if err != nil {
errs = append ( errs , err )
}
}
return multierr . Combine ( errs ... )
}
2023-06-14 13:52:27 +00:00
func ( pc * policyController ) handleGenerateForExisting ( policy kyvernov1 . PolicyInterface ) error {
2023-03-06 15:40:49 +00:00
var errors [ ] error
2024-06-12 15:23:53 +00:00
var triggers [ ] * unstructured . Unstructured
policyNew := policy . CreateDeepCopy ( )
policyNew . GetSpec ( ) . Rules = nil
2024-08-13 17:14:06 +00:00
ur := newGenerateUR ( policy )
logger := pc . log . WithName ( "handleGenerateForExisting" )
for _ , rule := range policy . GetSpec ( ) . Rules {
if ! rule . HasGenerate ( ) {
continue
}
2024-08-08 12:11:20 +00:00
// check if the rule sets the generateExisting field.
// if not, use the policy level setting
generateExisting := rule . Generation . GenerateExisting
if generateExisting != nil {
if ! * generateExisting {
continue
}
2024-08-13 17:14:06 +00:00
} else if ! policy . GetSpec ( ) . GenerateExisting {
2024-08-08 12:11:20 +00:00
continue
}
2024-06-12 15:23:53 +00:00
triggers = getTriggers ( pc . client , rule , policy . IsNamespaced ( ) , policy . GetNamespace ( ) , pc . log )
policyNew . GetSpec ( ) . SetRules ( [ ] kyvernov1 . Rule { rule } )
2023-06-14 13:52:27 +00:00
for _ , trigger := range triggers {
2024-08-13 17:14:06 +00:00
namespaceLabels := engineutils . GetNamespaceSelectorsFromNamespaceLister ( trigger . GetKind ( ) , trigger . GetNamespace ( ) , pc . nsLister , pc . log )
policyContext , err := common . NewBackgroundContext ( pc . log , pc . client , ur . Spec . Context , policy , trigger , pc . configuration , pc . jp , namespaceLabels )
2023-06-14 13:52:27 +00:00
if err != nil {
2024-08-13 17:14:06 +00:00
errors = append ( errors , fmt . Errorf ( "failed to build policy context for rule %s: %w" , rule . Name , err ) )
2023-06-14 13:52:27 +00:00
continue
}
2024-08-13 17:14:06 +00:00
engineResponse := pc . engine . ApplyBackgroundChecks ( context . TODO ( ) , policyContext )
if len ( engineResponse . PolicyResponse . Rules ) == 0 {
2023-06-14 13:52:27 +00:00
continue
}
2024-08-13 17:14:06 +00:00
logger . V ( 4 ) . Info ( "adding rule context" , "rule" , rule . Name , "trigger" , trigger . GetNamespace ( ) + "/" + trigger . GetName ( ) )
addRuleContext ( ur , rule . Name , common . ResourceSpecFromUnstructured ( * trigger ) , false )
}
}
if len ( ur . Spec . RuleContext ) == 0 {
return multierr . Combine ( errors ... )
}
2023-03-06 15:40:49 +00:00
2024-08-13 17:14:06 +00:00
logger . V ( 2 ) . Info ( "creating new UR for generate" )
created , err := pc . urGenerator . Generate ( context . TODO ( ) , pc . kyvernoClient , ur , pc . log )
if err != nil {
errors = append ( errors , err )
return multierr . Combine ( errors ... )
}
if created != nil {
updated := created . DeepCopy ( )
updated . Status . State = kyvernov2 . Pending
_ , err = pc . kyvernoClient . KyvernoV2 ( ) . UpdateRequests ( config . KyvernoNamespace ( ) ) . UpdateStatus ( context . TODO ( ) , updated , metav1 . UpdateOptions { } )
if err != nil {
errors = append ( errors , err )
return multierr . Combine ( errors ... )
2023-06-14 13:52:27 +00:00
}
2024-08-13 17:14:06 +00:00
pc . log . V ( 4 ) . Info ( "successfully created UR on policy update" , "policy" , policyNew . GetName ( ) )
2023-02-22 10:49:09 +00:00
}
2023-03-06 15:40:49 +00:00
return multierr . Combine ( errors ... )
2023-02-22 10:49:09 +00:00
}
2023-04-13 11:29:40 +00:00
func ( pc * policyController ) createURForDownstreamDeletion ( policy kyvernov1 . PolicyInterface ) error {
2023-03-01 03:48:18 +00:00
var errs [ ] error
2024-08-19 06:55:19 +00:00
var err error
2024-10-16 13:24:37 +00:00
rules := autogen . Default . ComputeRules ( policy , "" )
2024-08-13 17:14:06 +00:00
ur := newGenerateUR ( policy )
2023-03-01 03:48:18 +00:00
for _ , r := range rules {
2024-09-09 13:10:02 +00:00
if ! r . HasGenerate ( ) {
continue
}
2024-08-13 17:14:06 +00:00
generate := r . Generation
if ! generate . Synchronize {
continue
}
2024-08-19 06:55:19 +00:00
sync , orphanDownstreamOnPolicyDelete := r . GetSyncAndOrphanDownstream ( )
if generate . GetData ( ) != nil {
if sync && ( generate . GetType ( ) == kyvernov1 . Data ) && ! orphanDownstreamOnPolicyDelete {
2024-08-29 11:59:22 +00:00
if ur , err = pc . buildUrForDataRuleChanges ( policy , ur , r . Name , r . Generation . GeneratePattern , true , true ) ; err != nil {
2024-08-19 06:55:19 +00:00
errs = append ( errs , err )
}
}
2024-08-13 17:14:06 +00:00
}
2024-08-19 06:55:19 +00:00
for _ , foreach := range generate . ForEachGeneration {
if foreach . GetData ( ) != nil {
if sync && ( foreach . GetType ( ) == kyvernov1 . Data ) && ! orphanDownstreamOnPolicyDelete {
2024-08-29 11:59:22 +00:00
if ur , err = pc . buildUrForDataRuleChanges ( policy , ur , r . Name , foreach . GeneratePattern , true , true ) ; err != nil {
2024-08-19 06:55:19 +00:00
errs = append ( errs , err )
}
}
2023-03-01 03:48:18 +00:00
}
}
}
2024-08-13 17:14:06 +00:00
if len ( ur . Spec . RuleContext ) == 0 {
return multierr . Combine ( errs ... )
}
pc . log . V ( 2 ) . WithName ( "createURForDownstreamDeletion" ) . Info ( "creating new UR for generate" )
created , err := pc . urGenerator . Generate ( context . TODO ( ) , pc . kyvernoClient , ur , pc . log )
if err != nil {
errs = append ( errs , err )
}
if created != nil {
updated := created . DeepCopy ( )
updated . Status . State = kyvernov2 . Pending
updated . Status . GeneratedResources = ur . Status . GeneratedResources
_ , err = pc . kyvernoClient . KyvernoV2 ( ) . UpdateRequests ( config . KyvernoNamespace ( ) ) . UpdateStatus ( context . TODO ( ) , updated , metav1 . UpdateOptions { } )
if err != nil {
errs = append ( errs , err )
2023-02-22 10:49:09 +00:00
}
2023-06-14 13:52:27 +00:00
}
2024-08-13 17:14:06 +00:00
return multierr . Combine ( errs ... )
2023-06-14 13:52:27 +00:00
}
2023-02-22 10:49:09 +00:00
2024-08-29 11:59:22 +00:00
func ( pc * policyController ) buildUrForDataRuleChanges ( policy kyvernov1 . PolicyInterface , ur * kyvernov2 . UpdateRequest , ruleName string , pattern kyvernov1 . GeneratePattern , deleteDownstream , policyDeletion bool ) ( * kyvernov2 . UpdateRequest , error ) {
2023-06-14 13:52:27 +00:00
labels := map [ string ] string {
common . GeneratePolicyLabel : policy . GetName ( ) ,
common . GeneratePolicyNamespaceLabel : policy . GetNamespace ( ) ,
2024-08-19 06:55:19 +00:00
common . GenerateRuleLabel : ruleName ,
2023-07-06 08:00:36 +00:00
kyverno . LabelAppManagedBy : kyverno . ValueKyvernoApp ,
2023-06-14 13:52:27 +00:00
}
2024-08-19 06:55:19 +00:00
downstreams , err := common . FindDownstream ( pc . client , pattern . GetAPIVersion ( ) , pattern . GetKind ( ) , labels )
2023-06-14 13:52:27 +00:00
if err != nil {
2024-08-13 17:14:06 +00:00
return ur , err
}
if len ( downstreams . Items ) == 0 {
return ur , nil
2023-06-14 13:52:27 +00:00
}
2023-06-15 15:32:19 +00:00
pc . log . V ( 4 ) . Info ( "sync data rule changes to downstream targets" )
2023-06-14 13:52:27 +00:00
for _ , downstream := range downstreams . Items {
labels := downstream . GetLabels ( )
trigger := generateutils . TriggerFromLabels ( labels )
2024-08-19 06:55:19 +00:00
addRuleContext ( ur , ruleName , trigger , deleteDownstream )
2024-08-13 17:14:06 +00:00
if policyDeletion {
addGeneratedResources ( ur , downstream )
2023-02-22 10:49:09 +00:00
}
2023-03-01 03:48:18 +00:00
}
2024-08-13 17:14:06 +00:00
return ur , nil
2023-03-01 03:48:18 +00:00
}
2024-09-24 14:11:14 +00:00
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
}
2023-03-01 03:48:18 +00:00
// ruleDeletion returns true if any rule is deleted, along with deleted rules
2024-09-24 14:11:14 +00:00
func ruleChange ( old , new kyvernov1 . PolicyInterface ) ( _ kyvernov1 . PolicyInterface , ruleDeleted bool , _ updatedResource ) {
2023-03-01 03:48:18 +00:00
if ! new . GetDeletionTimestamp ( ) . IsZero ( ) {
2024-09-24 14:11:14 +00:00
return nil , false , updatedResource { }
2023-03-01 03:48:18 +00:00
}
newRules := new . GetSpec ( ) . Rules
oldRules := old . GetSpec ( ) . Rules
2024-09-24 14:11:14 +00:00
newRulesMap := make ( map [ string ] kyvernov1 . Rule , len ( newRules ) )
2023-03-01 03:48:18 +00:00
var deletedRules [ ] kyvernov1 . Rule
2024-09-24 14:11:14 +00:00
updatedResources := updatedResource {
policy : new . GetName ( ) ,
policyNamespace : new . GetNamespace ( ) ,
}
2023-03-01 03:48:18 +00:00
for _ , r := range newRules {
2024-09-24 14:11:14 +00:00
newRulesMap [ r . Name ] = r
2023-03-01 03:48:18 +00:00
}
2024-09-24 14:11:14 +00:00
for _ , oldRule := range oldRules {
if newRule , exist := newRulesMap [ oldRule . Name ] ; ! exist {
deletedRules = append ( deletedRules , oldRule )
2023-03-01 03:48:18 +00:00
ruleDeleted = true
2024-09-24 14:11:14 +00:00
} 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 )
2023-02-22 10:49:09 +00:00
}
}
2023-03-01 03:48:18 +00:00
2024-09-24 14:11:14 +00:00
return buildPolicyWithDeletedRules ( old , deletedRules ) , ruleDeleted , updatedResources
2023-03-01 03:48:18 +00:00
}
func buildPolicyWithDeletedRules ( policy kyvernov1 . PolicyInterface , deletedRules [ ] kyvernov1 . Rule ) kyvernov1 . PolicyInterface {
newPolicy := policy . CreateDeepCopy ( )
spec := newPolicy . GetSpec ( )
spec . SetRules ( deletedRules )
return newPolicy
2023-02-22 10:49:09 +00:00
}