Skip to content

Commit 1ce2acc

Browse files
author
Alexander Matyushentsev
authored
feat: support replace strategy for CRD (argoproj#252)
Signed-off-by: Alexander Matyushentsev <[email protected]>
1 parent 3c778a5 commit 1ce2acc

File tree

3 files changed

+53
-3
lines changed

3 files changed

+53
-3
lines changed

pkg/sync/sync_context.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,10 +819,21 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun bool, force bool, validat
819819
var err error
820820
var message string
821821
shouldReplace := sc.replace || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionReplace)
822-
// always use 'kubectl apply' for CRDs since 'replace' might recreate resource and so delete all CRD instances
823-
if shouldReplace && !kube.IsCRD(t.targetObj) {
822+
if shouldReplace {
824823
if t.liveObj != nil {
825-
message, err = sc.kubectl.ReplaceResource(context.TODO(), sc.rawConfig, t.targetObj, t.targetObj.GetNamespace(), dryRunStrategy, force)
824+
// Avoid using `kubectl replace` for CRDs since 'replace' might recreate resource and so delete all CRD instances
825+
if kube.IsCRD(t.targetObj) {
826+
update := t.targetObj.DeepCopy()
827+
update.SetResourceVersion(t.liveObj.GetResourceVersion())
828+
_, err = sc.kubectl.UpdateResource(context.TODO(), sc.rawConfig, update, t.targetObj.GetNamespace(), dryRunStrategy)
829+
if err == nil {
830+
message = fmt.Sprintf("%s/%s updated", t.targetObj.GetKind(), t.targetObj.GetName())
831+
} else {
832+
message = fmt.Sprintf("error when updating: %v", err.Error())
833+
}
834+
} else {
835+
message, err = sc.kubectl.ReplaceResource(context.TODO(), sc.rawConfig, t.targetObj, t.targetObj.GetNamespace(), dryRunStrategy, force)
836+
}
826837
} else {
827838
_, err = sc.kubectl.CreateResource(context.TODO(), sc.rawConfig, t.targetObj, t.targetObj.GetNamespace(), dryRunStrategy)
828839
if err == nil {

pkg/utils/kube/ctl.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type Kubectl interface {
4242
ApplyResource(ctx context.Context, config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRunStrategy cmdutil.DryRunStrategy, force, validate bool) (string, error)
4343
ReplaceResource(ctx context.Context, config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRunStrategy cmdutil.DryRunStrategy, force bool) (string, error)
4444
CreateResource(ctx context.Context, config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRunStrategy cmdutil.DryRunStrategy) (*unstructured.Unstructured, error)
45+
UpdateResource(ctx context.Context, config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRunStrategy cmdutil.DryRunStrategy) (*unstructured.Unstructured, error)
4546
ConvertToVersion(obj *unstructured.Unstructured, group, version string) (*unstructured.Unstructured, error)
4647
DeleteResource(ctx context.Context, config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string, deleteOptions metav1.DeleteOptions) error
4748
GetResource(ctx context.Context, config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string) (*unstructured.Unstructured, error)
@@ -368,6 +369,35 @@ func (k *KubectlCmd) CreateResource(ctx context.Context, config *rest.Config, ob
368369
return resourceIf.Create(ctx, obj, createOptions)
369370
}
370371

372+
func (k *KubectlCmd) UpdateResource(ctx context.Context, config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRunStrategy cmdutil.DryRunStrategy) (*unstructured.Unstructured, error) {
373+
gvk := obj.GroupVersionKind()
374+
span := k.Tracer.StartSpan("UpdateResource")
375+
span.SetBaggageItem("kind", gvk.Kind)
376+
span.SetBaggageItem("name", obj.GetName())
377+
defer span.Finish()
378+
dynamicIf, err := dynamic.NewForConfig(config)
379+
if err != nil {
380+
return nil, err
381+
}
382+
disco, err := discovery.NewDiscoveryClientForConfig(config)
383+
if err != nil {
384+
return nil, err
385+
}
386+
apiResource, err := ServerResourceForGroupVersionKind(disco, gvk)
387+
if err != nil {
388+
return nil, err
389+
}
390+
resource := gvk.GroupVersion().WithResource(apiResource.Name)
391+
resourceIf := ToResourceInterface(dynamicIf, apiResource, resource, namespace)
392+
393+
updateOptions := metav1.UpdateOptions{}
394+
switch dryRunStrategy {
395+
case cmdutil.DryRunClient, cmdutil.DryRunServer:
396+
updateOptions.DryRun = []string{metav1.DryRunAll}
397+
}
398+
return resourceIf.Update(ctx, obj, updateOptions)
399+
}
400+
371401
// ApplyResource performs an apply of a unstructured resource
372402
func (k *KubectlCmd) ApplyResource(ctx context.Context, config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRunStrategy cmdutil.DryRunStrategy, force, validate bool) (string, error) {
373403
span := k.Tracer.StartSpan("ApplyResource")

pkg/utils/kube/kubetest/mock.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ func (k *MockKubectlCmd) CreateResource(ctx context.Context, config *rest.Config
9898
return obj, command.Err
9999
}
100100

101+
func (k *MockKubectlCmd) UpdateResource(ctx context.Context, config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRunStrategy cmdutil.DryRunStrategy) (*unstructured.Unstructured, error) {
102+
k.SetLastResourceCommand(kube.GetResourceKey(obj), "update")
103+
command, ok := k.Commands[obj.GetName()]
104+
if !ok {
105+
return obj, nil
106+
}
107+
return obj, command.Err
108+
}
109+
101110
func (k *MockKubectlCmd) ApplyResource(ctx context.Context, config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRunStrategy cmdutil.DryRunStrategy, force, validate bool) (string, error) {
102111
k.SetLastValidate(validate)
103112
k.SetLastResourceCommand(kube.GetResourceKey(obj), "apply")

0 commit comments

Comments
 (0)