@@ -30,8 +30,10 @@ import (
3030 "k8s.io/apimachinery/pkg/types"
3131 "k8s.io/client-go/dynamic"
3232 dynfake "k8s.io/client-go/dynamic/fake"
33+ "k8s.io/client-go/kubernetes"
3334 clientsetfake "k8s.io/client-go/kubernetes/fake"
3435 "k8s.io/client-go/kubernetes/scheme"
36+ "k8s.io/client-go/tools/clientcmd"
3537 "k8s.io/client-go/tools/record"
3638 klog "k8s.io/klog/v2"
3739 parentpolicyv1 "open-cluster-management.io/governance-policy-propagator/api/v1"
@@ -57,11 +59,6 @@ func (d *DryRunner) dryRun(cmd *cobra.Command, args []string) error {
5759 return fmt .Errorf ("unable to read input policy: %w" , err )
5860 }
5961
60- inputObjects , err := d .readInputResources (cmd , args )
61- if err != nil {
62- return fmt .Errorf ("unable to read input resources: %w" , err )
63- }
64-
6562 if err := d .setupLogs (); err != nil {
6663 return fmt .Errorf ("unable to setup the logging configuration: %w" , err )
6764 }
@@ -74,43 +71,15 @@ func (d *DryRunner) dryRun(cmd *cobra.Command, args []string) error {
7471 return fmt .Errorf ("unable to setup the dryrun reconciler: %w" , err )
7572 }
7673
77- // Apply the user's resources to the fake cluster
78- for _ , obj := range inputObjects {
79- gvk := obj .GroupVersionKind ()
80-
81- scopedGVR , err := rec .DynamicWatcher .GVKToGVR (gvk )
74+ if ! d .fromCluster {
75+ inputObjects , err := d .readInputResources (cmd , args )
8276 if err != nil {
83- if errors .Is (err , depclient .ErrNoVersionedResource ) {
84- return fmt .Errorf ("%w for kind %v: if this is a custom resource, it may need an " +
85- "entry in the mappings file" , err , gvk .Kind )
86- }
87-
88- return fmt .Errorf ("unable to apply an input resource: %w" , err )
77+ return fmt .Errorf ("unable to read input resources: %w" , err )
8978 }
9079
91- var resInt dynamic.ResourceInterface
92-
93- if scopedGVR .Namespaced {
94- if obj .GetNamespace () == "" {
95- obj .SetNamespace ("default" )
96- }
97-
98- resInt = rec .TargetK8sDynamicClient .Resource (scopedGVR .GroupVersionResource ).Namespace (obj .GetNamespace ())
99- } else {
100- resInt = rec .TargetK8sDynamicClient .Resource (scopedGVR .GroupVersionResource )
101- }
102-
103- sanitizeForCreation (obj )
104-
105- if _ , err := resInt .Create (ctx , obj , metav1.CreateOptions {}); err != nil &&
106- ! k8serrors .IsAlreadyExists (err ) {
107- return fmt .Errorf ("unable to apply an input resource: %w" , err )
108- }
109-
110- // Manually convert resources from the dynamic client to the runtime client
111- err = rec .Client .Create (ctx , obj )
112- if err != nil && ! k8serrors .IsAlreadyExists (err ) {
113- return err
80+ err = d .applyInputResources (ctx , rec , inputObjects )
81+ if err != nil {
82+ return fmt .Errorf ("unable to apply input resources: %w" , err )
11483 }
11584 }
11685
@@ -259,9 +228,59 @@ func (d *DryRunner) readPolicy(cmd *cobra.Command) (*policyv1.ConfigurationPolic
259228 Name : parentName ,
260229 }}
261230
231+ if cfgpol .GetNamespace () == "" {
232+ cfgpol .SetNamespace ("default" )
233+ }
234+
262235 return & cfgpol , nil
263236}
264237
238+ func (d * DryRunner ) applyInputResources (
239+ ctx context.Context , rec * ctrl.ConfigurationPolicyReconciler , inputObjects []* unstructured.Unstructured ,
240+ ) error {
241+ // Apply the user's resources to the fake cluster
242+ for _ , obj := range inputObjects {
243+ gvk := obj .GroupVersionKind ()
244+
245+ scopedGVR , err := rec .DynamicWatcher .GVKToGVR (gvk )
246+ if err != nil {
247+ if errors .Is (err , depclient .ErrNoVersionedResource ) {
248+ return fmt .Errorf ("%w for kind %v: if this is a custom resource, it may need an " +
249+ "entry in the mappings file" , err , gvk .Kind )
250+ }
251+
252+ return fmt .Errorf ("unable to apply an input resource: %w" , err )
253+ }
254+
255+ var resInt dynamic.ResourceInterface
256+
257+ if scopedGVR .Namespaced {
258+ if obj .GetNamespace () == "" {
259+ obj .SetNamespace ("default" )
260+ }
261+
262+ resInt = rec .TargetK8sDynamicClient .Resource (scopedGVR .GroupVersionResource ).Namespace (obj .GetNamespace ())
263+ } else {
264+ resInt = rec .TargetK8sDynamicClient .Resource (scopedGVR .GroupVersionResource )
265+ }
266+
267+ sanitizeForCreation (obj )
268+
269+ if _ , err := resInt .Create (ctx , obj , metav1.CreateOptions {}); err != nil &&
270+ ! k8serrors .IsAlreadyExists (err ) {
271+ return fmt .Errorf ("unable to apply an input resource: %w" , err )
272+ }
273+
274+ // Manually convert resources from the dynamic client to the runtime client
275+ err = rec .Client .Create (ctx , obj )
276+ if err != nil && ! k8serrors .IsAlreadyExists (err ) {
277+ return err
278+ }
279+ }
280+
281+ return nil
282+ }
283+
265284// readInputResources takes stdin and any paths given as "positional" arguments,
266285// and decodes them from YAML into k8s resources. Directories can be passed in
267286// arguments: in this case all files in that directory will be read and decoded
@@ -417,11 +436,60 @@ func (d *DryRunner) setupReconciler(
417436 return nil , err
418437 }
419438
420- dynamicClient := dynfake .NewSimpleDynamicClient (scheme .Scheme )
421- clientset := clientsetfake .NewSimpleClientset ()
439+ runtimeClient := clientfake .NewClientBuilder ().
440+ WithScheme (scheme .Scheme ).
441+ WithObjects (configPolCRD , cfgPolicy ).
442+ WithStatusSubresource (cfgPolicy ).
443+ Build ()
444+
445+ nsSelUpdatesChan := make (chan event.GenericEvent , 20 )
446+
422447 watcherReconciler , _ := depclient .NewControllerRuntimeSource ()
423448
424- dynamicWatcher := depclient .NewWithClients (
449+ var dynamicWatcher depclient.DynamicWatcher
450+ var clientset kubernetes.Interface
451+ var dynamicClient dynamic.Interface
452+ var nsSelReconciler common.NamespaceSelectorReconciler
453+
454+ if d .fromCluster {
455+ loadingRules := clientcmd .NewDefaultClientConfigLoadingRules ()
456+ clientConfig := clientcmd .NewNonInteractiveDeferredLoadingClientConfig (
457+ loadingRules , & clientcmd.ConfigOverrides {},
458+ )
459+
460+ kubeConfig , err := clientConfig .ClientConfig ()
461+ if err != nil {
462+ return nil , fmt .Errorf ("unable to locate the default kubeconfig: %w" , err )
463+ }
464+
465+ clientset , err = kubernetes .NewForConfig (kubeConfig )
466+ if err != nil {
467+ return nil , fmt .Errorf ("failed to create kubernetes clientset for cluster: %w" , err )
468+ }
469+
470+ dynamicClient , err = dynamic .NewForConfig (kubeConfig )
471+ if err != nil {
472+ return nil , fmt .Errorf ("failed to create dynamic client for cluster: %w" , err )
473+ }
474+
475+ readOnlyMode := true // Prevent modifications to the cluster
476+
477+ realRuntimeClient , err := client .New (kubeConfig , client.Options {
478+ Scheme : scheme .Scheme ,
479+ DryRun : & readOnlyMode ,
480+ })
481+ if err != nil {
482+ return nil , fmt .Errorf ("failed to create runtime client for ns selector reconciler: %w" , err )
483+ }
484+
485+ nsSelReconciler = common .NewNamespaceSelectorReconciler (realRuntimeClient , nsSelUpdatesChan )
486+ } else {
487+ dynamicClient = dynfake .NewSimpleDynamicClient (scheme .Scheme )
488+ clientset = clientsetfake .NewSimpleClientset ()
489+ nsSelReconciler = common .NewNamespaceSelectorReconciler (runtimeClient , nsSelUpdatesChan )
490+ }
491+
492+ dynamicWatcher = depclient .NewWithClients (
425493 dynamicClient ,
426494 clientset .Discovery (),
427495 watcherReconciler ,
@@ -435,14 +503,28 @@ func (d *DryRunner) setupReconciler(
435503 }
436504 }()
437505
438- runtimeClient := clientfake .NewClientBuilder ().
439- WithScheme (scheme .Scheme ).
440- WithObjects (configPolCRD , cfgPolicy ).
441- WithStatusSubresource (cfgPolicy ).
442- Build ()
506+ rec := ctrl.ConfigurationPolicyReconciler {
507+ Client : runtimeClient ,
508+ DecryptionConcurrency : 1 ,
509+ DynamicWatcher : dynamicWatcher ,
510+ Scheme : scheme .Scheme ,
511+ Recorder : record .NewFakeRecorder (8 ),
512+ InstanceName : "policy-cli" ,
513+ TargetK8sClient : clientset ,
514+ TargetK8sDynamicClient : dynamicClient ,
515+ SelectorReconciler : & nsSelReconciler ,
516+ EnableMetrics : false ,
517+ UninstallMode : false ,
518+ EvalBackoffSeconds : 5 ,
519+ FullDiffs : d .fullDiffs ,
520+ }
443521
444- nsSelUpdatesChan := make (chan event.GenericEvent , 20 )
445- nsSelReconciler := common .NewNamespaceSelectorReconciler (runtimeClient , nsSelUpdatesChan )
522+ // wait for dynamic watcher to have started
523+ <- rec .DynamicWatcher .Started ()
524+
525+ if d .fromCluster {
526+ return & rec , nil
527+ }
446528
447529 defaultNs := & unstructured.Unstructured {
448530 Object : map [string ]interface {}{
@@ -467,21 +549,7 @@ func (d *DryRunner) setupReconciler(
467549 return nil , err
468550 }
469551
470- rec := ctrl.ConfigurationPolicyReconciler {
471- Client : runtimeClient ,
472- DecryptionConcurrency : 1 ,
473- DynamicWatcher : dynamicWatcher ,
474- Scheme : scheme .Scheme ,
475- Recorder : record .NewFakeRecorder (8 ),
476- InstanceName : "policy-cli" ,
477- TargetK8sClient : clientset ,
478- TargetK8sDynamicClient : dynamicClient ,
479- SelectorReconciler : & nsSelReconciler ,
480- EnableMetrics : false ,
481- UninstallMode : false ,
482- EvalBackoffSeconds : 5 ,
483- FullDiffs : d .fullDiffs ,
484- }
552+ fakeClientset := clientset .(* clientsetfake.Clientset )
485553
486554 if d .mappingsPath != "" {
487555 mFile , err := os .ReadFile (d .mappingsPath )
@@ -494,19 +562,16 @@ func (d *DryRunner) setupReconciler(
494562 return nil , err
495563 }
496564
497- clientset .Resources = mappings .ResourceLists (apiMappings )
565+ fakeClientset .Resources = mappings .ResourceLists (apiMappings )
498566 } else {
499- clientset .Resources , err = mappings .DefaultResourceLists ()
567+ fakeClientset .Resources , err = mappings .DefaultResourceLists ()
500568 if err != nil {
501569 return nil , err
502570 }
503571 }
504572
505573 // Add open-cluster-management policy CRD
506- addSupportedResources (clientset )
507-
508- // wait for dynamic watcher to have started
509- <- rec .DynamicWatcher .Started ()
574+ addSupportedResources (fakeClientset )
510575
511576 return & rec , nil
512577}
0 commit comments