Skip to content

Commit c1e2359

Browse files
authored
fix: address kubectl auth reconcile during server-side diff (#562)
* fix: address kubectl auth reconcile during server-side diff Signed-off-by: Leonardo Luz Almeida <[email protected]> * server-side diff force conflict Signed-off-by: Leonardo Luz Almeida <[email protected]> * do not ssa when ssd rbac Signed-off-by: Leonardo Luz Almeida <[email protected]> * debug Signed-off-by: Leonardo Luz Almeida <[email protected]> * better logs Signed-off-by: Leonardo Luz Almeida <[email protected]> * remove debug Signed-off-by: Leonardo Luz Almeida <[email protected]> * add comments Signed-off-by: Leonardo Luz Almeida <[email protected]> * better comments Signed-off-by: Leonardo Luz Almeida <[email protected]> * refactoring on rbacReconcile Signed-off-by: Leonardo Luz Almeida <[email protected]> --------- Signed-off-by: Leonardo Luz Almeida <[email protected]>
1 parent aba3819 commit c1e2359

File tree

2 files changed

+40
-26
lines changed

2 files changed

+40
-26
lines changed

pkg/diff/diff.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,17 @@ func serverSideDiff(config, live *unstructured.Unstructured, opts ...Option) (*D
165165
}
166166
predictedLiveStr, err := o.serverSideDryRunner.Run(context.Background(), config, o.manager)
167167
if err != nil {
168-
return nil, fmt.Errorf("error running server side apply in dryrun mode: %w", err)
168+
return nil, fmt.Errorf("error running server side apply in dryrun mode for resource %s/%s: %w", config.GetKind(), config.GetName(), err)
169169
}
170170
predictedLive, err := jsonStrToUnstructured(predictedLiveStr)
171171
if err != nil {
172-
return nil, fmt.Errorf("error converting json string to unstructured: %w", err)
172+
return nil, fmt.Errorf("error converting json string to unstructured for resource %s/%s: %w", config.GetKind(), config.GetName(), err)
173173
}
174174

175175
if o.ignoreMutationWebhook {
176176
predictedLive, err = removeWebhookMutation(predictedLive, live, o.gvkParser, o.manager)
177177
if err != nil {
178-
return nil, fmt.Errorf("error removing non config mutations: %w", err)
178+
return nil, fmt.Errorf("error removing non config mutations for resource %s/%s: %w", config.GetKind(), config.GetName(), err)
179179
}
180180
}
181181

@@ -184,13 +184,13 @@ func serverSideDiff(config, live *unstructured.Unstructured, opts ...Option) (*D
184184

185185
predictedLiveBytes, err := json.Marshal(predictedLive)
186186
if err != nil {
187-
return nil, fmt.Errorf("error marshaling predicted live resource: %w", err)
187+
return nil, fmt.Errorf("error marshaling predicted live for resource %s/%s: %w", config.GetKind(), config.GetName(), err)
188188
}
189189

190190
unstructured.RemoveNestedField(live.Object, "metadata", "managedFields")
191191
liveBytes, err := json.Marshal(live)
192192
if err != nil {
193-
return nil, fmt.Errorf("error marshaling live resource: %w", err)
193+
return nil, fmt.Errorf("error marshaling live resource %s/%s: %w", config.GetKind(), config.GetName(), err)
194194
}
195195
return buildDiffResult(predictedLiveBytes, liveBytes), nil
196196
}

pkg/utils/kube/resource_ops.go

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type kubectlResourceOperations struct {
5555

5656
type 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+
134149
func 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

Comments
 (0)