Skip to content

Commit 7b286db

Browse files
authored
Merge pull request #11916 from k8s-infra-cherrypick-robot/cherry-pick-11211-to-release-1.9
[release-1.9] 🌱 MachineSet controller: delete Bootstrap object when creating InfraMachine object failed
2 parents 9530644 + 34138a8 commit 7b286db

File tree

2 files changed

+164
-2
lines changed

2 files changed

+164
-2
lines changed

internal/controllers/machineset/machineset_controller.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -768,10 +768,17 @@ func (r *Reconciler) syncReplicas(ctx context.Context, s *scope) (ctrl.Result, e
768768
},
769769
})
770770
if err != nil {
771+
var deleteErr error
772+
if bootstrapRef != nil {
773+
// Cleanup the bootstrap resource if we can't create the InfraMachine; or we might risk to leak it.
774+
if err := r.Client.Delete(ctx, util.ObjectReferenceToUnstructured(*bootstrapRef)); err != nil && !apierrors.IsNotFound(err) {
775+
deleteErr = errors.Wrapf(err, "failed to cleanup %s %s after %s creation failed", bootstrapRef.Kind, klog.KRef(bootstrapRef.Namespace, bootstrapRef.Name), (&ms.Spec.Template.Spec.InfrastructureRef).Kind)
776+
}
777+
}
771778
conditions.MarkFalse(ms, clusterv1.MachinesCreatedCondition, clusterv1.InfrastructureTemplateCloningFailedReason, clusterv1.ConditionSeverityError, err.Error())
772-
return ctrl.Result{}, errors.Wrapf(err, "failed to clone infrastructure machine from %s %s while creating a machine",
779+
return ctrl.Result{}, kerrors.NewAggregate([]error{errors.Wrapf(err, "failed to clone infrastructure machine from %s %s while creating a machine",
773780
ms.Spec.Template.Spec.InfrastructureRef.Kind,
774-
klog.KRef(ms.Spec.Template.Spec.InfrastructureRef.Namespace, ms.Spec.Template.Spec.InfrastructureRef.Name))
781+
klog.KRef(ms.Spec.Template.Spec.InfrastructureRef.Namespace, ms.Spec.Template.Spec.InfrastructureRef.Name)), deleteErr})
775782
}
776783
log = log.WithValues(infraRef.Kind, klog.KRef(infraRef.Namespace, infraRef.Name))
777784
machine.Spec.InfrastructureRef = *infraRef

internal/controllers/machineset/machineset_controller_test.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ limitations under the License.
1717
package machineset
1818

1919
import (
20+
"context"
2021
"fmt"
22+
"strings"
2123
"testing"
2224
"time"
2325

@@ -26,11 +28,13 @@ import (
2628
apierrors "k8s.io/apimachinery/pkg/api/errors"
2729
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2830
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
31+
"k8s.io/apimachinery/pkg/runtime/schema"
2932
"k8s.io/apimachinery/pkg/util/intstr"
3033
"k8s.io/client-go/tools/record"
3134
"k8s.io/utils/ptr"
3235
"sigs.k8s.io/controller-runtime/pkg/client"
3336
"sigs.k8s.io/controller-runtime/pkg/client/fake"
37+
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
3438
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3539
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3640

@@ -2346,6 +2350,157 @@ func TestMachineSetReconciler_syncReplicas(t *testing.T) {
23462350
})
23472351
}
23482352

2353+
func TestMachineSetReconciler_syncReplicas_WithErrors(t *testing.T) {
2354+
t.Run("should hold off on sync replicas when create Infrastructure of machine failed ", func(t *testing.T) {
2355+
g := NewWithT(t)
2356+
fakeClient := fake.NewClientBuilder().WithObjects().WithInterceptorFuncs(interceptor.Funcs{
2357+
Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error {
2358+
// simulate scenarios where infra object creation fails
2359+
if obj.GetObjectKind().GroupVersionKind().Kind == "GenericInfrastructureMachine" {
2360+
return fmt.Errorf("inject error for create machineInfrastructure")
2361+
}
2362+
return client.Create(ctx, obj, opts...)
2363+
},
2364+
}).Build()
2365+
2366+
r := &Reconciler{
2367+
Client: fakeClient,
2368+
}
2369+
testCluster := &clusterv1.Cluster{}
2370+
testCluster.Namespace = "default"
2371+
testCluster.Name = "test-cluster"
2372+
version := "v1.14.2"
2373+
duration10m := &metav1.Duration{Duration: 10 * time.Minute}
2374+
machineSet := &clusterv1.MachineSet{
2375+
ObjectMeta: metav1.ObjectMeta{
2376+
Name: "machineset1",
2377+
Namespace: metav1.NamespaceDefault,
2378+
Finalizers: []string{"block-deletion"},
2379+
},
2380+
Spec: clusterv1.MachineSetSpec{
2381+
Replicas: ptr.To[int32](1),
2382+
ClusterName: "test-cluster",
2383+
Template: clusterv1.MachineTemplateSpec{
2384+
Spec: clusterv1.MachineSpec{
2385+
ClusterName: testCluster.Name,
2386+
Version: &version,
2387+
Bootstrap: clusterv1.Bootstrap{
2388+
ConfigRef: &corev1.ObjectReference{
2389+
APIVersion: "bootstrap.cluster.x-k8s.io/v1beta1",
2390+
Kind: "GenericBootstrapConfigTemplate",
2391+
Name: "ms-template",
2392+
Namespace: metav1.NamespaceDefault,
2393+
},
2394+
},
2395+
InfrastructureRef: corev1.ObjectReference{
2396+
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
2397+
Kind: "GenericInfrastructureMachineTemplate",
2398+
Name: "ms-template",
2399+
Namespace: metav1.NamespaceDefault,
2400+
},
2401+
NodeDrainTimeout: duration10m,
2402+
NodeDeletionTimeout: duration10m,
2403+
NodeVolumeDetachTimeout: duration10m,
2404+
},
2405+
},
2406+
},
2407+
}
2408+
2409+
// Create bootstrap template resource.
2410+
bootstrapResource := map[string]interface{}{
2411+
"kind": "GenericBootstrapConfig",
2412+
"apiVersion": "bootstrap.cluster.x-k8s.io/v1beta1",
2413+
"metadata": map[string]interface{}{
2414+
"annotations": map[string]interface{}{
2415+
"precedence": "GenericBootstrapConfig",
2416+
},
2417+
},
2418+
}
2419+
bootstrapTmpl := &unstructured.Unstructured{
2420+
Object: map[string]interface{}{
2421+
"spec": map[string]interface{}{
2422+
"template": bootstrapResource,
2423+
},
2424+
},
2425+
}
2426+
bootstrapTmpl.SetKind("GenericBootstrapConfigTemplate")
2427+
bootstrapTmpl.SetAPIVersion("bootstrap.cluster.x-k8s.io/v1beta1")
2428+
bootstrapTmpl.SetName("ms-template")
2429+
bootstrapTmpl.SetNamespace(metav1.NamespaceDefault)
2430+
g.Expect(r.Client.Create(context.TODO(), bootstrapTmpl)).To(Succeed())
2431+
2432+
// Create infrastructure template resource.
2433+
infraResource := map[string]interface{}{
2434+
"kind": "GenericInfrastructureMachine",
2435+
"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
2436+
"metadata": map[string]interface{}{
2437+
"annotations": map[string]interface{}{
2438+
"precedence": "GenericInfrastructureMachineTemplate",
2439+
},
2440+
},
2441+
"spec": map[string]interface{}{
2442+
"size": "3xlarge",
2443+
},
2444+
}
2445+
infraTmpl := &unstructured.Unstructured{
2446+
Object: map[string]interface{}{
2447+
"spec": map[string]interface{}{
2448+
"template": infraResource,
2449+
},
2450+
},
2451+
}
2452+
infraTmpl.SetKind("GenericInfrastructureMachineTemplate")
2453+
infraTmpl.SetAPIVersion("infrastructure.cluster.x-k8s.io/v1beta1")
2454+
infraTmpl.SetName("ms-template")
2455+
infraTmpl.SetNamespace(metav1.NamespaceDefault)
2456+
g.Expect(r.Client.Create(context.TODO(), infraTmpl)).To(Succeed())
2457+
2458+
s := &scope{
2459+
cluster: testCluster,
2460+
machineSet: machineSet,
2461+
machines: []*clusterv1.Machine{},
2462+
getAndAdoptMachinesForMachineSetSucceeded: true,
2463+
}
2464+
_, err := r.syncReplicas(ctx, s)
2465+
g.Expect(err).To(HaveOccurred())
2466+
2467+
// Verify the proper condition is set on the MachineSet.
2468+
condition := clusterv1.MachinesCreatedCondition
2469+
g.Expect(conditions.Has(machineSet, condition)).To(BeTrue(), "MachineSet should have the %s condition set", condition)
2470+
2471+
machinesCreatedCondition := conditions.Get(machineSet, condition)
2472+
g.Expect(machinesCreatedCondition.Status).
2473+
To(Equal(corev1.ConditionFalse), "%s condition status should be %s", condition, corev1.ConditionFalse)
2474+
g.Expect(machinesCreatedCondition.Reason).
2475+
To(Equal(clusterv1.InfrastructureTemplateCloningFailedReason), "%s condition reason should be %s", condition, clusterv1.InfrastructureTemplateCloningFailedReason)
2476+
2477+
// Verify no new Machines are created.
2478+
machineList := &clusterv1.MachineList{}
2479+
g.Expect(r.Client.List(ctx, machineList)).To(Succeed())
2480+
g.Expect(machineList.Items).To(BeEmpty(), "There should not be any machines")
2481+
2482+
// Verify no boostrap object created
2483+
bootstrapList := &unstructured.UnstructuredList{}
2484+
bootstrapList.SetGroupVersionKind(schema.GroupVersionKind{
2485+
Group: bootstrapTmpl.GetObjectKind().GroupVersionKind().Group,
2486+
Version: bootstrapTmpl.GetObjectKind().GroupVersionKind().Version,
2487+
Kind: strings.TrimSuffix(bootstrapTmpl.GetObjectKind().GroupVersionKind().Kind, clusterv1.TemplateSuffix),
2488+
})
2489+
g.Expect(r.Client.List(ctx, bootstrapList)).To(Succeed())
2490+
g.Expect(bootstrapList.Items).To(BeEmpty(), "There should not be any bootstrap object")
2491+
2492+
// Verify no infra object created
2493+
infraList := &unstructured.UnstructuredList{}
2494+
infraList.SetGroupVersionKind(schema.GroupVersionKind{
2495+
Group: infraTmpl.GetObjectKind().GroupVersionKind().Group,
2496+
Version: infraTmpl.GetObjectKind().GroupVersionKind().Version,
2497+
Kind: strings.TrimSuffix(infraTmpl.GetObjectKind().GroupVersionKind().Kind, clusterv1.TemplateSuffix),
2498+
})
2499+
g.Expect(r.Client.List(ctx, infraList)).To(Succeed())
2500+
g.Expect(infraList.Items).To(BeEmpty(), "There should not be any infra object")
2501+
})
2502+
}
2503+
23492504
func TestComputeDesiredMachine(t *testing.T) {
23502505
duration5s := &metav1.Duration{Duration: 5 * time.Second}
23512506
duration10s := &metav1.Duration{Duration: 10 * time.Second}

0 commit comments

Comments
 (0)