Repository: hetznercloud/hcloud-cloud-controller-manager
Pull request #764 (2024-10-22 13:12:56)

feat(instance): add label to distinguish servers from Cloud and Robot
This update to the HCCM introduces a new label for Kubernetes nodes: `instance.hetzner.cloud/provided-by`. This label will be automatically applied to nodes, indicating their origin as either:
- `robot` for dedicated servers
- `cloud` for cloud-based virtual machines

With this features other components (e.g. csi-driver) can fine-tune their deployment requirements.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/hetznercloud/hcloud-cloud-controller-manager/pull/764?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 73.00%. Comparing base [(`9a6cfbb`)](https://app.codecov.io/gh/hetznercloud/hcloud-cloud-controller-manager/commit/9a6cfbb88e9174d6e54d3512bec54b0a0dda22fa?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud) to head [(`abda7f9`)](https://app.codecov.io/gh/hetznercloud/hcloud-cloud-controller-manager/commit/abda7f965620c801a4b8d1db588dfec5cc5f920e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #764      +/-   ##
==========================================
+ Coverage   72.00%   73.00%   +1.00%     
==========================================
  Files          31        1      -30     
  Lines        2650      226    -2424     
==========================================
- Hits         1908      165    -1743     
+ Misses        553       45     -508     
+ Partials      189       16     -173     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/hetznercloud/hcloud-cloud-controller-manager/pull/764?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud).

jooola: The title of the PR is confusing, in our context, a server type might be for example `cx22`. 

Patch:
diff --git a/README.md b/README.md
index bd563cef..f973f258 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ metadata:
     node.kubernetes.io/instance-type: cx22
     topology.kubernetes.io/region: fsn1
     topology.kubernetes.io/zone: fsn1-dc8
+    instance.hetzner.cloud/provided-by: cloud
   name: node
 spec:
   podCIDR: 10.244.0.0/24
diff --git a/docs/robot.md b/docs/robot.md
index 8c7385b4..76dc84f0 100644
--- a/docs/robot.md
+++ b/docs/robot.md
@@ -47,6 +47,9 @@ The Node controller adds information about the server to the Node object. The va
   - `topology.kubernetes.io/zone`
     - Examples: `hel1-dc5` `fsn1-dc16`
     - We use the lowercase variant of the location to match the Cloud Datacenters
+  - `instance.hetzner.cloud/provided-by`
+    - Examples: `robot` `cloud`
+    - We detect if the node is a Robot server or Cloud VM and set the label accordingly
 - Provider ID
   - We set the field `Node.spec.providerID` to identify the Robot server after the initial adoption.
   - The format is `hrobot://$SERVER_NUMBER`, but we can also read from the deprecated format used by [syself/hetzner-cloud-controller-manager](https://github.com/syself/hetzner-cloud-controller-manager): `hcloud://bm-$SERVER_NUMBER`
diff --git a/hcloud/instances.go b/hcloud/instances.go
index e43d4564..fa32023d 100644
--- a/hcloud/instances.go
+++ b/hcloud/instances.go
@@ -33,6 +33,10 @@ import (
 	"github.com/hetznercloud/hcloud-go/v2/hcloud"
 )
 
+const (
+	ProvidedBy = "instance.hetzner.cloud/provided-by"
+)
+
 type instances struct {
 	client        *hcloud.Client
 	robotClient   robot.Client
@@ -273,6 +277,9 @@ func (s hcloudServer) Metadata(addressFamily config.AddressFamily, networkID int
 		NodeAddresses: hcloudNodeAddresses(addressFamily, networkID, s.Server),
 		Zone:          s.Datacenter.Name,
 		Region:        s.Datacenter.Location.Name,
+		AdditionalLabels: map[string]string{
+			ProvidedBy: "cloud",
+		},
 	}, nil
 }
 
@@ -299,5 +306,8 @@ func (s robotServer) Metadata(addressFamily config.AddressFamily, _ int64) (*clo
 		NodeAddresses: robotNodeAddresses(addressFamily, s.Server),
 		Zone:          getZoneOfRobotServer(s.Server),
 		Region:        getRegionOfRobotServer(s.Server),
+		AdditionalLabels: map[string]string{
+			ProvidedBy: "robot",
+		},
 	}, nil
 }


Test patch:
diff --git a/hcloud/instances_test.go b/hcloud/instances_test.go
index b49fe145..ec4d12b9 100644
--- a/hcloud/instances_test.go
+++ b/hcloud/instances_test.go
@@ -361,6 +361,9 @@ func TestInstances_InstanceMetadata(t *testing.T) {
 		},
 		Zone:   "Test DC",
 		Region: "Test Location",
+		AdditionalLabels: map[string]string{
+			"instance.hetzner.cloud/provided-by": "cloud",
+		},
 	}
 
 	if !reflect.DeepEqual(metadata, expectedMetadata) {
@@ -405,6 +408,9 @@ func TestInstances_InstanceMetadataRobotServer(t *testing.T) {
 		},
 		Zone:   "nbg1-dc1",
 		Region: "nbg1",
+		AdditionalLabels: map[string]string{
+			"instance.hetzner.cloud/provided-by": "robot",
+		},
 	}
 
 	if !reflect.DeepEqual(metadata, expectedMetadata) {
diff --git a/tests/e2e/cloud_test.go b/tests/e2e/cloud_test.go
index d62c22f1..04eb8caa 100644
--- a/tests/e2e/cloud_test.go
+++ b/tests/e2e/cloud_test.go
@@ -35,12 +35,13 @@ func TestNodeSetCorrectNodeLabelsAndIPAddresses(t *testing.T) {
 
 	labels := node.Labels
 	expectedLabels := map[string]string{
-		"node.kubernetes.io/instance-type": server.ServerType.Name,
-		"topology.kubernetes.io/region":    server.Datacenter.Location.Name,
-		"topology.kubernetes.io/zone":      server.Datacenter.Name,
-		"kubernetes.io/hostname":           server.Name,
-		"kubernetes.io/os":                 "linux",
-		"kubernetes.io/arch":               "amd64",
+		"node.kubernetes.io/instance-type":   server.ServerType.Name,
+		"topology.kubernetes.io/region":      server.Datacenter.Location.Name,
+		"topology.kubernetes.io/zone":        server.Datacenter.Name,
+		"kubernetes.io/hostname":             server.Name,
+		"kubernetes.io/os":                   "linux",
+		"kubernetes.io/arch":                 "amd64",
+		"instance.hetzner.cloud/provided-by": "cloud",
 	}
 	for expectedLabel, expectedValue := range expectedLabels {
 		if labelValue, ok := labels[expectedLabel]; !ok || labelValue != expectedValue {
diff --git a/tests/e2e/robot_test.go b/tests/e2e/robot_test.go
index cb5a1578..200169e3 100644
--- a/tests/e2e/robot_test.go
+++ b/tests/e2e/robot_test.go
@@ -41,9 +41,10 @@ func TestNodeSetCorrectNodeLabelsAndIPAddressesRobot(t *testing.T) {
 
 	labels := node.Labels
 	expectedLabels := map[string]string{
-		"kubernetes.io/hostname": server.Name,
-		"kubernetes.io/os":       "linux",
-		"kubernetes.io/arch":     "amd64",
+		"kubernetes.io/hostname":             server.Name,
+		"kubernetes.io/os":                   "linux",
+		"kubernetes.io/arch":                 "amd64",
+		"instance.hetzner.cloud/provided-by": "robot",
 	}
 	for expectedLabel, expectedValue := range expectedLabels {
 		assert.Equal(t, expectedValue, labels[expectedLabel], "node does not have expected label %s", expectedLabel)