Repository: syself/cluster-api-provider-hetzner
Pull request #1463 (2024-08-29 09:12:28)

:seedling: Fix typos in docs.
**What this PR does / why we need it**:

The make target `install-clusterctl` does not exist anymore.



Patch:
diff --git a/docs/caph/01-getting-started/02-quickstart/02-management-cluster-setup.md b/docs/caph/01-getting-started/02-quickstart/02-management-cluster-setup.md
index 481efee3..2358044e 100644
--- a/docs/caph/01-getting-started/02-quickstart/02-management-cluster-setup.md
+++ b/docs/caph/01-getting-started/02-quickstart/02-management-cluster-setup.md
@@ -51,7 +51,6 @@ For production use, a “real” Kubernetes cluster should be used with appropri
 ### Install Clusterctl
 
 To install Clusterctl, refer to the instructions available in the official ClusterAPI documentation [here](https://cluster-api.sigs.k8s.io/user/quick-start.html#install-clusterctl).
-Alternatively, use the `make install-clusterctl` command to do the same.
 
 ### Initialize the management cluster
 
@@ -74,13 +73,9 @@ For a specific version, use the `--infrastructure hetzner:vX.X.X` flag with the
 ## Variable Preparation to generate a cluster-template
 
 ```shell
-export SSH_KEY_NAME="<ssh-key-name>" \
-export CLUSTER_NAME="my-cluster" \
-export HCLOUD_REGION="fsn1" \
-export CONTROL_PLANE_MACHINE_COUNT=3 \
-export WORKER_MACHINE_COUNT=3 \
-export KUBERNETES_VERSION=1.29.4 \
-export HCLOUD_CONTROL_PLANE_MACHINE_TYPE=cpx31 \
+export SSH_KEY_NAME="<ssh-key-name>"
+export HCLOUD_REGION="fsn1"
+export HCLOUD_CONTROL_PLANE_MACHINE_TYPE=cpx31
 export HCLOUD_WORKER_MACHINE_TYPE=cpx31
 ```
 
diff --git a/docs/caph/01-getting-started/03-preparation.md b/docs/caph/01-getting-started/03-preparation.md
index c3918b65..e09ea996 100644
--- a/docs/caph/01-getting-started/03-preparation.md
+++ b/docs/caph/01-getting-started/03-preparation.md
@@ -66,13 +66,13 @@ Or for a specific [version](https://github.com/syself/cluster-api-provider-hetzn
 ## Variable Preparation to generate a cluster-template
 
 ```shell
-export SSH_KEY_NAME="<ssh-key-name>" \
-export CLUSTER_NAME="my-cluster" \
-export HCLOUD_REGION="fsn1" \
-export CONTROL_PLANE_MACHINE_COUNT=3 \
-export WORKER_MACHINE_COUNT=3 \
-export KUBERNETES_VERSION=1.29.4 \
-export HCLOUD_CONTROL_PLANE_MACHINE_TYPE=cpx31 \
+export SSH_KEY_NAME="<ssh-key-name>"
+export CLUSTER_NAME="my-cluster"
+export HCLOUD_REGION="fsn1"
+export CONTROL_PLANE_MACHINE_COUNT=3
+export WORKER_MACHINE_COUNT=3
+export KUBERNETES_VERSION=1.29.4
+export HCLOUD_CONTROL_PLANE_MACHINE_TYPE=cpx31
 export HCLOUD_WORKER_MACHINE_TYPE=cpx31
 ```
 
@@ -120,10 +120,10 @@ The secret name and the tokens can also be customized in the cluster template.
 In order for the provider integration hetzner to communicate with the Hetzner API ([HCloud API](https://docs.hetzner.cloud/) + [Robot API](https://robot.your-server.de/doc/webservice/en.html#preface)), we need to create a secret with the access data. The secret must be in the same namespace as the other CRs.
 
 ```shell
-export HCLOUD_TOKEN="<YOUR-TOKEN>" \
-export HETZNER_ROBOT_USER="<YOUR-ROBOT-USER>" \
-export HETZNER_ROBOT_PASSWORD="<YOUR-ROBOT-PASSWORD>" \
-export HETZNER_SSH_PUB_PATH="<YOUR-SSH-PUBLIC-PATH>" \
+export HCLOUD_TOKEN="<YOUR-TOKEN>"
+export HETZNER_ROBOT_USER="<YOUR-ROBOT-USER>"
+export HETZNER_ROBOT_PASSWORD="<YOUR-ROBOT-PASSWORD>"
+export HETZNER_SSH_PUB_PATH="<YOUR-SSH-PUBLIC-PATH>"
 export HETZNER_SSH_PRIV_PATH="<YOUR-SSH-PRIVATE-PATH>"
 ```
 
diff --git a/docs/caph/02-topics/05-baremetal/02-management-cluster.md b/docs/caph/02-topics/05-baremetal/02-management-cluster.md
index aba6ce47..de5f1d9b 100644
--- a/docs/caph/02-topics/05-baremetal/02-management-cluster.md
+++ b/docs/caph/02-topics/05-baremetal/02-management-cluster.md
@@ -128,11 +128,11 @@ We create two secrets named `hetzner` for Hetzner Cloud and Robot API access and
 The `hetzner` secret contains API token for hcloud token. It also contains username and password that is used to interact with robot API. `robot-ssh` secret contains the public-key, private-key and name of the ssh-key used for baremetal servers.
 
 ```shell
-export HCLOUD_TOKEN="<YOUR-TOKEN>" \
-export HETZNER_ROBOT_USER="<YOUR-ROBOT-USER>" \
-export HETZNER_ROBOT_PASSWORD="<YOUR-ROBOT-PASSWORD>" \
-export SSH_KEY_NAME="<YOUR-SSH-KEY-NAME>" \
-export HETZNER_SSH_PUB_PATH="<YOUR-SSH-PUBLIC-PATH>" \
+export HCLOUD_TOKEN="<YOUR-TOKEN>"
+export HETZNER_ROBOT_USER="<YOUR-ROBOT-USER>"
+export HETZNER_ROBOT_PASSWORD="<YOUR-ROBOT-PASSWORD>"
+export SSH_KEY_NAME="<YOUR-SSH-KEY-NAME>"
+export HETZNER_SSH_PUB_PATH="<YOUR-SSH-PUBLIC-PATH>"
 export HETZNER_SSH_PRIV_PATH="<YOUR-SSH-PRIVATE-PATH>"
 ```
 
diff --git a/pkg/services/hcloud/client/client.go b/pkg/services/hcloud/client/client.go
index fa04d485..777d2ce2 100644
--- a/pkg/services/hcloud/client/client.go
+++ b/pkg/services/hcloud/client/client.go
@@ -40,7 +40,8 @@ var ErrUnauthorized = fmt.Errorf("unauthorized")
 
 // Client collects all methods used by the controller in the hcloud cloud API.
 type Client interface {
-	Close()
+	// Reset resets the local cache. Only implemented in the fake client.
+	Reset()
 
 	CreateLoadBalancer(context.Context, hcloud.LoadBalancerCreateOpts) (*hcloud.LoadBalancer, error)
 	DeleteLoadBalancer(context.Context, int64) error
@@ -78,6 +79,7 @@ type Client interface {
 
 // Factory is the interface for creating new Client objects.
 type Factory interface {
+	// NewClient returns a new Client in the real implementation, and the shared global Client in the fake implementation.
 	NewClient(hcloudToken string) Client
 }
 
@@ -141,8 +143,9 @@ type realClient struct {
 	client *hcloud.Client
 }
 
-// Close implements the Close method of the HCloudClient interface.
-func (c *realClient) Close() {}
+// Reset implements the Reset method of the HCloudClient interface.
+func (c *realClient) Reset() {
+}
 
 func (c *realClient) CreateLoadBalancer(ctx context.Context, opts hcloud.LoadBalancerCreateOpts) (*hcloud.LoadBalancer, error) {
 	res, _, err := c.client.LoadBalancer.Create(ctx, opts)
diff --git a/pkg/services/hcloud/client/fake/hcloud_client.go b/pkg/services/hcloud/client/fake/hcloud_client.go
index 54229b17..5369babe 100644
--- a/pkg/services/hcloud/client/fake/hcloud_client.go
+++ b/pkg/services/hcloud/client/fake/hcloud_client.go
@@ -55,8 +55,8 @@ func (f *cacheHCloudClientFactory) NewClient(string) hcloudclient.Client {
 	return cacheHCloudClientInstance
 }
 
-// Close implements Close method of hcloud client interface.
-func (c *cacheHCloudClient) Close() {
+// Reset implements Reset method of hcloud client interface.
+func (c *cacheHCloudClient) Reset() {
 	c.counterMutex.Lock()
 	defer c.counterMutex.Unlock()
 
diff --git a/pkg/services/hcloud/client/mocks/Client.go b/pkg/services/hcloud/client/mocks/Client.go
index fd561434..4ed39a3f 100644
--- a/pkg/services/hcloud/client/mocks/Client.go
+++ b/pkg/services/hcloud/client/mocks/Client.go
@@ -161,11 +161,6 @@ func (_m *Client) ChangeLoadBalancerType(_a0 context.Context, _a1 *hcloud.LoadBa
 	return r0
 }
 
-// Close provides a mock function with given fields:
-func (_m *Client) Close() {
-	_m.Called()
-}
-
 // CreateLoadBalancer provides a mock function with given fields: _a0, _a1
 func (_m *Client) CreateLoadBalancer(_a0 context.Context, _a1 hcloud.LoadBalancerCreateOpts) (*hcloud.LoadBalancer, error) {
 	ret := _m.Called(_a0, _a1)
@@ -718,6 +713,11 @@ func (_m *Client) RebootServer(_a0 context.Context, _a1 *hcloud.Server) error {
 	return r0
 }
 
+// Reset provides a mock function with given fields:
+func (_m *Client) Reset() {
+	_m.Called()
+}
+
 // ShutdownServer provides a mock function with given fields: _a0, _a1
 func (_m *Client) ShutdownServer(_a0 context.Context, _a1 *hcloud.Server) error {
 	ret := _m.Called(_a0, _a1)
diff --git a/pkg/services/hcloud/server/server.go b/pkg/services/hcloud/server/server.go
index 7911f708..b13067d1 100644
--- a/pkg/services/hcloud/server/server.go
+++ b/pkg/services/hcloud/server/server.go
@@ -431,17 +431,6 @@ func (s *Service) createServer(ctx context.Context) (*hcloud.Server, error) {
 		}
 	}
 
-	if len(sshKeySpecs) == 0 {
-		conditions.MarkFalse(
-			s.scope.HCloudMachine,
-			infrav1.ServerCreateSucceededCondition,
-			infrav1.SSHKeyNotFoundReason,
-			clusterv1.ConditionSeverityError,
-			"No SSH key configured for the server. Please add an SSH key",
-		)
-		return nil, errServerCreateNotPossible
-	}
-
 	// get all ssh keys that are stored in HCloud API
 	sshKeysAPI, err := s.scope.HCloudClient.ListSSHKeys(ctx, hcloud.SSHKeyListOpts{})
 	if err != nil {


Test patch:
diff --git a/controllers/controllers_suite_test.go b/controllers/controllers_suite_test.go
index c3459c15..d59015fc 100644
--- a/controllers/controllers_suite_test.go
+++ b/controllers/controllers_suite_test.go
@@ -45,11 +45,10 @@ const (
 )
 
 var (
-	testEnv      *helpers.TestEnvironment
-	hcloudClient hcloudclient.Client
-	ctx          = ctrl.SetupSignalHandler()
-	wg           sync.WaitGroup
-
+	testEnv                   *helpers.TestEnvironment
+	hcloudClient              hcloudclient.Client
+	ctx                       = ctrl.SetupSignalHandler()
+	wg                        sync.WaitGroup
 	defaultPlacementGroupName = "caph-placement-group"
 	defaultFailureDomain      = "fsn1"
 )
@@ -65,7 +64,6 @@ var _ = BeforeSuite(func() {
 
 	testEnv = helpers.NewTestEnvironment()
 	hcloudClient = testEnv.HCloudClientFactory.NewClient("")
-
 	wg.Add(1)
 
 	Expect((&HetznerClusterReconciler{
diff --git a/controllers/hcloudmachine_controller_test.go b/controllers/hcloudmachine_controller_test.go
index a6450e2f..8943395c 100644
--- a/controllers/hcloudmachine_controller_test.go
+++ b/controllers/hcloudmachine_controller_test.go
@@ -17,9 +17,7 @@ limitations under the License.
 package controllers
 
 import (
-	"fmt"
 	"testing"
-	"time"
 
 	"github.com/hetznercloud/hcloud-go/v2/hcloud"
 	. "github.com/onsi/ginkgo/v2"
@@ -242,6 +240,8 @@ var _ = Describe("HCloudMachineReconciler", func() {
 	)
 
 	BeforeEach(func() {
+		hcloudClient.Reset()
+
 		var err error
 		testNs, err = testEnv.CreateNamespace(ctx, "hcloudmachine-reconciler")
 		Expect(err).NotTo(HaveOccurred())
@@ -365,26 +365,23 @@ var _ = Describe("HCloudMachineReconciler", func() {
 				}, timeout).Should(BeTrue())
 			})
 
-			It("creates the HCloud machine in Hetzner 1 (flaky)", func() {
+			It("creates the HCloud machine in Hetzner 1", func() {
 				By("checking that no servers exist")
 
-				Eventually(func(start time.Time) bool {
+				Eventually(func() bool {
 					servers, err := hcloudClient.ListServers(ctx, hcloud.ServerListOpts{
 						ListOpts: hcloud.ListOpts{
 							LabelSelector: utils.LabelsToLabelSelector(map[string]string{hetznerCluster.ClusterTagKey(): "owned"}),
 						},
 					})
 					if err != nil {
-						fmt.Printf("flaky test. ListServers failed: %s\n", err.Error())
 						return false
 					}
 					if len(servers) != 0 {
-						fmt.Printf("flaky test. There are still servers: %+v\n", servers)
 						return false
 					}
-					fmt.Printf("flaky test. OK after %s\n", time.Since(start).String())
 					return true
-				}, 2*timeout, interval).WithArguments(time.Now()).Should(BeTrue())
+				}, timeout, interval).Should(BeTrue())
 
 				By("checking that bootstrap condition is not ready")
 
@@ -588,25 +585,6 @@ var _ = Describe("HCloudMachineReconciler", func() {
 				}, timeout).Should(BeTrue())
 			})
 		})
-		Context("should show error when ssh key empty", func() {
-			BeforeEach(func() {
-				hetznerCluster.Spec.SSHKeys.HCloud = nil
-				hcloudMachine.Spec.SSHKeys = nil
-				hetznerCluster.Spec.HetznerSecret.Key.SSHKey = ""
-				Expect(testEnv.Create(ctx, hetznerCluster)).To(Succeed())
-				Expect(testEnv.Create(ctx, hcloudMachine)).To(Succeed())
-			})
-
-			AfterEach(func() {
-				Expect(testEnv.Cleanup(ctx, hetznerCluster, hcloudMachine)).To(Succeed())
-			})
-
-			It("should show the expected reason for server not created", func() {
-				Eventually(func() bool {
-					return isPresentAndFalseWithReason(key, hcloudMachine, infrav1.ServerCreateSucceededCondition, infrav1.SSHKeyNotFoundReason)
-				}, timeout).Should(BeTrue())
-			})
-		})
 
 		Context("with public network specs", func() {
 			BeforeEach(func() {
@@ -657,6 +635,7 @@ var _ = Describe("Hetzner secret", func() {
 	)
 
 	BeforeEach(func() {
+		hcloudClient.Reset()
 		var err error
 		testNs, err = testEnv.CreateNamespace(ctx, "hcloudmachine-validation")
 		Expect(err).NotTo(HaveOccurred())
@@ -806,6 +785,8 @@ var _ = Describe("HCloudMachine validation", func() {
 	)
 
 	BeforeEach(func() {
+		hcloudClient.Reset()
+
 		var err error
 		testNs, err = testEnv.CreateNamespace(ctx, "hcloudmachine-validation")
 		Expect(err).NotTo(HaveOccurred())
@@ -863,6 +844,8 @@ var _ = Describe("IgnoreInsignificantHetznerClusterUpdates Predicate", func() {
 	)
 
 	BeforeEach(func() {
+		hcloudClient.Reset()
+
 		predicate = IgnoreInsignificantHetznerClusterUpdates(klog.Background())
 
 		oldCluster = &infrav1.HetznerCluster{
diff --git a/controllers/hcloudmachinetemplate_controller_test.go b/controllers/hcloudmachinetemplate_controller_test.go
index bccbedd8..40122a2e 100644
--- a/controllers/hcloudmachinetemplate_controller_test.go
+++ b/controllers/hcloudmachinetemplate_controller_test.go
@@ -38,6 +38,7 @@ var _ = Describe("HCloudMachineTemplateReconciler", func() {
 	)
 
 	BeforeEach(func() {
+		hcloudClient.Reset()
 		var err error
 		testNs, err = testEnv.CreateNamespace(ctx, "hcloudmachinetemplate-reconciler")
 		Expect(err).NotTo(HaveOccurred())
diff --git a/controllers/hcloudremediation_controller_test.go b/controllers/hcloudremediation_controller_test.go
index c1910e11..6dda568b 100644
--- a/controllers/hcloudremediation_controller_test.go
+++ b/controllers/hcloudremediation_controller_test.go
@@ -54,6 +54,7 @@ var _ = Describe("HCloudRemediationReconciler", func() {
 	)
 
 	BeforeEach(func() {
+		hcloudClient.Reset()
 		var err error
 		testNs, err = testEnv.CreateNamespace(ctx, "hcloudmachinetemplate-reconciler")
 		Expect(err).NotTo(HaveOccurred())
diff --git a/controllers/hetznerbaremetalremediation_controller_test.go b/controllers/hetznerbaremetalremediation_controller_test.go
index 633be58b..7950cb4c 100644
--- a/controllers/hetznerbaremetalremediation_controller_test.go
+++ b/controllers/hetznerbaremetalremediation_controller_test.go
@@ -257,7 +257,7 @@ var _ = Describe("HetznerBareMetalRemediationReconciler", func() {
 					Expect(testEnv.Cleanup(ctx, hetznerBareMetalRemediation, hetznerBaremetalMachine)).To(Succeed())
 				})
 
-				It("should not remediate if no annotations is present in the hetznerBaremetalMachine (flaky)", func() {
+				It("should not remediate if no annotations is present in the hetznerBaremetalMachine", func() {
 					Expect(testEnv.Create(ctx, hetznerBaremetalMachine)).To(Succeed())
 					Expect(testEnv.Create(ctx, hetznerBareMetalRemediation)).To(Succeed())
 
diff --git a/controllers/hetznercluster_controller_test.go b/controllers/hetznercluster_controller_test.go
index 1a1cce88..79f1b7c9 100644
--- a/controllers/hetznercluster_controller_test.go
+++ b/controllers/hetznercluster_controller_test.go
@@ -270,6 +270,7 @@ var _ = Describe("Hetzner ClusterReconciler", func() {
 			hetznerClusterName string
 		)
 		BeforeEach(func() {
+			hcloudClient.Reset()
 			testNs, err = testEnv.CreateNamespace(ctx, "cluster-tests")
 			Expect(err).NotTo(HaveOccurred())
 			namespace = testNs.Name
@@ -735,7 +736,6 @@ var _ = Describe("Hetzner ClusterReconciler", func() {
 
 		Context("HetznerMachines belonging to the cluster", func() {
 			var bootstrapSecret *corev1.Secret
-
 			BeforeEach(func() {
 				bootstrapSecret = getDefaultBootstrapSecret(namespace)
 				Expect(testEnv.Create(ctx, bootstrapSecret)).To(Succeed())
@@ -946,6 +946,7 @@ var _ = Describe("Hetzner secret", func() {
 	)
 
 	BeforeEach(func() {
+		hcloudClient.Reset()
 		var err error
 		testNs, err = testEnv.CreateNamespace(ctx, "hetzner-secret")
 		Expect(err).NotTo(HaveOccurred())
@@ -1043,6 +1044,7 @@ var _ = Describe("HetznerCluster validation", func() {
 		testNs         *corev1.Namespace
 	)
 	BeforeEach(func() {
+		hcloudClient.Reset()
 		var err error
 		testNs, err = testEnv.CreateNamespace(ctx, "hcloudmachine-validation")
 		Expect(err).NotTo(HaveOccurred())
@@ -1128,6 +1130,7 @@ var _ = Describe("HetznerCluster validation", func() {
 var _ = Describe("reconcileRateLimit", func() {
 	var hetznerCluster *infrav1.HetznerCluster
 	BeforeEach(func() {
+		hcloudClient.Reset()
 		hetznerCluster = &infrav1.HetznerCluster{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "rate-limit-cluster",
diff --git a/pkg/services/hcloud/client/fake/hcloud_client_test.go b/pkg/services/hcloud/client/fake/hcloud_client_test.go
index f9e13c15..919bf96a 100644
--- a/pkg/services/hcloud/client/fake/hcloud_client_test.go
+++ b/pkg/services/hcloud/client/fake/hcloud_client_test.go
@@ -81,6 +81,7 @@ var _ = Describe("Load balancer", func() {
 	var network *hcloud.Network
 
 	BeforeEach(func() {
+		client.Reset()
 		_, err := client.CreateLoadBalancer(ctx, opts)
 		Expect(err).To(Succeed())
 
@@ -92,9 +93,6 @@ var _ = Describe("Load balancer", func() {
 		network, err = client.CreateNetwork(ctx, networkOpts)
 		Expect(err).To(Succeed())
 	})
-	AfterEach(func() {
-		client.Close()
-	})
 
 	It("creates a load balancer and returns it with an ID", func() {
 		Expect(lb.ID).ToNot(Equal(0))
@@ -421,18 +419,20 @@ var _ = Describe("Load balancer", func() {
 		err := client.DeleteServiceFromLoadBalancer(ctx, lb, 999)
 		Expect(err).ToNot(Succeed())
 	})
+})
 
-	_ = Describe("Images", func() {
-		var listOpts hcloud.ImageListOpts
-		client := factory.NewClient("")
-		BeforeEach(func() {
-			listOpts.LabelSelector = "caph-image-name==fedora-control-plane"
-		})
-		It("lists at least one image", func() {
-			resp, err := client.ListImages(ctx, listOpts)
-			Expect(err).To(Succeed())
-			Expect(len(resp)).To(BeNumerically(">", 0))
-		})
+var _ = Describe("Images", func() {
+	var listOpts hcloud.ImageListOpts
+	client := factory.NewClient("")
+
+	BeforeEach(func() {
+		client.Reset()
+		listOpts.LabelSelector = "caph-image-name==fedora-control-plane"
+	})
+	It("lists at least one image", func() {
+		resp, err := client.ListImages(ctx, listOpts)
+		Expect(err).To(Succeed())
+		Expect(len(resp)).To(BeNumerically(">", 0))
 	})
 })
 
@@ -463,6 +463,7 @@ var _ = Describe("Server", func() {
 	var network *hcloud.Network
 
 	BeforeEach(func() {
+		client.Reset()
 		var err error
 		server, err = client.CreateServer(ctx, opts)
 		Expect(err).To(Succeed())
@@ -478,9 +479,6 @@ var _ = Describe("Server", func() {
 		})
 		Expect(err).To(Succeed())
 	})
-	AfterEach(func() {
-		client.Close()
-	})
 
 	It("creates a server with an ID", func() {
 		Expect(server.ID).ToNot(Equal(0))
@@ -624,13 +622,11 @@ var _ = Describe("Network", func() {
 	var network *hcloud.Network
 
 	BeforeEach(func() {
+		client.Reset()
 		var err error
 		network, err = client.CreateNetwork(ctx, opts)
 		Expect(err).To(Succeed())
 	})
-	AfterEach(func() {
-		client.Close()
-	})
 
 	It("creates a network with an ID", func() {
 		Expect(network.ID).ToNot(Equal(0))
@@ -676,11 +672,11 @@ var _ = Describe("Placement groups", func() {
 	}
 
 	client := factory.NewClient("")
-
 	var server *hcloud.Server
 	var placementGroup *hcloud.PlacementGroup
 
 	BeforeEach(func() {
+		client.Reset()
 		var err error
 		server, err = client.CreateServer(ctx, hcloud.ServerCreateOpts{
 			Name: "test-server",
@@ -703,9 +699,6 @@ var _ = Describe("Placement groups", func() {
 		placementGroup, err = client.CreatePlacementGroup(ctx, opts)
 		Expect(err).To(Succeed())
 	})
-	AfterEach(func() {
-		client.Close()
-	})
 
 	It("creates a placementGroup with an ID", func() {
 		Expect(placementGroup.ID).ToNot(Equal(0))