2023-03-01 11:48:18 +08:00
package generate
import (
"context"
"fmt"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"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
)
func ( c * GenerateController ) deleteDownstream ( policy kyvernov1 . PolicyInterface , ur * kyvernov1beta1 . UpdateRequest ) ( err error ) {
if ! ur . Spec . DeleteDownstream {
return nil
}
// handle data policy/rule deletion
if ur . Status . GeneratedResources != nil {
c . log . V ( 4 ) . Info ( "policy/rule no longer exists, deleting the downstream resource based on synchronize" , "ur" , ur . Name , "policy" , ur . Spec . Policy , "rule" , ur . Spec . Rule )
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
return c . handleNonPolicyChanges ( policy , ur )
2023-03-01 11:48:18 +08:00
}
2023-06-20 20:58:23 +08:00
func ( c * GenerateController ) handleNonPolicyChanges ( policy kyvernov1 . PolicyInterface , ur * kyvernov1beta1 . UpdateRequest ) error {
2023-03-01 11:48:18 +08:00
if ! ur . Spec . DeleteDownstream {
return nil
}
for _ , rule := range policy . GetSpec ( ) . Rules {
if ur . Spec . Rule != rule . Name {
continue
}
2023-06-07 21:50:47 +08:00
labels := map [ string ] string {
common . GeneratePolicyLabel : policy . GetName ( ) ,
common . GeneratePolicyNamespaceLabel : policy . GetNamespace ( ) ,
common . GenerateRuleLabel : rule . Name ,
kyvernov1 . LabelAppManagedBy : kyvernov1 . ValueKyvernoApp ,
}
2023-03-01 11:48:18 +08:00
2023-06-20 20:58:23 +08:00
downstreams , err := c . getDownstreams ( rule , labels , ur )
if err != nil {
return fmt . Errorf ( "failed to fetch downstream resources: %v" , err )
2023-03-01 11:48:18 +08:00
}
2023-06-20 20:58:23 +08:00
var errs [ ] error
failedDownstreams := [ ] kyvernov1 . ResourceSpec { }
for _ , downstream := range downstreams . Items {
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 {
2023-06-20 20:58:23 +08:00
c . log . V ( 4 ) . Info ( "downstream resource deleted" , 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 {
c . log . Error ( err , "failed to update ur status" )
}
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
2023-06-20 20:58:23 +08:00
func ( c * GenerateController ) getDownstreams ( rule kyvernov1 . Rule , selector map [ string ] string , ur * kyvernov1beta1 . UpdateRequest ) ( * unstructured . UnstructuredList , error ) {
gv , err := ur . Spec . GetResource ( ) . GetGroupVersion ( )
2023-06-13 17:12:13 +08:00
if err != nil {
return nil , err
}
2023-06-20 20:58:23 +08:00
selector [ common . GenerateTriggerNameLabel ] = ur . Spec . GetResource ( ) . GetName ( )
selector [ common . GenerateTriggerNSLabel ] = ur . Spec . GetResource ( ) . GetNamespace ( )
selector [ common . GenerateTriggerKindLabel ] = ur . Spec . GetResource ( ) . GetKind ( )
selector [ common . GenerateTriggerGroupLabel ] = gv . Group
selector [ common . GenerateTriggerVersionLabel ] = gv . Version
if rule . Generation . GetKind ( ) != "" {
c . log . V ( 4 ) . Info ( "fetching downstream resources" , "APIVersion" , rule . Generation . GetAPIVersion ( ) , "kind" , rule . Generation . GetKind ( ) , "selector" , selector )
return FindDownstream ( c . client , rule . Generation . GetAPIVersion ( ) , rule . Generation . GetKind ( ) , selector )
2023-06-13 17:12:13 +08:00
}
2023-06-20 20:58:23 +08:00
dsList := & unstructured . UnstructuredList { }
2023-06-13 17:12:13 +08:00
for _ , kind := range rule . Generation . CloneList . Kinds {
2023-06-20 20:58:23 +08:00
apiVersion , kind := kubeutils . GetKindFromGVK ( kind )
c . log . V ( 4 ) . Info ( "fetching downstream resources" , "APIVersion" , apiVersion , "kind" , kind , "selector" , selector )
dsList , err = FindDownstream ( c . client , apiVersion , kind , selector )
if err != nil {
return nil , err
} else {
dsList . Items = append ( dsList . Items , dsList . Items ... )
}
2023-06-13 17:12:13 +08:00
}
2023-06-20 20:58:23 +08:00
return dsList , nil
2023-06-13 17:12:13 +08:00
}