Repository: syself/cluster-api-provider-hetzner (go)

Issue:
CAPH: Handle HCloudMachineTemplates with ClusterClass as owner
## Description
The HCloudMachineTemplate controller assumes that the owner is a cluster. This is not necessarily the case when we use ClusterClasses, because then the ClusterClass is the owner - not the cluster.

In these cases, we just want to abort. We don't need to do anything if the owner is a clusterClass.

Reference: 
https://github.com/syself/cluster-api-provider-hetzner/blob/main/controllers/hcloudmachinetemplate_controller.go#L67


Pull request description:
✨ Handle HCloudMachineTemplate controller clusterclass as a owner
<!-- If this is your first PR, welcome! Please make sure you read the [contributing guidelines](../CONTRIBUTING.md#contributing-a-patch). -->
<!-- please add an icon to the title of this PR and delete this line and similar ones -->
<!-- the icon will be either ⚠️ (:warning:, major or breaking changes), ✨ (:sparkles:, feature additions), 🐛 (:bug:, patches and bugfixes), 📖 (:book:, documentation or proposals), or 🌱 (:seedling:, minor or other) -->

**What this PR does / why we need it**:

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #921 

**Special notes for your reviewer**:

_Please confirm that if this PR changes any image versions, then that's the sole change this PR makes._

**TODOs**:

- [ ] squash commits
- [ ] include documentation
- [ ] add unit tests



Patch:
diff --git a/api/v1beta1/hcloudmachinetemplate_types.go b/api/v1beta1/hcloudmachinetemplate_types.go
index f8d849cf..4fc8c929 100644
--- a/api/v1beta1/hcloudmachinetemplate_types.go
+++ b/api/v1beta1/hcloudmachinetemplate_types.go
@@ -38,6 +38,10 @@ type HCloudMachineTemplateStatus struct {
 	// Conditions defines current service state of the HCloudMachineTemplate.
 	// +optional
 	Conditions clusterv1.Conditions `json:"conditions,omitempty"`
+
+	// OwnerType is the type of object that owns the HCloudMachineTemplate.
+	// +optional
+	OwnerType string `json:"ownerType,omitempty"`
 }
 
 // +kubebuilder:subresource:status
diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachinetemplates.yaml
index 2c5a14d9..b97f8ba4 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachinetemplates.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_hcloudmachinetemplates.yaml
@@ -230,6 +230,9 @@ spec:
                   - type
                   type: object
                 type: array
+              ownerType:
+                description: OwnerType is the type of object that owns the HCloudMachineTemplate.
+                type: string
             type: object
         type: object
     served: true
diff --git a/controllers/hcloudmachinetemplate_controller.go b/controllers/hcloudmachinetemplate_controller.go
index 247f91ec..d9e82103 100644
--- a/controllers/hcloudmachinetemplate_controller.go
+++ b/controllers/hcloudmachinetemplate_controller.go
@@ -22,22 +22,24 @@ import (
 	"fmt"
 	"time"
 
+	infrav1 "github.com/syself/cluster-api-provider-hetzner/api/v1beta1"
+	"github.com/syself/cluster-api-provider-hetzner/pkg/scope"
+	secretutil "github.com/syself/cluster-api-provider-hetzner/pkg/secrets"
+	hcloudclient "github.com/syself/cluster-api-provider-hetzner/pkg/services/hcloud/client"
+	"github.com/syself/cluster-api-provider-hetzner/pkg/services/hcloud/machinetemplate"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/klog/v2"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util"
 	"sigs.k8s.io/cluster-api/util/conditions"
+	"sigs.k8s.io/cluster-api/util/patch"
 	"sigs.k8s.io/cluster-api/util/predicates"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller"
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-
-	infrav1 "github.com/syself/cluster-api-provider-hetzner/api/v1beta1"
-	"github.com/syself/cluster-api-provider-hetzner/pkg/scope"
-	secretutil "github.com/syself/cluster-api-provider-hetzner/pkg/secrets"
-	hcloudclient "github.com/syself/cluster-api-provider-hetzner/pkg/services/hcloud/client"
-	"github.com/syself/cluster-api-provider-hetzner/pkg/services/hcloud/machinetemplate"
 )
 
 // HCloudMachineTemplateReconciler reconciles a HCloudMachineTemplate object.
@@ -64,13 +66,32 @@ func (r *HCloudMachineTemplateReconciler) Reconcile(ctx context.Context, req rec
 
 	log = log.WithValues("HCloudMachineTemplate", klog.KObj(machineTemplate))
 
+	patchHelper, err := patch.NewHelper(machineTemplate, r.Client)
+	if err != nil {
+		return reconcile.Result{}, fmt.Errorf("failed to get patch helper: %w", err)
+	}
+
+	defer func() {
+		if err := patchHelper.Patch(ctx, machineTemplate); err != nil {
+			log.Error(err, "failed to patch HCloudMachineTemplate")
+		}
+	}()
+
 	// Fetch the Cluster.
-	cluster, err := util.GetOwnerCluster(ctx, r.Client, machineTemplate.ObjectMeta)
+	var cluster *clusterv1.Cluster
+	clusterClass, err := getOwnerClusterClass(ctx, r.Client, machineTemplate.ObjectMeta)
+	// nothing to do if a ClusterClass is the owner
+	if err == nil && clusterClass != nil {
+		machineTemplate.Status.OwnerType = clusterClass.Kind
+		return reconcile.Result{}, nil
+	}
+	cluster, err = util.GetOwnerCluster(ctx, r.Client, machineTemplate.ObjectMeta)
 	if err != nil || cluster == nil {
 		log.Info(fmt.Sprintf("%s is missing ownerRef to cluster or cluster does not exist %s/%s",
 			machineTemplate.Kind, machineTemplate.Namespace, machineTemplate.Name))
 		return reconcile.Result{Requeue: true}, nil
 	}
+	machineTemplate.Status.OwnerType = cluster.Kind
 
 	log = log.WithValues("Cluster", klog.KObj(cluster))
 
@@ -119,10 +140,6 @@ func (r *HCloudMachineTemplateReconciler) Reconcile(ctx context.Context, req rec
 		} else {
 			conditions.MarkTrue(machineTemplate, infrav1.HCloudTokenAvailableCondition)
 		}
-
-		if err := machineTemplateScope.Close(ctx); err != nil && reterr == nil {
-			reterr = err
-		}
 	}()
 
 	// check whether rate limit has been reached and if so, then wait.
@@ -169,3 +186,35 @@ func (r *HCloudMachineTemplateReconciler) SetupWithManager(ctx context.Context,
 		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
 		Complete(r)
 }
+
+// getOwnerClusterClass returns the ClusterClass object owning the current resource.
+func getOwnerClusterClass(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*clusterv1.ClusterClass, error) {
+	for _, ref := range obj.GetOwnerReferences() {
+		if ref.Kind != "ClusterClass" {
+			continue
+		}
+		gv, err := schema.ParseGroupVersion(ref.APIVersion)
+		if err != nil {
+			return nil, fmt.Errorf("failed to parse GroupVersion: %w", err)
+		}
+		if gv.Group == clusterv1.GroupVersion.Group {
+			return getClusterClassByName(ctx, c, obj.Namespace, ref.Name)
+		}
+	}
+	return nil, nil
+}
+
+// getClusterClassByName finds and return a Cluster object using the specified params.
+func getClusterClassByName(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.ClusterClass, error) {
+	clusterClass := &clusterv1.ClusterClass{}
+	key := client.ObjectKey{
+		Namespace: namespace,
+		Name:      name,
+	}
+
+	if err := c.Get(ctx, key, clusterClass); err != nil {
+		return nil, fmt.Errorf("failed to get ClusterClass/%s: %w", name, err)
+	}
+
+	return clusterClass, nil
+}
diff --git a/go.mod b/go.mod
index ebc0d244..622f1a65 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,7 @@ require (
 	github.com/hetznercloud/hcloud-go/v2 v2.2.0
 	github.com/onsi/ginkgo/v2 v2.11.0
 	github.com/onsi/gomega v1.27.8
+	github.com/prometheus/client_golang v1.16.0
 	github.com/stretchr/testify v1.8.4
 	github.com/syself/hrobot-go v0.2.4
 	go.uber.org/zap v1.24.0
@@ -94,7 +95,6 @@ require (
 	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/prometheus/client_golang v1.16.0 // indirect
 	github.com/prometheus/client_model v0.4.0 // indirect
 	github.com/prometheus/common v0.42.0 // indirect
 	github.com/prometheus/procfs v0.10.1 // indirect
diff --git a/pkg/services/baremetal/host/host.go b/pkg/services/baremetal/host/host.go
index b3a808dd..9f29e888 100644
--- a/pkg/services/baremetal/host/host.go
+++ b/pkg/services/baremetal/host/host.go
@@ -93,18 +93,6 @@ func (s *Service) Reconcile(ctx context.Context) (result reconcile.Result, err e
 
 	hostStateMachine := newHostStateMachine(s.scope.HetznerBareMetalHost, s, s.scope.Logger)
 
-	defer func() {
-		// remove deprecated conditions
-		conditions.Delete(s.scope.HetznerBareMetalHost, infrav1.HetznerBareMetalHostReadyCondition)
-
-		conditions.SetSummary(s.scope.HetznerBareMetalHost)
-
-		// save host if it changed during reconciliation
-		if !reflect.DeepEqual(oldHost, *s.scope.HetznerBareMetalHost) {
-			result, err = SaveHostAndReturn(ctx, s.scope.Client, s.scope.HetznerBareMetalHost)
-		}
-	}()
-
 	// reconcile state
 	actResult := hostStateMachine.ReconcileState()
 	result, err = actResult.Result()
@@ -112,6 +100,16 @@ func (s *Service) Reconcile(ctx context.Context) (result reconcile.Result, err e
 		return reconcile.Result{Requeue: true}, fmt.Errorf("action %q failed: %w", initialState, err)
 	}
 
+	// remove deprecated conditions
+	conditions.Delete(s.scope.HetznerBareMetalHost, infrav1.HetznerBareMetalHostReadyCondition)
+
+	conditions.SetSummary(s.scope.HetznerBareMetalHost)
+
+	// save host if it changed during reconciliation
+	if !reflect.DeepEqual(oldHost, *s.scope.HetznerBareMetalHost) {
+		return SaveHostAndReturn(ctx, s.scope.Client, s.scope.HetznerBareMetalHost)
+	}
+
 	return result, nil
 }
 


Test patch:
diff --git a/controllers/hcloudmachinetemplate_controller_test.go b/controllers/hcloudmachinetemplate_controller_test.go
index d383942b..ec808aa7 100644
--- a/controllers/hcloudmachinetemplate_controller_test.go
+++ b/controllers/hcloudmachinetemplate_controller_test.go
@@ -33,16 +33,10 @@ import (
 
 var _ = Describe("HCloudMachineTemplateReconciler", func() {
 	var (
-		capiCluster *clusterv1.Cluster
-
-		hetznerCluster  *infrav1.HetznerCluster
 		machineTemplate *infrav1.HCloudMachineTemplate
-
-		testNs *corev1.Namespace
-
-		hetznerSecret *corev1.Secret
-
-		key client.ObjectKey
+		testNs          *corev1.Namespace
+		hetznerSecret   *corev1.Secret
+		key             client.ObjectKey
 	)
 
 	BeforeEach(func() {
@@ -50,105 +44,208 @@ var _ = Describe("HCloudMachineTemplateReconciler", func() {
 		testNs, err = testEnv.CreateNamespace(ctx, "hcloudmachinetemplate-reconciler")
 		Expect(err).NotTo(HaveOccurred())
 
-		capiCluster = &clusterv1.Cluster{
-			ObjectMeta: metav1.ObjectMeta{
-				GenerateName: "test-",
-				Namespace:    testNs.Name,
-				Finalizers:   []string{clusterv1.ClusterFinalizer},
-			},
-			Spec: clusterv1.ClusterSpec{
-				InfrastructureRef: &corev1.ObjectReference{
-					APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
-					Kind:       "HetznerCluster",
-					Name:       "hetzner-test",
-					Namespace:  testNs.Name,
-				},
-			},
-		}
-
-		Expect(testEnv.Create(ctx, capiCluster)).To(Succeed())
-
-		hetznerCluster = &infrav1.HetznerCluster{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "hetzner-test",
-				Namespace: testNs.Name,
-				OwnerReferences: []metav1.OwnerReference{
-					{
-						APIVersion: "cluster.x-k8s.io/v1beta1",
-						Kind:       "Cluster",
-						Name:       capiCluster.Name,
-						UID:        capiCluster.UID,
-					},
-				},
-			},
-			Spec: getDefaultHetznerClusterSpec(),
-		}
-
-		Expect(testEnv.Create(ctx, hetznerCluster)).To(Succeed())
-
-		machineTemplate = &infrav1.HCloudMachineTemplate{
-			ObjectMeta: metav1.ObjectMeta{
-				GenerateName:    "hcloud-machine-template-",
-				Namespace:       testNs.Name,
-				OwnerReferences: hetznerCluster.OwnerReferences,
-			},
-			Spec: infrav1.HCloudMachineTemplateSpec{
-				Template: infrav1.HCloudMachineTemplateResource{
-					Spec: infrav1.HCloudMachineSpec{
-						ImageName:          "fedora-control-plane",
-						Type:               "cpx31",
-						PlacementGroupName: &defaultPlacementGroupName,
-					},
-				},
-			},
-		}
-		Expect(testEnv.Create(ctx, machineTemplate)).To(Succeed())
-
 		hetznerSecret = getDefaultHetznerSecret(testNs.Name)
 		Expect(testEnv.Create(ctx, hetznerSecret)).To(Succeed())
 
-		key = client.ObjectKey{Namespace: testNs.Name, Name: machineTemplate.Name}
+		key = client.ObjectKey{Namespace: testNs.Name, Name: "hcloud-machine-template"}
 	})
 
 	AfterEach(func() {
-		Expect(testEnv.Cleanup(ctx, testNs, capiCluster, hetznerCluster,
-			machineTemplate, hetznerSecret)).To(Succeed())
+		Expect(testEnv.Cleanup(ctx, testNs, hetznerSecret)).To(Succeed())
 	})
 
 	Context("Basic test", func() {
-		It("sets the capacity in status", func() {
-			Eventually(func() bool {
-				if err := testEnv.Get(ctx, key, machineTemplate); err != nil {
-					testEnv.GetLogger().Error(err, "did not find machine template")
-					return false
+		Context("ClusterClass test", func() {
+			var (
+				capiClusterClass *clusterv1.ClusterClass
+			)
+
+			BeforeEach(func() {
+				capiClusterClass = &clusterv1.ClusterClass{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "test-cluster-class",
+						Namespace: testNs.Name,
+					},
+					Spec: clusterv1.ClusterClassSpec{
+						ControlPlane: clusterv1.ControlPlaneClass{
+							MachineInfrastructure: &clusterv1.LocalObjectTemplate{
+								Ref: &corev1.ObjectReference{
+									APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
+									Kind:       "HCloudMachineTemplate",
+									Name:       "hcloud-machine-template",
+									Namespace:  testNs.Name,
+								},
+							},
+							LocalObjectTemplate: clusterv1.LocalObjectTemplate{
+								Ref: &corev1.ObjectReference{
+									APIVersion: "controlplane.cluster.x-k8s.io/v1beta1",
+									Kind:       "KubeadmControlPlaneTemplate",
+									Name:       "quick-start-control-plane",
+									Namespace:  testNs.Name,
+								},
+							},
+						},
+						Infrastructure: clusterv1.LocalObjectTemplate{
+							Ref: &corev1.ObjectReference{
+								APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
+								Kind:       "HetznerClusterTemplate",
+								Name:       "hcloud-cluster-template",
+								Namespace:  testNs.Name,
+							},
+						},
+					},
 				}
-
-				// If capacity is not set (yet), there is nothing to compare
-				if machineTemplate.Status.Capacity.Cpu() == nil || machineTemplate.Status.Capacity.Memory() == nil {
-					testEnv.GetLogger().Info("capacity not set", "capacity", machineTemplate.Status.Capacity)
-					return false
+				Expect(testEnv.Create(ctx, capiClusterClass)).To(Succeed())
+
+				machineTemplate = &infrav1.HCloudMachineTemplate{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "hcloud-machine-template",
+						Namespace: testNs.Name,
+						OwnerReferences: []metav1.OwnerReference{
+							{
+								APIVersion: "cluster.x-k8s.io/v1beta1",
+								Kind:       "ClusterClass",
+								Name:       capiClusterClass.Name,
+								UID:        capiClusterClass.UID,
+							},
+						},
+					},
+					Spec: infrav1.HCloudMachineTemplateSpec{
+						Template: infrav1.HCloudMachineTemplateResource{
+							Spec: infrav1.HCloudMachineSpec{
+								ImageName:          "fedora-control-plane",
+								Type:               "cpx31",
+								PlacementGroupName: &defaultPlacementGroupName,
+							},
+						},
+					},
 				}
+				Expect(testEnv.Create(ctx, machineTemplate)).To(Succeed())
+			})
+
+			AfterEach(func() {
+				Expect(testEnv.Cleanup(ctx, capiClusterClass, machineTemplate)).To(Succeed())
+			})
+
+			It("checks clusterClass ownership", func() {
+				Eventually(func() bool {
+					if err := testEnv.Get(ctx, key, machineTemplate); err != nil {
+						testEnv.GetLogger().Error(err, "did not find machine template")
+						return false
+					}
+
+					testEnv.GetLogger().Info("found the machine template", "OwnerType", machineTemplate.Status.OwnerType)
+					return machineTemplate.Status.OwnerType == "ClusterClass"
+				}, timeout).Should(BeTrue())
+			})
+		})
 
-				// compare CPU
-				expectedCPU, err := machinetemplate.GetCPUQuantityFromInt(fake.DefaultCPUCores)
-				Expect(err).To(Succeed())
-
-				if !expectedCPU.Equal(*machineTemplate.Status.Capacity.Cpu()) {
-					testEnv.GetLogger().Info("cpu did not equal", "expected", expectedCPU, "actual", machineTemplate.Status.Capacity.Cpu())
-					return false
+		Context("Cluster test", func() {
+			var (
+				capiCluster    *clusterv1.Cluster
+				hetznerCluster *infrav1.HetznerCluster
+			)
+
+			BeforeEach(func() {
+				capiCluster = &clusterv1.Cluster{
+					ObjectMeta: metav1.ObjectMeta{
+						GenerateName: "test-",
+						Namespace:    testNs.Name,
+						Finalizers:   []string{clusterv1.ClusterFinalizer},
+					},
+					Spec: clusterv1.ClusterSpec{
+						InfrastructureRef: &corev1.ObjectReference{
+							APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
+							Kind:       "HetznerCluster",
+							Name:       "hetzner-test",
+							Namespace:  testNs.Name,
+						},
+					},
 				}
-
-				// compare memory
-				expectedMemory, err := machinetemplate.GetMemoryQuantityFromFloat32(fake.DefaultMemoryInGB)
-				Expect(err).To(Succeed())
-
-				if !expectedMemory.Equal(*machineTemplate.Status.Capacity.Memory()) {
-					testEnv.GetLogger().Info("memory did not equal", "expected", expectedMemory, "actual", machineTemplate.Status.Capacity.Memory())
-					return false
+				Expect(testEnv.Create(ctx, capiCluster)).To(Succeed())
+
+				hetznerCluster = &infrav1.HetznerCluster{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "hetzner-test",
+						Namespace: testNs.Name,
+						OwnerReferences: []metav1.OwnerReference{
+							{
+								APIVersion: "cluster.x-k8s.io/v1beta1",
+								Kind:       "Cluster",
+								Name:       capiCluster.Name,
+								UID:        capiCluster.UID,
+							},
+						},
+					},
+					Spec: getDefaultHetznerClusterSpec(),
 				}
+				Expect(testEnv.Create(ctx, hetznerCluster)).To(Succeed())
 
-				return true
-			}, timeout, time.Second).Should(BeTrue())
+				machineTemplate = &infrav1.HCloudMachineTemplate{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:            "hcloud-machine-template",
+						Namespace:       testNs.Name,
+						OwnerReferences: hetznerCluster.OwnerReferences,
+					},
+					Spec: infrav1.HCloudMachineTemplateSpec{
+						Template: infrav1.HCloudMachineTemplateResource{
+							Spec: infrav1.HCloudMachineSpec{
+								ImageName:          "fedora-control-plane",
+								Type:               "cpx31",
+								PlacementGroupName: &defaultPlacementGroupName,
+							},
+						},
+					},
+				}
+				Expect(testEnv.Create(ctx, machineTemplate)).To(Succeed())
+			})
+
+			It("checks the Cluster ownership", func() {
+				Eventually(func() bool {
+					if err := testEnv.Get(ctx, key, machineTemplate); err != nil {
+						testEnv.GetLogger().Error(err, "did not find machine template")
+						return false
+					}
+
+					testEnv.GetLogger().Info("found the machine template", "OwnerType", machineTemplate.Status.OwnerType)
+					return machineTemplate.Status.OwnerType == "Cluster"
+				}, timeout).Should(BeTrue())
+			})
+
+			It("sets the capacity in status", func() {
+				Eventually(func() bool {
+					if err := testEnv.Get(ctx, key, machineTemplate); err != nil {
+						testEnv.GetLogger().Error(err, "did not find machine template")
+						return false
+					}
+
+					// If capacity is not set (yet), there is nothing to compare
+					if machineTemplate.Status.Capacity.Cpu() == nil || machineTemplate.Status.Capacity.Memory() == nil {
+						testEnv.GetLogger().Info("capacity not set", "capacity", machineTemplate.Status.Capacity)
+						return false
+					}
+
+					// compare CPU
+					expectedCPU, err := machinetemplate.GetCPUQuantityFromInt(fake.DefaultCPUCores)
+					Expect(err).To(Succeed())
+
+					if !expectedCPU.Equal(*machineTemplate.Status.Capacity.Cpu()) {
+						testEnv.GetLogger().Info("cpu did not equal", "expected", expectedCPU, "actual", machineTemplate.Status.Capacity.Cpu())
+						return false
+					}
+
+					// compare memory
+					expectedMemory, err := machinetemplate.GetMemoryQuantityFromFloat32(fake.DefaultMemoryInGB)
+					Expect(err).To(Succeed())
+
+					if !expectedMemory.Equal(*machineTemplate.Status.Capacity.Memory()) {
+						testEnv.GetLogger().Info("memory did not equal", "expected", expectedMemory, "actual", machineTemplate.Status.Capacity.Memory())
+						return false
+					}
+
+					return true
+				}, timeout, time.Second).Should(BeTrue())
+			})
 		})
 	})
 })