Skip to content

Commit ad039ca

Browse files
authored
🐛 Fix deleting resources from Helm charts (#31)
There are two problems with deleting resources from Helm charts right now: The first one is that the objects stay in the status.resources list, even after they are deleted. The second is that instead of returning an error when a deletion failed, we returned an error on success. Instead, as we base deletion of objects based on the status.resources list (this can be changed in the future), we keep resources that are supposed to be deleted but failed to in the status as not-synced. Additionally, we requeue and try the deletion another time, just as we do with applying currently. As the current Update method is unused since we changed from create to apply, it is deleted. Mocks are generated freshly as well. Signed-off-by: janiskemper <[email protected]>
1 parent dde4841 commit ad039ca

File tree

4 files changed

+19
-83
lines changed

4 files changed

+19
-83
lines changed

internal/controller/clusteraddon_controller.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
200200

201201
shouldRequeue, err := r.templateAndApplyClusterAddonHelmChart(ctx, in)
202202
if err != nil {
203+
conditions.MarkFalse(clusterAddon, csov1alpha1.HelmChartAppliedCondition, csov1alpha1.FailedToApplyObjectsReason, clusterv1.ConditionSeverityError, "failed to apply")
203204
return ctrl.Result{}, fmt.Errorf("failed to apply helm chart: %w", err)
204205
}
205206
if shouldRequeue {
@@ -240,6 +241,7 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
240241

241242
shouldRequeue, err := r.templateAndApplyClusterAddonHelmChart(ctx, in)
242243
if err != nil {
244+
conditions.MarkFalse(clusterAddon, csov1alpha1.HelmChartAppliedCondition, csov1alpha1.FailedToApplyObjectsReason, clusterv1.ConditionSeverityError, "failed to apply")
243245
return ctrl.Result{}, fmt.Errorf("failed to apply helm chart: %w", err)
244246
}
245247
if shouldRequeue {

pkg/kube/helpers.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,13 @@ func parseK8sYaml(template []byte) ([]*unstructured.Unstructured, error) {
105105
return objs, nil
106106
}
107107

108-
func getResourceMap(resources []*csov1alpha1.Resource) (resourceMap map[types.NamespacedName]*csov1alpha1.Resource, notToApply []*csov1alpha1.Resource) {
109-
resourceMap = make(map[types.NamespacedName]*csov1alpha1.Resource)
110-
notToApply = make([]*csov1alpha1.Resource, 0, len(resources))
108+
func getResourceMap(resources []*csov1alpha1.Resource) map[types.NamespacedName]*csov1alpha1.Resource {
109+
resourceMap := make(map[types.NamespacedName]*csov1alpha1.Resource)
111110

112111
for i, resource := range resources {
113-
if resource.Status == csov1alpha1.ResourceStatusSynced {
114-
notToApply = append(notToApply, resources[i])
115-
}
116112
resourceMap[types.NamespacedName{Name: resource.Name, Namespace: resource.Namespace}] = resources[i]
117113
}
118-
return resourceMap, notToApply
114+
return resourceMap
119115
}
120116

121117
func setLabel(target *unstructured.Unstructured, key, val string) error {

pkg/kube/kube.go

Lines changed: 14 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323

2424
csov1alpha1 "github.com/SovereignCloudStack/cluster-stack-operator/api/v1alpha1"
25+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2627
"k8s.io/apimachinery/pkg/types"
2728
"k8s.io/client-go/kubernetes"
@@ -37,7 +38,6 @@ type kube struct {
3738
// Client has all the meathod for helm chart kube operation.
3839
type Client interface {
3940
Apply(ctx context.Context, template []byte, oldResources []*csov1alpha1.Resource) (newResources []*csov1alpha1.Resource, shouldRequeue bool, err error)
40-
Update(ctx context.Context, template []byte, oldResources []*csov1alpha1.Resource) (newResources []*csov1alpha1.Resource, shouldRequeue bool, err error)
4141
Delete(template []byte) error
4242
}
4343

@@ -72,12 +72,13 @@ func (k *kube) Apply(ctx context.Context, template []byte, oldResources []*csov1
7272
return nil, false, fmt.Errorf("couldn't parse k8s yaml: %w", err)
7373
}
7474

75-
resourceMap, newResources := getResourceMap(oldResources)
75+
resourceMap := getResourceMap(oldResources)
7676
for _, obj := range objs {
7777
oldResource, found := resourceMap[types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}]
7878

7979
// do nothing if synced
8080
if found && oldResource.Status == csov1alpha1.ResourceStatusSynced {
81+
newResources = append(newResources, oldResource)
8182
continue
8283
}
8384

@@ -103,45 +104,7 @@ func (k *kube) Apply(ctx context.Context, template []byte, oldResources []*csov1
103104
reterr := fmt.Errorf("failed to apply object: %w", err)
104105
resource.Error = reterr.Error()
105106
resource.Status = csov1alpha1.ResourceStatusNotSynced
106-
logger.Error(reterr, "failed to apply object", "obj", obj.GetObjectKind().GroupVersionKind())
107-
shouldRequeue = true
108-
} else {
109-
resource.Status = csov1alpha1.ResourceStatusSynced
110-
}
111-
112-
newResources = append(newResources, resource)
113-
}
114-
115-
return newResources, shouldRequeue, nil
116-
}
117-
118-
func (k *kube) Update(ctx context.Context, template []byte, oldResources []*csov1alpha1.Resource) (newResources []*csov1alpha1.Resource, shouldRequeue bool, err error) {
119-
logger := log.FromContext(ctx)
120-
121-
objs, err := parseK8sYaml(template)
122-
if err != nil {
123-
return nil, false, fmt.Errorf("couldn't parse k8s yaml: %w", err)
124-
}
125-
126-
for _, obj := range objs {
127-
if err := setLabel(obj, ObjectLabelKeyOwned, ObjectLabelValueOwned); err != nil {
128-
return nil, false, fmt.Errorf("error setting label: %w", err)
129-
}
130-
131-
// call the function and get dynamic.ResourceInterface
132-
// getDynamicResourceInterface
133-
dr, err := getDynamicResourceInterface(k.Namespace, k.RestConfig, obj.GroupVersionKind())
134-
if err != nil {
135-
return nil, false, fmt.Errorf("failed to get dynamic resource interface: %w", err)
136-
}
137-
138-
resource := csov1alpha1.NewResourceFromUnstructured(obj)
139-
140-
if _, err := dr.Apply(ctx, obj.GetName(), obj, metav1.ApplyOptions{FieldManager: "kubectl", Force: true}); err != nil {
141-
reterr := fmt.Errorf("failed to apply object: %w", err)
142-
resource.Error = reterr.Error()
143-
resource.Status = csov1alpha1.ResourceStatusNotSynced
144-
logger.Error(reterr, "failed to apply object", "obj", obj.GetObjectKind().GroupVersionKind())
107+
logger.Error(reterr, "failed to apply object", "obj", obj.GetObjectKind().GroupVersionKind(), "name", obj.GetName(), "namespace", obj.GetNamespace())
145108
shouldRequeue = true
146109
} else {
147110
resource.Status = csov1alpha1.ResourceStatusSynced
@@ -163,10 +126,18 @@ func (k *kube) Update(ctx context.Context, template []byte, oldResources []*csov
163126
return nil, false, fmt.Errorf("failed to get dynamic resource interface: %w", err)
164127
}
165128

166-
if err := dr.Delete(ctx, resource.Name, metav1.DeleteOptions{}); err == nil {
167-
return nil, false, fmt.Errorf("failed to delete object %q of namespace %q: %w", resource.Name, resource.Namespace, err)
129+
if err := dr.Delete(ctx, resource.Name, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
130+
reterr := fmt.Errorf("failed to delete object: %w", err)
131+
logger.Error(reterr, "failed to delete object", "obj", resource.GroupVersionKind(), "namespacedName", resource.NamespacedName())
132+
133+
// append resource to status and requeue again to be able to retry deletion
134+
resource.Status = csov1alpha1.ResourceStatusNotSynced
135+
resource.Error = reterr.Error()
136+
newResources = append(newResources, resource)
137+
shouldRequeue = true
168138
}
169139
}
140+
170141
return newResources, shouldRequeue, nil
171142
}
172143

pkg/kube/mocks/Client.go

Lines changed: 0 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)