diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index 60cd9a2dff4..667bc00605b 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -153,6 +153,8 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu protected String kubernetesClusterNodeNamePrefix; + private static final int MAX_CLUSTER_PREFIX_LENGTH = 43; + protected KubernetesClusterResourceModifierActionWorker(final KubernetesCluster kubernetesCluster, final KubernetesClusterManagerImpl clusterManager) { super(kubernetesCluster, clusterManager); } @@ -772,19 +774,35 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu } } + /** + * Generates a valid name prefix for Kubernetes cluster nodes. + * + *
The prefix must comply with Kubernetes naming constraints: + *
The generated prefix is limited to 43 characters to accommodate the full node naming pattern: + *
{'prefix'}-{'control' | 'node'}-{'11-digit-hash'}
+ *
+ * @return A valid node name prefix, truncated if necessary
+ * @see Kubernetes "Object Names and IDs" documentation
+ */
protected String getKubernetesClusterNodeNamePrefix() {
- String prefix = kubernetesCluster.getName();
- if (!NetUtils.verifyDomainNameLabel(prefix, true)) {
- prefix = prefix.replaceAll("[^a-zA-Z0-9-]", "");
- if (prefix.length() == 0) {
- prefix = kubernetesCluster.getUuid();
- }
- prefix = "k8s-" + prefix;
+ String prefix = kubernetesCluster.getName().toLowerCase();
+
+ if (NetUtils.verifyDomainNameLabel(prefix, true)) {
+ return StringUtils.truncate(prefix, MAX_CLUSTER_PREFIX_LENGTH);
}
- if (prefix.length() > 40) {
- prefix = prefix.substring(0, 40);
+
+ prefix = prefix.replaceAll("[^a-z0-9-]", "");
+ if (prefix.isEmpty()) {
+ prefix = kubernetesCluster.getUuid();
}
- return prefix;
+ return StringUtils.truncate("k8s-" + prefix, MAX_CLUSTER_PREFIX_LENGTH);
}
protected KubernetesClusterVO updateKubernetesClusterEntry(final Long cores, final Long memory, final Long size,
diff --git a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorkerTest.java b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorkerTest.java
new file mode 100644
index 00000000000..c220a3468af
--- /dev/null
+++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorkerTest.java
@@ -0,0 +1,138 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.kubernetes.cluster.actionworkers;
+
+import com.cloud.kubernetes.cluster.KubernetesCluster;
+import com.cloud.kubernetes.cluster.KubernetesClusterManagerImpl;
+import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
+import com.cloud.kubernetes.cluster.dao.KubernetesClusterDetailsDao;
+import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
+import com.cloud.kubernetes.version.dao.KubernetesSupportedVersionDao;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class KubernetesClusterResourceModifierActionWorkerTest {
+ @Mock
+ private KubernetesClusterDao kubernetesClusterDaoMock;
+
+ @Mock
+ private KubernetesClusterDetailsDao kubernetesClusterDetailsDaoMock;
+
+ @Mock
+ private KubernetesClusterVmMapDao kubernetesClusterVmMapDaoMock;
+
+ @Mock
+ private KubernetesSupportedVersionDao kubernetesSupportedVersionDaoMock;
+
+ @Mock
+ private KubernetesClusterManagerImpl kubernetesClusterManagerMock;
+
+ @Mock
+ private KubernetesCluster kubernetesClusterMock;
+
+ private KubernetesClusterResourceModifierActionWorker kubernetesClusterResourceModifierActionWorker;
+
+ @Before
+ public void setUp() {
+ kubernetesClusterManagerMock.kubernetesClusterDao = kubernetesClusterDaoMock;
+ kubernetesClusterManagerMock.kubernetesSupportedVersionDao = kubernetesSupportedVersionDaoMock;
+ kubernetesClusterManagerMock.kubernetesClusterDetailsDao = kubernetesClusterDetailsDaoMock;
+ kubernetesClusterManagerMock.kubernetesClusterVmMapDao = kubernetesClusterVmMapDaoMock;
+
+ kubernetesClusterResourceModifierActionWorker = new KubernetesClusterResourceModifierActionWorker(kubernetesClusterMock, kubernetesClusterManagerMock);
+ }
+
+ @Test
+ public void getKubernetesClusterNodeNamePrefixTestReturnOriginalPrefixWhenNamingAllRequirementsAreMet() {
+ String originalPrefix = "k8s-cluster-01";
+ String expectedPrefix = "k8s-cluster-01";
+
+ Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix);
+ Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix());
+ }
+
+ @Test
+ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldOnlyContainLowerCaseCharacters() {
+ String originalPrefix = "k8s-CLUSTER-01";
+ String expectedPrefix = "k8s-cluster-01";
+
+ Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix);
+ Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix());
+ }
+
+ @Test
+ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldBeTruncatedWhenRequired() {
+ int maxPrefixLength = 43;
+
+ String originalPrefix = "c".repeat(maxPrefixLength + 1);
+ String expectedPrefix = "c".repeat(maxPrefixLength);
+
+ Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix);
+ String normalizedPrefix = kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix();
+ Assert.assertEquals(expectedPrefix, normalizedPrefix);
+ Assert.assertEquals(maxPrefixLength, normalizedPrefix.length());
+ }
+
+ @Test
+ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldBeTruncatedWhenRequiredAndWhenOriginalPrefixIsInvalid() {
+ int maxPrefixLength = 43;
+
+ String originalPrefix = "1!" + "c".repeat(maxPrefixLength);
+ String expectedPrefix = "k8s-1" + "c".repeat(maxPrefixLength - 5);
+
+ Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix);
+ String normalizedPrefix = kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix();
+ Assert.assertEquals(expectedPrefix, normalizedPrefix);
+ Assert.assertEquals(maxPrefixLength, normalizedPrefix.length());
+ }
+
+ @Test
+ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldOnlyIncludeAlphanumericCharactersAndHyphen() {
+ String originalPrefix = "Cluster!@#$%^&*()_+?.-01|<>";
+ String expectedPrefix = "k8s-cluster-01";
+
+ Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix);
+ Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix());
+ }
+
+ @Test
+ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldContainClusterUuidWhenAllCharactersAreInvalid() {
+ String clusterUuid = "2699b547-cb56-4a59-a2c6-331cfb21d2e4";
+ String originalPrefix = "!@#$%^&*()_+?.|<>";
+ String expectedPrefix = "k8s-" + clusterUuid;
+
+ Mockito.when(kubernetesClusterMock.getUuid()).thenReturn(clusterUuid);
+ Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix);
+ Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix());
+ }
+
+ @Test
+ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldNotStartWithADigit() {
+ String originalPrefix = "1 cluster";
+ String expectedPrefix = "k8s-1cluster";
+
+ Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix);
+ Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix());
+ }
+}
diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
index 47c35eaada1..802b84f6a1c 100644
--- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java
+++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
@@ -1055,13 +1055,23 @@ public class NetUtils {
return Integer.toString(portRange[0]) + ":" + Integer.toString(portRange[1]);
}
+ /**
+ * Validates a domain name.
+ *
+ * Domain names must satisfy the following constraints: + *