2020-01-07 10:33:28 -08:00
package generate
import (
2020-11-09 11:26:12 -08:00
contextdefault "context"
2020-02-04 12:13:41 -08:00
"encoding/json"
2020-12-22 11:07:31 -08:00
"errors"
2020-01-07 10:33:28 -08:00
"fmt"
2020-10-07 11:12:31 -07:00
"reflect"
2020-12-08 01:34:46 +05:30
"strings"
2020-10-07 11:12:31 -07:00
"time"
2020-03-17 11:05:20 -07:00
"github.com/go-logr/logr"
2020-10-07 11:12:31 -07:00
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
2021-02-04 02:39:42 +05:30
pkgcommon "github.com/kyverno/kyverno/pkg/common"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/config"
dclient "github.com/kyverno/kyverno/pkg/dclient"
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/context"
2020-12-04 10:04:46 -08:00
"github.com/kyverno/kyverno/pkg/engine/utils"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/engine/variables"
2020-12-15 15:21:39 -08:00
kyvernoutils "github.com/kyverno/kyverno/pkg/utils"
2020-01-09 17:53:27 -08:00
apierrors "k8s.io/apimachinery/pkg/api/errors"
2020-08-31 23:55:13 +05:30
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020-01-07 10:33:28 -08:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2020-12-15 04:22:13 +05:30
"k8s.io/apimachinery/pkg/labels"
2020-01-07 10:33:28 -08:00
)
func ( c * Controller ) processGR ( gr * kyverno . GenerateRequest ) error {
2020-08-07 09:47:33 +05:30
logger := c . log . WithValues ( "name" , gr . Name , "policy" , gr . Spec . Policy , "kind" , gr . Spec . Resource . Kind , "apiVersion" , gr . Spec . Resource . APIVersion , "namespace" , gr . Spec . Resource . Namespace , "name" , gr . Spec . Resource . Name )
2020-01-07 15:13:57 -08:00
var err error
var resource * unstructured . Unstructured
var genResources [ ] kyverno . ResourceSpec
2020-07-20 08:00:02 -07:00
2020-01-07 10:33:28 -08:00
// 1 - Check if the resource exists
2020-01-07 15:13:57 -08:00
resource , err = getResource ( c . client , gr . Spec . Resource )
2020-01-07 10:33:28 -08:00
if err != nil {
2020-12-21 11:04:19 -08:00
// Don't update status
2020-11-12 16:44:57 -08:00
logger . V ( 3 ) . Info ( "resource does not exist or is pending creation, re-queueing" , "details" , err . Error ( ) )
2020-01-07 10:33:28 -08:00
return err
}
2020-07-20 08:00:02 -07:00
2020-11-20 14:14:59 -08:00
// trigger resource is being terminated
if resource == nil {
return nil
}
2020-01-07 10:33:28 -08:00
// 2 - Apply the generate policy on the resource
2021-02-04 02:39:42 +05:30
namespaceLabels := pkgcommon . GetNamespaceSelectorsFromGenericInformer ( resource . GetKind ( ) , resource . GetNamespace ( ) , c . nsInformer , logger )
genResources , err = c . applyGenerate ( * resource , * gr , namespaceLabels )
2020-07-20 08:00:02 -07:00
2020-12-22 11:07:31 -08:00
if err != nil {
// Need not update the stauts when policy doesn't apply on resource, because all the generate requests are removed by the cleanup controller
if strings . Contains ( err . Error ( ) , doesNotApply ) {
logger . V ( 4 ) . Info ( "skipping updating status of generate request" )
return nil
}
2020-07-20 08:00:02 -07:00
2020-12-22 11:07:31 -08:00
// 3 - Report failure Events
events := failedEvents ( err , * gr , * resource )
c . eventGen . Add ( events ... )
2020-12-08 01:34:46 +05:30
}
2020-01-07 10:33:28 -08:00
// 4 - Update Status
2020-01-07 15:13:57 -08:00
return updateStatus ( c . statusControl , * gr , err , genResources )
2020-01-07 10:33:28 -08:00
}
2020-12-22 11:07:31 -08:00
const doesNotApply = "policy does not apply to resource"
2021-02-04 02:39:42 +05:30
func ( c * Controller ) applyGenerate ( resource unstructured . Unstructured , gr kyverno . GenerateRequest , namespaceLabels map [ string ] string ) ( [ ] kyverno . ResourceSpec , error ) {
2020-08-07 09:47:33 +05:30
logger := c . log . WithValues ( "name" , gr . Name , "policy" , gr . Spec . Policy , "kind" , gr . Spec . Resource . Kind , "apiVersion" , gr . Spec . Resource . APIVersion , "namespace" , gr . Spec . Resource . Namespace , "name" , gr . Spec . Resource . Name )
2020-01-07 10:33:28 -08:00
// Get the list of rules to be applied
// get policy
2020-07-08 14:22:32 -07:00
// build context
ctx := context . NewContext ( )
2020-12-01 12:30:08 -08:00
logger . V ( 3 ) . Info ( "applying generate policy rule" )
policyObj , err := c . policyLister . Get ( gr . Spec . Policy )
2020-01-07 10:33:28 -08:00
if err != nil {
2020-07-08 08:01:47 -07:00
if apierrors . IsNotFound ( err ) {
2020-07-20 08:00:02 -07:00
for _ , e := range gr . Status . GeneratedResources {
2020-08-07 09:47:33 +05:30
resp , err := c . client . GetResource ( e . APIVersion , e . Kind , e . Namespace , e . Name )
2020-11-20 14:14:59 -08:00
if err != nil && ! apierrors . IsNotFound ( err ) {
2020-11-12 16:44:57 -08:00
logger . Error ( err , "failed to find generated resource" , "name" , e . Name )
continue
2020-07-15 10:39:02 -07:00
}
2020-12-29 00:05:29 +05:30
if resp != nil && resp . GetLabels ( ) [ "policy.kyverno.io/synchronize" ] == "enable" {
2020-08-07 09:47:33 +05:30
if err := c . client . DeleteResource ( resp . GetAPIVersion ( ) , resp . GetKind ( ) , resp . GetNamespace ( ) , resp . GetName ( ) , false ) ; err != nil {
2020-12-01 12:30:08 -08:00
logger . Error ( err , "generated resource is not deleted" , "Resource" , e . Name )
2020-07-15 10:39:02 -07:00
}
2020-07-10 17:06:16 -07:00
}
2020-07-08 14:22:32 -07:00
}
2020-11-12 16:44:57 -08:00
2020-07-10 15:23:07 -07:00
return nil , nil
2020-07-08 08:01:47 -07:00
}
2020-11-12 16:44:57 -08:00
logger . Error ( err , "error in fetching policy" )
2020-07-10 17:21:30 -07:00
return nil , err
2020-01-07 10:33:28 -08:00
}
2020-07-08 14:22:32 -07:00
2020-01-07 10:33:28 -08:00
resourceRaw , err := resource . MarshalJSON ( )
if err != nil {
2020-03-17 11:05:20 -07:00
logger . Error ( err , "failed to marshal resource" )
2020-01-07 15:13:57 -08:00
return nil , err
2020-01-07 10:33:28 -08:00
}
2020-12-01 12:30:08 -08:00
2020-01-24 09:37:12 -08:00
err = ctx . AddResource ( resourceRaw )
if err != nil {
2020-03-17 11:05:20 -07:00
logger . Error ( err , "failed to load resource in context" )
2020-01-24 09:37:12 -08:00
return nil , err
}
2020-12-01 12:30:08 -08:00
2020-01-24 09:37:12 -08:00
err = ctx . AddUserInfo ( gr . Spec . Context . UserRequestInfo )
if err != nil {
2020-03-17 11:05:20 -07:00
logger . Error ( err , "failed to load SA in context" )
2020-01-24 09:37:12 -08:00
return nil , err
}
2020-12-01 12:30:08 -08:00
2020-12-23 15:10:07 -08:00
err = ctx . AddServiceAccount ( gr . Spec . Context . UserRequestInfo . AdmissionUserInfo . Username )
2020-01-24 09:37:12 -08:00
if err != nil {
2020-03-17 11:05:20 -07:00
logger . Error ( err , "failed to load UserInfo in context" )
2020-01-24 09:37:12 -08:00
return nil , err
}
2020-01-07 10:33:28 -08:00
2021-03-23 10:34:03 -07:00
if err := ctx . AddImageInfo ( & resource ) ; err != nil {
logger . Error ( err , "unable to add image info to variables context" )
}
2021-02-01 12:59:13 -08:00
policyContext := & engine . PolicyContext {
2020-12-16 12:29:16 -08:00
NewResource : resource ,
Policy : * policyObj ,
AdmissionInfo : gr . Spec . Context . UserRequestInfo ,
ExcludeGroupRole : c . Config . GetExcludeGroupRole ( ) ,
ExcludeResourceFunc : c . Config . ToFilter ,
ResourceCache : c . resCache ,
JSONContext : ctx ,
2021-02-04 02:39:42 +05:30
NamespaceLabels : namespaceLabels ,
2021-02-22 12:08:26 -08:00
Client : c . client ,
2020-01-07 10:33:28 -08:00
}
// check if the policy still applies to the resource
2020-01-24 12:05:53 -08:00
engineResponse := engine . Generate ( policyContext )
2020-01-07 10:33:28 -08:00
if len ( engineResponse . PolicyResponse . Rules ) == 0 {
2020-12-22 11:07:31 -08:00
logger . V ( 4 ) . Info ( doesNotApply )
return nil , errors . New ( doesNotApply )
2020-01-07 10:33:28 -08:00
}
2020-09-19 01:04:43 +05:30
2020-12-15 15:21:39 -08:00
var applicableRules [ ] string
2020-09-19 01:04:43 +05:30
// Removing GR if rule is failed. Used when the generate condition failed but gr exist
2020-09-04 03:04:23 +05:30
for _ , r := range engineResponse . PolicyResponse . Rules {
2020-08-31 23:55:13 +05:30
if ! r . Success {
2020-12-01 12:30:08 -08:00
logger . V ( 4 ) . Info ( "querying all generate requests" )
2020-12-15 04:22:13 +05:30
selector := labels . SelectorFromSet ( labels . Set ( map [ string ] string {
2021-06-30 00:43:11 +03:00
"generate.kyverno.io/policy-name" : engineResponse . PolicyResponse . Policy . Name ,
2020-12-23 12:20:29 +05:30
"generate.kyverno.io/resource-name" : engineResponse . PolicyResponse . Resource . Name ,
"generate.kyverno.io/resource-kind" : engineResponse . PolicyResponse . Resource . Kind ,
"generate.kyverno.io/resource-namespace" : engineResponse . PolicyResponse . Resource . Namespace ,
2020-12-15 04:22:13 +05:30
} ) )
grList , err := c . grLister . List ( selector )
2020-08-31 23:55:13 +05:30
if err != nil {
2020-12-15 04:22:13 +05:30
logger . Error ( err , "failed to get generate request for the resource" , "kind" , engineResponse . PolicyResponse . Resource . Kind , "name" , engineResponse . PolicyResponse . Resource . Name , "namespace" , engineResponse . PolicyResponse . Resource . Namespace )
2020-08-31 23:55:13 +05:30
continue
}
2020-11-12 16:44:57 -08:00
2020-12-15 04:22:13 +05:30
for _ , v := range grList {
2020-12-15 15:21:39 -08:00
err := c . kyvernoClient . KyvernoV1 ( ) . GenerateRequests ( config . KyvernoNamespace ) . Delete ( contextdefault . TODO ( ) , v . GetName ( ) , metav1 . DeleteOptions { } )
if err != nil {
logger . Error ( err , "failed to delete generate request" )
2020-08-31 23:55:13 +05:30
}
}
2020-12-15 15:21:39 -08:00
} else {
applicableRules = append ( applicableRules , r . Name )
2020-08-31 23:55:13 +05:30
}
}
2020-09-04 03:04:23 +05:30
2020-01-07 10:33:28 -08:00
// Apply the generate rule on resource
2020-12-15 15:21:39 -08:00
return c . applyGeneratePolicy ( logger , policyContext , gr , applicableRules )
2020-01-07 10:33:28 -08:00
}
2020-01-07 15:13:57 -08:00
func updateStatus ( statusControl StatusControlInterface , gr kyverno . GenerateRequest , err error , genResources [ ] kyverno . ResourceSpec ) error {
2020-01-07 10:33:28 -08:00
if err != nil {
2020-01-07 15:13:57 -08:00
return statusControl . Failed ( gr , err . Error ( ) , genResources )
2020-01-07 10:33:28 -08:00
}
// Generate request successfully processed
2020-01-07 15:13:57 -08:00
return statusControl . Success ( gr , genResources )
2020-01-07 10:33:28 -08:00
}
2021-02-01 12:59:13 -08:00
func ( c * Controller ) applyGeneratePolicy ( log logr . Logger , policyContext * engine . PolicyContext , gr kyverno . GenerateRequest , applicableRules [ ] string ) ( genResources [ ] kyverno . ResourceSpec , err error ) {
2020-01-07 10:33:28 -08:00
// Get the response as the actions to be performed on the resource
// - - substitute values
policy := policyContext . Policy
resource := policyContext . NewResource
2020-09-23 02:41:49 +05:30
resCache := policyContext . ResourceCache
jsonContext := policyContext . JSONContext
2020-08-31 23:55:13 +05:30
// To manage existing resources, we compare the creation time for the default resource to be generated and policy creation time
2020-01-07 10:33:28 -08:00
2020-03-04 13:11:48 +05:30
ruleNameToProcessingTime := make ( map [ string ] time . Duration )
2020-01-07 10:33:28 -08:00
for _ , rule := range policy . Spec . Rules {
2021-04-13 21:44:43 +03:00
var err error
2020-01-07 10:33:28 -08:00
if ! rule . HasGenerate ( ) {
continue
}
2020-09-04 03:04:23 +05:30
2020-12-15 15:21:39 -08:00
if ! kyvernoutils . ContainsString ( applicableRules , rule . Name ) {
continue
}
2020-11-12 16:44:57 -08:00
startTime := time . Now ( )
2020-09-04 03:04:23 +05:30
processExisting := false
2020-12-16 19:44:28 +05:30
var genResource kyverno . ResourceSpec
2020-09-04 03:04:23 +05:30
2020-09-19 01:04:43 +05:30
if len ( rule . MatchResources . Kinds ) > 0 {
if len ( rule . MatchResources . Annotations ) == 0 && rule . MatchResources . Selector == nil {
2020-12-16 19:44:28 +05:30
rcreationTime := resource . GetCreationTimestamp ( )
pcreationTime := policy . GetCreationTimestamp ( )
processExisting = rcreationTime . Before ( & pcreationTime )
2020-09-04 03:04:23 +05:30
}
}
2020-09-23 02:41:49 +05:30
// add configmap json data to context
2021-04-29 22:39:44 +05:30
if err := engine . LoadContext ( log , rule . Context , resCache , policyContext , rule . Name ) ; err != nil {
2021-04-13 21:44:43 +03:00
log . Error ( err , "cannot add configmaps to context" )
return nil , err
}
if rule , err = variables . SubstituteAllInRule ( log , policyContext . JSONContext , rule ) ; err != nil {
log . Error ( err , "variable substitution failed for rule %s" , rule . Name )
2020-09-23 02:41:49 +05:30
return nil , err
}
2020-12-16 19:44:28 +05:30
if ! processExisting {
2020-12-24 12:41:54 +05:30
genResource , err = applyRule ( log , c . client , rule , resource , jsonContext , policy . Name , gr )
2020-12-16 19:44:28 +05:30
if err != nil {
log . Error ( err , "failed to apply generate rule" , "policy" , policy . Name ,
2021-06-11 01:18:26 +05:30
"rule" , rule . Name , "resource" , resource . GetName ( ) , "suggestion" , "users need to grant Kyverno's service account additional privileges" )
2020-12-16 19:44:28 +05:30
return nil , err
}
ruleNameToProcessingTime [ rule . Name ] = time . Since ( startTime )
genResources = append ( genResources , genResource )
2020-01-07 10:33:28 -08:00
}
}
2020-01-07 15:13:57 -08:00
return genResources , nil
2020-01-07 10:33:28 -08:00
}
2020-12-04 10:04:46 -08:00
func getResourceInfo ( object map [ string ] interface { } ) ( kind , name , namespace , apiversion string , err error ) {
if kind , _ , err = unstructured . NestedString ( object , "kind" ) ; err != nil {
return "" , "" , "" , "" , err
}
if name , _ , err = unstructured . NestedString ( object , "name" ) ; err != nil {
return "" , "" , "" , "" , err
}
if namespace , _ , err = unstructured . NestedString ( object , "namespace" ) ; err != nil {
return "" , "" , "" , "" , err
}
if apiversion , _ , err = unstructured . NestedString ( object , "apiVersion" ) ; err != nil {
return "" , "" , "" , "" , err
}
return
}
2020-12-16 19:44:28 +05:30
func applyRule ( log logr . Logger , client * dclient . Client , rule kyverno . Rule , resource unstructured . Unstructured , ctx context . EvalInterface , policy string , gr kyverno . GenerateRequest ) ( kyverno . ResourceSpec , error ) {
2020-01-07 10:33:28 -08:00
var rdata map [ string ] interface { }
var err error
2020-02-04 12:13:41 -08:00
var mode ResourceMode
2020-01-07 15:13:57 -08:00
var noGenResource kyverno . ResourceSpec
2020-02-13 13:57:48 -08:00
genUnst , err := getUnstrRule ( rule . Generation . DeepCopy ( ) )
if err != nil {
return noGenResource , err
}
2020-12-14 02:43:16 -08:00
2020-12-04 10:04:46 -08:00
genKind , genName , genNamespace , genAPIVersion , err := getResourceInfo ( genUnst . Object )
2020-02-13 13:57:48 -08:00
if err != nil {
return noGenResource , err
2020-01-10 11:59:05 -08:00
}
2020-12-14 02:43:16 -08:00
logger := log . WithValues ( "genKind" , genKind , "genAPIVersion" , genAPIVersion , "genNamespace" , genNamespace , "genName" , genName )
2020-01-07 15:13:57 -08:00
// Resource to be generated
newGenResource := kyverno . ResourceSpec {
2020-08-07 09:47:33 +05:30
APIVersion : genAPIVersion ,
Kind : genKind ,
Namespace : genNamespace ,
Name : genName ,
2020-01-07 10:33:28 -08:00
}
2020-12-04 10:04:46 -08:00
2020-12-14 02:43:16 -08:00
genData , _ , err := unstructured . NestedMap ( genUnst . Object , "data" )
2020-02-13 13:57:48 -08:00
if err != nil {
2020-12-14 02:43:16 -08:00
return noGenResource , fmt . Errorf ( "failed to read `data`: %v" , err . Error ( ) )
2020-02-13 13:57:48 -08:00
}
2020-12-04 10:04:46 -08:00
2020-12-12 21:19:37 -08:00
genClone , _ , err := unstructured . NestedMap ( genUnst . Object , "clone" )
2020-02-13 13:57:48 -08:00
if err != nil {
2020-12-14 02:43:16 -08:00
return noGenResource , fmt . Errorf ( "failed to read `clone`: %v" , err . Error ( ) )
2020-02-13 13:57:48 -08:00
}
2020-12-04 10:04:46 -08:00
2020-12-14 02:43:16 -08:00
if genClone != nil && len ( genClone ) != 0 {
2020-12-24 18:39:23 +05:30
rdata , mode , err = manageClone ( logger , genAPIVersion , genKind , genNamespace , genName , policy , genClone , client )
2020-02-13 13:57:48 -08:00
} else {
2020-12-14 02:43:16 -08:00
rdata , mode , err = manageData ( logger , genAPIVersion , genKind , genNamespace , genName , genData , client )
2020-02-13 13:57:48 -08:00
}
2020-08-31 23:55:13 +05:30
2020-12-14 02:43:16 -08:00
if err != nil {
2021-01-04 12:33:00 +05:30
logger . Error ( err , "failed to generate resource" , "mode" , mode )
2020-12-14 02:43:16 -08:00
return newGenResource , err
}
2020-09-19 01:04:43 +05:30
2021-02-07 20:26:56 -08:00
logger . V ( 3 ) . Info ( "applying generate rule" , "mode" , mode )
2020-12-14 02:43:16 -08:00
if rdata == nil && mode == Update {
2021-07-09 18:01:46 -07:00
logger . V ( 4 ) . Info ( "no changes required for generate target resource" )
2020-02-13 13:57:48 -08:00
return newGenResource , nil
2020-01-07 10:33:28 -08:00
}
2020-12-14 02:43:16 -08:00
2020-02-04 12:13:41 -08:00
// build the resource template
2020-01-07 10:33:28 -08:00
newResource := & unstructured . Unstructured { }
newResource . SetUnstructuredContent ( rdata )
2020-02-13 13:57:48 -08:00
newResource . SetName ( genName )
newResource . SetNamespace ( genNamespace )
2020-05-16 21:29:23 -07:00
if newResource . GetKind ( ) == "" {
newResource . SetKind ( genKind )
}
2020-09-04 03:04:23 +05:30
2020-08-07 09:47:33 +05:30
newResource . SetAPIVersion ( genAPIVersion )
2020-02-10 12:44:20 -08:00
// manage labels
// - app.kubernetes.io/managed-by: kyverno
2021-04-27 01:28:34 +05:30
// "kyverno.io/generated-by-kind": kind (trigger resource)
// "kyverno.io/generated-by-namespace": namespace (trigger resource)
// "kyverno.io/generated-by-name": name (trigger resource)
2020-02-10 12:44:20 -08:00
manageLabels ( newResource , resource )
2020-07-10 15:23:07 -07:00
// Add Synchronize label
label := newResource . GetLabels ( )
2020-12-29 00:05:29 +05:30
label [ "policy.kyverno.io/policy-name" ] = policy
label [ "policy.kyverno.io/gr-name" ] = gr . Name
2020-12-24 18:39:23 +05:30
delete ( label , "generate.kyverno.io/clone-policy-name" )
2020-07-10 15:23:07 -07:00
if mode == Create {
2020-09-04 03:04:23 +05:30
if rule . Generation . Synchronize {
2020-12-29 00:05:29 +05:30
label [ "policy.kyverno.io/synchronize" ] = "enable"
2020-09-04 03:04:23 +05:30
} else {
2020-12-29 00:05:29 +05:30
label [ "policy.kyverno.io/synchronize" ] = "disable"
2020-09-04 03:04:23 +05:30
}
2020-12-14 02:43:16 -08:00
2020-02-04 12:13:41 -08:00
// Reset resource version
newResource . SetResourceVersion ( "" )
2020-12-16 19:44:28 +05:30
newResource . SetLabels ( label )
2020-02-04 12:13:41 -08:00
// Create the resource
2020-08-07 09:47:33 +05:30
_ , err = client . CreateResource ( genAPIVersion , genKind , genNamespace , newResource , false )
2020-02-04 12:13:41 -08:00
if err != nil {
return noGenResource , err
}
2020-12-01 12:30:08 -08:00
2021-07-09 18:01:46 -07:00
logger . V ( 2 ) . Info ( "created generate target resource" )
2020-02-04 12:13:41 -08:00
} else if mode == Update {
2021-08-13 17:23:39 +05:30
// if synchronize is true - update the label and generated resource with generate policy data
2020-09-04 03:04:23 +05:30
if rule . Generation . Synchronize {
2021-08-09 21:10:47 +05:30
logger . V ( 4 ) . Info ( "updating existing resource" )
2020-12-29 00:05:29 +05:30
label [ "policy.kyverno.io/synchronize" ] = "enable"
2021-08-09 21:10:47 +05:30
newResource . SetLabels ( label )
_ , err := client . UpdateResource ( genAPIVersion , genKind , genNamespace , newResource , false )
if err != nil {
logger . Error ( err , "failed to update resource" )
return noGenResource , err
}
2020-09-04 03:04:23 +05:30
} else {
2021-08-13 17:23:39 +05:30
// if synchronize is false - update the label in already generated resource,
// without comparing it with the generate policy data
generatedObj , err := client . GetResource ( genAPIVersion , genKind , genNamespace , genName )
2021-08-09 21:10:47 +05:30
if err != nil {
2021-08-13 17:23:39 +05:30
logger . Error ( err , fmt . Sprintf ( "generated resource not found name:%v namespace:%v kind:%v" , genName , genNamespace , genKind ) )
return newGenResource , err
2021-08-09 21:10:47 +05:30
}
2021-08-13 17:23:39 +05:30
currentGeneratedResourcelabel := generatedObj . GetLabels ( )
currentSynclabel := currentGeneratedResourcelabel [ "policy.kyverno.io/synchronize" ]
// update only if the labels mismatches
if ( ! rule . Generation . Synchronize && currentSynclabel == "enable" ) ||
( rule . Generation . Synchronize && currentSynclabel == "disable" ) {
logger . V ( 4 ) . Info ( "updating label in existing resource" )
currentGeneratedResourcelabel [ "policy.kyverno.io/synchronize" ] = "disable"
generatedObj . SetLabels ( currentGeneratedResourcelabel )
_ , err = client . UpdateResource ( genAPIVersion , genKind , genNamespace , generatedObj , false )
if err != nil {
logger . Error ( err , "failed to update label in existing resource" )
return noGenResource , err
}
}
2020-12-04 10:04:46 -08:00
2020-02-04 12:13:41 -08:00
}
2021-08-13 17:23:39 +05:30
2021-07-09 18:01:46 -07:00
logger . V ( 2 ) . Info ( "updated generate target resource" )
2020-01-07 10:33:28 -08:00
}
2020-12-14 02:43:16 -08:00
2020-01-07 15:13:57 -08:00
return newGenResource , nil
2020-01-07 10:33:28 -08:00
}
2020-12-14 02:43:16 -08:00
func manageData ( log logr . Logger , apiVersion , kind , namespace , name string , data map [ string ] interface { } , client * dclient . Client ) ( map [ string ] interface { } , ResourceMode , error ) {
2020-08-07 09:47:33 +05:30
obj , err := client . GetResource ( apiVersion , kind , namespace , name )
2020-01-07 10:33:28 -08:00
if err != nil {
2020-09-04 03:04:23 +05:30
if apierrors . IsNotFound ( err ) {
return data , Create , nil
}
2020-12-12 21:19:37 -08:00
2020-12-14 02:43:16 -08:00
log . Error ( err , "failed to get resource" )
2020-02-04 12:13:41 -08:00
return nil , Skip , err
2020-01-07 10:33:28 -08:00
}
2020-12-12 21:19:37 -08:00
2021-01-04 12:33:00 +05:30
log . V ( 3 ) . Info ( "found target resource" , "resource" , obj )
2020-12-14 02:43:16 -08:00
if data == nil {
2021-01-04 12:33:00 +05:30
log . V ( 3 ) . Info ( "data is nil - skipping update" , "resource" , obj )
2020-12-14 02:43:16 -08:00
return nil , Skip , nil
}
2020-09-04 03:04:23 +05:30
updateObj := & unstructured . Unstructured { }
updateObj . SetUnstructuredContent ( data )
updateObj . SetResourceVersion ( obj . GetResourceVersion ( ) )
return updateObj . UnstructuredContent ( ) , Update , nil
2020-01-07 10:33:28 -08:00
}
2020-12-24 18:39:23 +05:30
func manageClone ( log logr . Logger , apiVersion , kind , namespace , name , policy string , clone map [ string ] interface { } , client * dclient . Client ) ( map [ string ] interface { } , ResourceMode , error ) {
2020-12-14 02:43:16 -08:00
rNamespace , _ , err := unstructured . NestedString ( clone , "namespace" )
2020-02-13 13:57:48 -08:00
if err != nil {
2020-12-14 02:43:16 -08:00
return nil , Skip , fmt . Errorf ( "failed to find source namespace: %v" , err )
2020-01-07 10:33:28 -08:00
}
2020-12-14 02:43:16 -08:00
rName , _ , err := unstructured . NestedString ( clone , "name" )
2020-02-13 13:57:48 -08:00
if err != nil {
2020-12-14 02:43:16 -08:00
return nil , Skip , fmt . Errorf ( "failed to find source name: %v" , err )
2020-02-13 13:57:48 -08:00
}
2020-08-07 09:47:33 +05:30
2020-12-14 02:43:16 -08:00
if rNamespace == namespace && rName == name {
log . V ( 4 ) . Info ( "skip resource self-clone" )
2020-02-13 13:57:48 -08:00
return nil , Skip , nil
}
2020-06-22 18:49:43 -07:00
2020-02-13 13:57:48 -08:00
// check if the resource as reference in clone exists?
2020-12-14 02:43:16 -08:00
obj , err := client . GetResource ( apiVersion , kind , rNamespace , rName )
2020-01-07 10:33:28 -08:00
if err != nil {
2020-12-14 02:43:16 -08:00
return nil , Skip , fmt . Errorf ( "source resource %s %s/%s/%s not found. %v" , apiVersion , kind , rNamespace , rName , err )
2020-01-07 10:33:28 -08:00
}
2021-08-24 16:02:12 +02:00
// remove ownerReferences when cloning resources to other namespace
if rNamespace != namespace && obj . GetOwnerReferences ( ) != nil {
obj . SetOwnerReferences ( nil )
}
2020-06-22 18:49:43 -07:00
// check if resource to be generated exists
2020-08-07 09:47:33 +05:30
newResource , err := client . GetResource ( apiVersion , kind , namespace , name )
2020-06-22 18:49:43 -07:00
if err == nil {
obj . SetUID ( newResource . GetUID ( ) )
obj . SetSelfLink ( newResource . GetSelfLink ( ) )
obj . SetCreationTimestamp ( newResource . GetCreationTimestamp ( ) )
obj . SetManagedFields ( newResource . GetManagedFields ( ) )
obj . SetResourceVersion ( newResource . GetResourceVersion ( ) )
if reflect . DeepEqual ( obj , newResource ) {
return nil , Skip , nil
}
return obj . UnstructuredContent ( ) , Update , nil
}
2020-02-04 12:13:41 -08:00
// create the resource based on the reference clone
return obj . UnstructuredContent ( ) , Create , nil
2020-02-13 13:57:48 -08:00
2020-01-07 10:33:28 -08:00
}
2020-02-13 13:57:48 -08:00
// ResourceMode defines the mode for generated resource
type ResourceMode string
const (
//Skip : failed to process rule, will not update the resource
Skip ResourceMode = "SKIP"
//Create : create a new resource
Create = "CREATE"
//Update : update/overwrite the new resource
Update = "UPDATE"
)
func getUnstrRule ( rule * kyverno . Generation ) ( * unstructured . Unstructured , error ) {
ruleData , err := json . Marshal ( rule )
if err != nil {
return nil , err
}
2020-12-04 10:04:46 -08:00
return utils . ConvertToUnstructured ( ruleData )
2020-01-07 10:33:28 -08:00
}