@@ -55,7 +55,7 @@ type kubectlResourceOperations struct {
5555
5656type commandExecutor func (f cmdutil.Factory , ioStreams genericclioptions.IOStreams , fileName string ) error
5757
58- func (k * kubectlResourceOperations ) runResourceCommand (ctx context.Context , obj * unstructured.Unstructured , dryRunStrategy cmdutil.DryRunStrategy , executor commandExecutor ) (string , error ) {
58+ func (k * kubectlResourceOperations ) runResourceCommand (ctx context.Context , obj * unstructured.Unstructured , dryRunStrategy cmdutil.DryRunStrategy , serverSideDiff bool , executor commandExecutor ) (string , error ) {
5959 manifestBytes , err := json .Marshal (obj )
6060 if err != nil {
6161 return "" , err
@@ -91,21 +91,14 @@ func (k *kubectlResourceOperations) runResourceCommand(ctx context.Context, obj
9191 }
9292
9393 var out []string
94- if obj .GetAPIVersion () == "rbac.authorization.k8s.io/v1" {
95- // If it is an RBAC resource, run `kubectl auth reconcile`. This is preferred over
96- // `kubectl apply`, which cannot tolerate changes in roleRef, which is an immutable field.
97- // See: https://github.com/kubernetes/kubernetes/issues/66353
98- // `auth reconcile` will delete and recreate the resource if necessary
99- outReconcile , err := func () (string , error ) {
100- cleanup , err := k .processKubectlRun ("auth" )
101- if err != nil {
102- return "" , err
103- }
104- defer cleanup ()
105- return k .authReconcile (ctx , obj , manifestFile .Name (), dryRunStrategy )
106- }()
94+ // rbac resouces are first applied with auth reconcile kubectl feature.
95+ // serverSideDiff should avoid this step as the resources are not being actually
96+ // applied but just running in dryrun mode. Also, kubectl auth reconcile doesn't
97+ // currently support running dryrun in server mode.
98+ if obj .GetAPIVersion () == "rbac.authorization.k8s.io/v1" && ! serverSideDiff {
99+ outReconcile , err := k .rbacReconcile (ctx , obj , manifestFile .Name (), dryRunStrategy )
107100 if err != nil {
108- return "" , err
101+ return "" , fmt . Errorf ( "error running rbacReconcile: %s" , err )
109102 }
110103 out = append (out , outReconcile )
111104 // We still want to fallthrough and run `kubectl apply` in order set the
@@ -131,6 +124,28 @@ func (k *kubectlResourceOperations) runResourceCommand(ctx context.Context, obj
131124 return strings .Join (out , ". " ), nil
132125}
133126
127+ // rbacReconcile will perform reconciliation for RBAC resources. It will run
128+ // the following command:
129+ //
130+ // kubectl auth reconcile
131+ //
132+ // This is preferred over `kubectl apply`, which cannot tolerate changes in
133+ // roleRef, which is an immutable field.
134+ // See: https://github.com/kubernetes/kubernetes/issues/66353
135+ // `auth reconcile` will delete and recreate the resource if necessary
136+ func (k * kubectlResourceOperations ) rbacReconcile (ctx context.Context , obj * unstructured.Unstructured , fileName string , dryRunStrategy cmdutil.DryRunStrategy ) (string , error ) {
137+ cleanup , err := k .processKubectlRun ("auth" )
138+ if err != nil {
139+ return "" , fmt .Errorf ("error processing kubectl run auth: %w" , err )
140+ }
141+ defer cleanup ()
142+ outReconcile , err := k .authReconcile (ctx , obj , fileName , dryRunStrategy )
143+ if err != nil {
144+ return "" , fmt .Errorf ("error running kubectl auth reconcile: %w" , err )
145+ }
146+ return outReconcile , nil
147+ }
148+
134149func kubeCmdFactory (kubeconfig , ns string , config * rest.Config ) cmdutil.Factory {
135150 kubeConfigFlags := genericclioptions .NewConfigFlags (true )
136151 if ns != "" {
@@ -149,7 +164,7 @@ func (k *kubectlResourceOperations) ReplaceResource(ctx context.Context, obj *un
149164 span .SetBaggageItem ("name" , obj .GetName ())
150165 defer span .Finish ()
151166 k .log .Info (fmt .Sprintf ("Replacing resource %s/%s in cluster: %s, namespace: %s" , obj .GetKind (), obj .GetName (), k .config .Host , obj .GetNamespace ()))
152- return k .runResourceCommand (ctx , obj , dryRunStrategy , func (f cmdutil.Factory , ioStreams genericclioptions.IOStreams , fileName string ) error {
167+ return k .runResourceCommand (ctx , obj , dryRunStrategy , false , func (f cmdutil.Factory , ioStreams genericclioptions.IOStreams , fileName string ) error {
153168 cleanup , err := k .processKubectlRun ("replace" )
154169 if err != nil {
155170 return err
@@ -170,7 +185,7 @@ func (k *kubectlResourceOperations) CreateResource(ctx context.Context, obj *uns
170185 span .SetBaggageItem ("kind" , gvk .Kind )
171186 span .SetBaggageItem ("name" , obj .GetName ())
172187 defer span .Finish ()
173- return k .runResourceCommand (ctx , obj , dryRunStrategy , func (f cmdutil.Factory , ioStreams genericclioptions.IOStreams , fileName string ) error {
188+ return k .runResourceCommand (ctx , obj , dryRunStrategy , false , func (f cmdutil.Factory , ioStreams genericclioptions.IOStreams , fileName string ) error {
174189 cleanup , err := k .processKubectlRun ("create" )
175190 if err != nil {
176191 return err
@@ -230,7 +245,7 @@ func (k *kubectlResourceOperations) ApplyResource(ctx context.Context, obj *unst
230245 span .SetBaggageItem ("name" , obj .GetName ())
231246 defer span .Finish ()
232247 k .log .Info (fmt .Sprintf ("Applying resource %s/%s in cluster: %s, namespace: %s" , obj .GetKind (), obj .GetName (), k .config .Host , obj .GetNamespace ()))
233- return k .runResourceCommand (ctx , obj , dryRunStrategy , func (f cmdutil.Factory , ioStreams genericclioptions.IOStreams , fileName string ) error {
248+ return k .runResourceCommand (ctx , obj , dryRunStrategy , serverSideDiff , func (f cmdutil.Factory , ioStreams genericclioptions.IOStreams , fileName string ) error {
234249 cleanup , err := k .processKubectlRun ("apply" )
235250 if err != nil {
236251 return err
@@ -317,7 +332,7 @@ func (k *kubectlResourceOperations) newApplyOptions(ioStreams genericclioptions.
317332 if manager != "" {
318333 o .FieldManager = manager
319334 }
320- if serverSideApply {
335+ if serverSideApply || serverSideDiff {
321336 o .ForceConflicts = true
322337 }
323338 return o , nil
@@ -469,9 +484,8 @@ func (k *kubectlResourceOperations) authReconcile(ctx context.Context, obj *unst
469484 }
470485 reconcileOpts , err := newReconcileOptions (k .fact , kubeClient , manifestFile , ioStreams , obj .GetNamespace (), dryRunStrategy )
471486 if err != nil {
472- return "" , err
487+ return "" , fmt . Errorf ( "error calling newReconcileOptions: %w" , err )
473488 }
474-
475489 err = reconcileOpts .Validate ()
476490 if err != nil {
477491 return "" , errors .New (cleanKubectlOutput (err .Error ()))
0 commit comments