2023-03-01 11:48:18 +08:00
package generate
import (
"context"
"fmt"
2023-07-06 10:00:36 +02:00
"github.com/kyverno/kyverno/api/kyverno"
2023-03-01 11:48:18 +08:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-06-20 11:44:43 +02:00
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
2023-03-01 11:48:18 +08:00
"github.com/kyverno/kyverno/pkg/background/common"
2023-06-13 17:12:13 +08:00
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
2023-03-01 11:48:18 +08:00
"go.uber.org/multierr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
2023-06-20 20:58:23 +08:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2023-03-01 11:48:18 +08:00
)
2024-08-14 01:14:06 +08:00
func ( c * GenerateController ) deleteDownstream ( policy kyvernov1 . PolicyInterface , ruleContext kyvernov2 . RuleContext , ur * kyvernov2 . UpdateRequest ) ( err error ) {
2023-03-01 11:48:18 +08:00
// handle data policy/rule deletion
if ur . Status . GeneratedResources != nil {
2024-08-14 01:14:06 +08:00
c . log . V ( 4 ) . Info ( "policy/rule no longer exists, deleting the downstream resource based on synchronize" , "ur" , ur . Name , "policy" , ur . Spec . Policy )
2023-03-01 11:48:18 +08:00
var errs [ ] error
failedDownstreams := [ ] kyvernov1 . ResourceSpec { }
for _ , e := range ur . Status . GeneratedResources {
if err := c . client . DeleteResource ( context . TODO ( ) , e . GetAPIVersion ( ) , e . GetKind ( ) , e . GetNamespace ( ) , e . GetName ( ) , false ) ; err != nil && ! apierrors . IsNotFound ( err ) {
failedDownstreams = append ( failedDownstreams , e )
errs = append ( errs , err )
}
}
if len ( errs ) != 0 {
c . log . Error ( multierr . Combine ( errs ... ) , "failed to clean up downstream resources on policy deletion" )
_ , err = c . statusControl . Failed ( ur . GetName ( ) ,
fmt . Sprintf ( "failed to clean up downstream resources on policy deletion: %v" , multierr . Combine ( errs ... ) ) ,
failedDownstreams )
} else {
_ , err = c . statusControl . Success ( ur . GetName ( ) , nil )
}
return
}
if policy == nil {
return nil
}
2023-06-20 20:58:23 +08:00
2024-08-14 01:14:06 +08:00
return c . handleNonPolicyChanges ( policy , ruleContext , ur )
2023-03-01 11:48:18 +08:00
}
2024-08-14 01:14:06 +08:00
func ( c * GenerateController ) handleNonPolicyChanges ( policy kyvernov1 . PolicyInterface , ruleContext kyvernov2 . RuleContext , ur * kyvernov2 . UpdateRequest ) error {
logger := c . log . V ( 4 ) . WithValues ( "ur" , ur . Name , "policy" , ur . Spec . Policy , "rule" , ruleContext . Rule )
logger . Info ( "synchronize for none-policy changes" )
2023-03-01 11:48:18 +08:00
for _ , rule := range policy . GetSpec ( ) . Rules {
2024-08-14 01:14:06 +08:00
if ruleContext . Rule != rule . Name {
2023-03-01 11:48:18 +08:00
continue
}
2024-08-14 01:14:06 +08:00
logger . Info ( "deleting the downstream resource based on synchronize" )
2023-06-07 21:50:47 +08:00
labels := map [ string ] string {
common . GeneratePolicyLabel : policy . GetName ( ) ,
common . GeneratePolicyNamespaceLabel : policy . GetNamespace ( ) ,
2024-08-29 19:59:22 +08:00
// common.GenerateRuleLabel: rule.Name,
kyverno . LabelAppManagedBy : kyverno . ValueKyvernoApp ,
2023-06-07 21:50:47 +08:00
}
2023-03-01 11:48:18 +08:00
2024-08-14 01:14:06 +08:00
downstreams , err := c . getDownstreams ( rule , labels , & ruleContext )
2023-06-20 20:58:23 +08:00
if err != nil {
return fmt . Errorf ( "failed to fetch downstream resources: %v" , err )
2023-03-01 11:48:18 +08:00
}
2024-08-29 19:59:22 +08:00
if len ( downstreams ) == 0 {
logger . V ( 4 ) . Info ( "no downstream resources found by label selectors" , "labels" , labels )
return nil
}
2023-06-20 20:58:23 +08:00
var errs [ ] error
failedDownstreams := [ ] kyvernov1 . ResourceSpec { }
2024-08-29 19:59:22 +08:00
for _ , downstream := range downstreams {
2023-06-20 20:58:23 +08:00
spec := common . ResourceSpecFromUnstructured ( downstream )
if err := c . client . DeleteResource ( context . TODO ( ) , downstream . GetAPIVersion ( ) , downstream . GetKind ( ) , downstream . GetNamespace ( ) , downstream . GetName ( ) , false ) ; err != nil && ! apierrors . IsNotFound ( err ) {
failedDownstreams = append ( failedDownstreams , spec )
errs = append ( errs , err )
2023-06-13 17:12:13 +08:00
} else {
2024-08-14 01:14:06 +08:00
logger . Info ( "downstream resource deleted" , "spec" , spec . String ( ) )
2023-06-13 17:12:13 +08:00
}
2023-03-01 11:48:18 +08:00
}
2023-06-20 20:58:23 +08:00
if len ( errs ) != 0 {
_ , err = c . statusControl . Failed ( ur . GetName ( ) ,
fmt . Sprintf ( "failed to clean up downstream resources on source deletion: %v" , multierr . Combine ( errs ... ) ) ,
failedDownstreams )
} else {
_ , err = c . statusControl . Success ( ur . GetName ( ) , nil )
}
if err != nil {
2024-08-14 01:14:06 +08:00
logger . Error ( err , "failed to update ur status" )
2023-06-20 20:58:23 +08:00
}
2023-03-01 11:48:18 +08:00
}
2023-06-20 20:58:23 +08:00
2023-03-01 11:48:18 +08:00
return nil
}
2023-06-13 17:12:13 +08:00
2024-08-29 19:59:22 +08:00
func ( c * GenerateController ) getDownstreams ( rule kyvernov1 . Rule , selector map [ string ] string , ruleContext * kyvernov2 . RuleContext ) ( [ ] unstructured . Unstructured , error ) {
2024-08-14 01:14:06 +08:00
gv , err := ruleContext . Trigger . GetGroupVersion ( )
2023-06-13 17:12:13 +08:00
if err != nil {
return nil , err
}
2024-08-14 01:14:06 +08:00
selector [ common . GenerateTriggerUIDLabel ] = string ( ruleContext . Trigger . GetUID ( ) )
selector [ common . GenerateTriggerNSLabel ] = ruleContext . Trigger . GetNamespace ( )
selector [ common . GenerateTriggerKindLabel ] = ruleContext . Trigger . GetKind ( )
2023-06-20 20:58:23 +08:00
selector [ common . GenerateTriggerGroupLabel ] = gv . Group
selector [ common . GenerateTriggerVersionLabel ] = gv . Version
2024-08-29 19:59:22 +08:00
for _ , g := range rule . Generation . ForEachGeneration {
return c . fetch ( g . GeneratePattern , selector , ruleContext )
}
return c . fetch ( rule . Generation . GeneratePattern , selector , ruleContext )
}
func ( c * GenerateController ) fetch ( generatePattern kyvernov1 . GeneratePattern , selector map [ string ] string , ruleContext * kyvernov2 . RuleContext ) ( [ ] unstructured . Unstructured , error ) {
downstreamResources := [ ] unstructured . Unstructured { }
if generatePattern . GetKind ( ) != "" {
2023-11-06 16:07:13 +05:30
// Fetch downstream resources using trigger uid label
2024-08-29 19:59:22 +08:00
c . log . V ( 4 ) . Info ( "fetching downstream resource by the UID" , "APIVersion" , generatePattern . GetAPIVersion ( ) , "kind" , generatePattern . GetKind ( ) , "selector" , selector )
dsList , err := common . FindDownstream ( c . client , generatePattern . GetAPIVersion ( ) , generatePattern . GetKind ( ) , selector )
2023-11-06 16:07:13 +05:30
if err != nil {
return nil , err
}
2024-08-29 19:59:22 +08:00
if len ( dsList . Items ) == 0 {
2023-11-06 16:07:13 +05:30
// Fetch downstream resources using the trigger name label
delete ( selector , common . GenerateTriggerUIDLabel )
2024-08-14 01:14:06 +08:00
selector [ common . GenerateTriggerNameLabel ] = ruleContext . Trigger . GetName ( )
2024-08-29 19:59:22 +08:00
c . log . V ( 4 ) . Info ( "fetching downstream resource by the name" , "APIVersion" , generatePattern . GetAPIVersion ( ) , "kind" , generatePattern . GetKind ( ) , "selector" , selector )
dsList , err = common . FindDownstream ( c . client , generatePattern . GetAPIVersion ( ) , generatePattern . GetKind ( ) , selector )
2023-11-06 16:07:13 +05:30
if err != nil {
return nil , err
}
}
2024-08-29 19:59:22 +08:00
downstreamResources = append ( downstreamResources , dsList . Items ... )
2023-11-06 16:07:13 +05:30
2024-08-29 19:59:22 +08:00
return downstreamResources , err
2023-06-13 17:12:13 +08:00
}
2024-08-29 19:59:22 +08:00
for _ , kind := range generatePattern . CloneList . Kinds {
2023-06-20 20:58:23 +08:00
apiVersion , kind := kubeutils . GetKindFromGVK ( kind )
2023-11-06 16:07:13 +05:30
c . log . V ( 4 ) . Info ( "fetching downstream cloneList resources by the UID" , "APIVersion" , apiVersion , "kind" , kind , "selector" , selector )
2024-08-29 19:59:22 +08:00
dsList , err := common . FindDownstream ( c . client , apiVersion , kind , selector )
2023-06-20 20:58:23 +08:00
if err != nil {
return nil , err
2023-11-06 16:07:13 +05:30
}
if len ( dsList . Items ) == 0 {
delete ( selector , common . GenerateTriggerUIDLabel )
2024-08-14 01:14:06 +08:00
selector [ common . GenerateTriggerNameLabel ] = ruleContext . Trigger . GetName ( )
2024-08-29 19:59:22 +08:00
c . log . V ( 4 ) . Info ( "fetching downstream resource by the name" , "APIVersion" , generatePattern . GetAPIVersion ( ) , "kind" , generatePattern . GetKind ( ) , "selector" , selector )
dsList , err = common . FindDownstream ( c . client , generatePattern . GetAPIVersion ( ) , generatePattern . GetKind ( ) , selector )
2023-11-06 16:07:13 +05:30
if err != nil {
return nil , err
}
2023-06-20 20:58:23 +08:00
}
2024-08-29 19:59:22 +08:00
downstreamResources = append ( downstreamResources , dsList . Items ... )
2023-06-13 17:12:13 +08:00
}
2024-08-29 19:59:22 +08:00
return downstreamResources , nil
2023-06-13 17:12:13 +08:00
}