From cef6250e5fcaddacef2b2b81436fefc585740375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Fri, 25 Apr 2025 15:23:05 -0300 Subject: [PATCH 1/7] validate naming of k8s clusters --- .../apache/cloudstack/api/ApiConstants.java | 4 ++ .../cluster/KubernetesClusterManagerImpl.java | 32 ++++++++++-- ...esClusterResourceModifierActionWorker.java | 13 +---- .../cluster/CreateKubernetesClusterCmd.java | 2 +- .../KubernetesClusterManagerImplTest.java | 50 +++++++++++++++++++ 5 files changed, 85 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index b2042c116a7f..25201f096774 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1107,6 +1107,10 @@ public class ApiConstants { public static final String NFS_MOUNT_OPTIONS = "nfsmountopts"; + public static final String PARAMETER_DESCRIPTION_KUBERNETES_CLUSTER_NAME = "Kubernetes cluster's name. It must: " + + "contain at most 43 characters; contain only lowercase alphanumeric characters or '-'; " + + "start with a letter; end with an alphanumeric character."; + /** * This enum specifies IO Drivers, each option controls specific policies on I/O. * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0). diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index 9c402f83b031..0e565335874a 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -746,9 +746,7 @@ private void validateManagedKubernetesClusterCreateParameters(final CreateKubern final Long nodeRootDiskSize = cmd.getNodeRootDiskSize(); final String externalLoadBalancerIpAddress = cmd.getExternalLoadBalancerIpAddress(); - if (name == null || name.isEmpty()) { - throw new InvalidParameterValueException("Invalid name for the Kubernetes cluster name: " + name); - } + validateKubernetesClusterName(name); if (controlNodeCount < 1) { throw new InvalidParameterValueException("Invalid cluster control nodes count: " + controlNodeCount); @@ -838,6 +836,34 @@ private void validateManagedKubernetesClusterCreateParameters(final CreateKubern } } + /** + * Checks whether Kubernetes cluster name complies with the Kubernetes naming convention; throws an {@link InvalidParameterValueException} otherwise. + * @see Kubernetes documentation + * + * @param name Kubernetes cluster name to be validated. + * @throws InvalidParameterValueException When Kubernetes cluster name does not comply with Kubernetes naming convention. + */ + protected void validateKubernetesClusterName(String name) { + String baseErrorString = "Unable to create Kubernetes cluster. Reason: "; + if (!name.equals(name.toLowerCase())) { + String errorString = String.format("%s cluster name [%s] needs to be entirely in lowercase.", baseErrorString, name); + LOGGER.debug(errorString); + throw new InvalidParameterValueException(errorString); + } + if (name.length() > 43) { + String reason = "CloudStack appends the VM type and an 11-character hash to the cluster name to generate VM names, which must not exceed 63 characters. Please ensure the cluster name is 43 characters or fewer."; + String errorString = String.format("%s cluster name [%s] needs to contain at most 43 characters. %s", baseErrorString, name, reason); + LOGGER.debug(errorString); + throw new InvalidParameterValueException(errorString); + } + String pattern = "[a-z]($|[a-z\\d-]*[a-z\\d]$)"; + if (!name.matches(pattern)) { + String errorString = String.format("%s cluster name [%s] needs to start with a letter and end with an alphanumeric character, and can contain only '-' aside from alphanumeric characters.", baseErrorString, name); + LOGGER.debug(errorString); + throw new InvalidParameterValueException(errorString); + } + } + private Network getKubernetesClusterNetworkIfMissing(final String clusterName, final DataCenter zone, final Account owner, final int controlNodesCount, final int nodesCount, final String externalLoadBalancerIpAddress, final Long networkId) throws CloudRuntimeException { Network network = null; 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 60cd9a2dff4e..a75a43b7faa2 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 @@ -773,18 +773,7 @@ protected void setupKubernetesClusterVpcTierRules(IpAddress publicIp, Network ne } 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; - } - if (prefix.length() > 40) { - prefix = prefix.substring(0, 40); - } - return prefix; + return kubernetesCluster.getName(); } protected KubernetesClusterVO updateKubernetesClusterEntry(final Long cores, final Long memory, final Long size, diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java index 529088254306..0fcf0df2d227 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java +++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java @@ -67,7 +67,7 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name for the Kubernetes cluster") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = ApiConstants.PARAMETER_DESCRIPTION_KUBERNETES_CLUSTER_NAME) private String name; @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "description for the Kubernetes cluster") diff --git a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java index a6d46ffc9aa1..3d1158e2e722 100644 --- a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java +++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java @@ -292,4 +292,54 @@ public void removeVmsFromCluster() { Mockito.when(kubernetesClusterDao.findById(Mockito.anyLong())).thenReturn(cluster); Assert.assertTrue(kubernetesClusterManager.removeVmsFromCluster(cmd).size() > 0); } + + @Test(expected = InvalidParameterValueException.class) + public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameContainsUpperCaseLetters() { + kubernetesClusterManager.validateKubernetesClusterName("clusterName"); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameExceedsMaxAllowedLength() { + kubernetesClusterManager.validateKubernetesClusterName("c".repeat(44)); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameStartsWithDigit() { + kubernetesClusterManager.validateKubernetesClusterName("1clustername"); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameContainsOneDigit() { + kubernetesClusterManager.validateKubernetesClusterName("1"); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameStartsWithNonAlphanumericCharacter() { + kubernetesClusterManager.validateKubernetesClusterName("-clustername"); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameContainsOneHyphen() { + kubernetesClusterManager.validateKubernetesClusterName("-"); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameEndsWithNonAlphanumericCharacter() { + kubernetesClusterManager.validateKubernetesClusterName("clustername-"); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameContainsNonAlphanumericCharactersOtherThanHyphen() { + kubernetesClusterManager.validateKubernetesClusterName("cluster$name"); + } + + @Test + public void validateKubernetesClusterNameTestValidateClusterNameWhenItCompliesWithTheNamingConvention() { + kubernetesClusterManager.validateKubernetesClusterName("c-" + "c".repeat(41)); + } + + @Test + public void validateKubernetesClusterNameTestValidateClusterNameWithOneCharacter() { + kubernetesClusterManager.validateKubernetesClusterName("k"); + } } From 14f97e9529a402f61d7158f60eabd8a9f77320e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Fri, 23 May 2025 20:43:50 -0300 Subject: [PATCH 2/7] normalize instead of throwing an exception --- .../apache/cloudstack/api/ApiConstants.java | 4 -- .../cluster/KubernetesClusterManagerImpl.java | 32 ++---------- ...esClusterResourceModifierActionWorker.java | 30 ++++++++++- .../cluster/CreateKubernetesClusterCmd.java | 2 +- .../KubernetesClusterManagerImplTest.java | 50 ------------------- .../java/com/cloud/utils/net/NetUtils.java | 22 +++++--- 6 files changed, 49 insertions(+), 91 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 25201f096774..b2042c116a7f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1107,10 +1107,6 @@ public class ApiConstants { public static final String NFS_MOUNT_OPTIONS = "nfsmountopts"; - public static final String PARAMETER_DESCRIPTION_KUBERNETES_CLUSTER_NAME = "Kubernetes cluster's name. It must: " + - "contain at most 43 characters; contain only lowercase alphanumeric characters or '-'; " + - "start with a letter; end with an alphanumeric character."; - /** * This enum specifies IO Drivers, each option controls specific policies on I/O. * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0). diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index 0e565335874a..9c402f83b031 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -746,7 +746,9 @@ private void validateManagedKubernetesClusterCreateParameters(final CreateKubern final Long nodeRootDiskSize = cmd.getNodeRootDiskSize(); final String externalLoadBalancerIpAddress = cmd.getExternalLoadBalancerIpAddress(); - validateKubernetesClusterName(name); + if (name == null || name.isEmpty()) { + throw new InvalidParameterValueException("Invalid name for the Kubernetes cluster name: " + name); + } if (controlNodeCount < 1) { throw new InvalidParameterValueException("Invalid cluster control nodes count: " + controlNodeCount); @@ -836,34 +838,6 @@ private void validateManagedKubernetesClusterCreateParameters(final CreateKubern } } - /** - * Checks whether Kubernetes cluster name complies with the Kubernetes naming convention; throws an {@link InvalidParameterValueException} otherwise. - * @see Kubernetes documentation - * - * @param name Kubernetes cluster name to be validated. - * @throws InvalidParameterValueException When Kubernetes cluster name does not comply with Kubernetes naming convention. - */ - protected void validateKubernetesClusterName(String name) { - String baseErrorString = "Unable to create Kubernetes cluster. Reason: "; - if (!name.equals(name.toLowerCase())) { - String errorString = String.format("%s cluster name [%s] needs to be entirely in lowercase.", baseErrorString, name); - LOGGER.debug(errorString); - throw new InvalidParameterValueException(errorString); - } - if (name.length() > 43) { - String reason = "CloudStack appends the VM type and an 11-character hash to the cluster name to generate VM names, which must not exceed 63 characters. Please ensure the cluster name is 43 characters or fewer."; - String errorString = String.format("%s cluster name [%s] needs to contain at most 43 characters. %s", baseErrorString, name, reason); - LOGGER.debug(errorString); - throw new InvalidParameterValueException(errorString); - } - String pattern = "[a-z]($|[a-z\\d-]*[a-z\\d]$)"; - if (!name.matches(pattern)) { - String errorString = String.format("%s cluster name [%s] needs to start with a letter and end with an alphanumeric character, and can contain only '-' aside from alphanumeric characters.", baseErrorString, name); - LOGGER.debug(errorString); - throw new InvalidParameterValueException(errorString); - } - } - private Network getKubernetesClusterNetworkIfMissing(final String clusterName, final DataCenter zone, final Account owner, final int controlNodesCount, final int nodesCount, final String externalLoadBalancerIpAddress, final Long networkId) throws CloudRuntimeException { Network network = null; 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 a75a43b7faa2..1bf207b90404 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 @@ -772,8 +772,36 @@ protected void setupKubernetesClusterVpcTierRules(IpAddress publicIp, Network ne } } + /** + * 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() { - return kubernetesCluster.getName(); + int maxPrefixLength = 43; + String prefix = kubernetesCluster.getName().toLowerCase(); + + if (NetUtils.verifyDomainNameLabel(prefix, true)) { + return StringUtils.truncate(prefix, maxPrefixLength); + } + + prefix = prefix.replaceAll("[^a-z0-9-]", ""); + if (prefix.isEmpty()) { + prefix = kubernetesCluster.getUuid(); + } + return StringUtils.truncate("k8s-" + prefix, maxPrefixLength); } protected KubernetesClusterVO updateKubernetesClusterEntry(final Long cores, final Long memory, final Long size, diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java index 0fcf0df2d227..529088254306 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java +++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java @@ -67,7 +67,7 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = ApiConstants.PARAMETER_DESCRIPTION_KUBERNETES_CLUSTER_NAME) + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name for the Kubernetes cluster") private String name; @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "description for the Kubernetes cluster") diff --git a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java index 3d1158e2e722..a6d46ffc9aa1 100644 --- a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java +++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java @@ -292,54 +292,4 @@ public void removeVmsFromCluster() { Mockito.when(kubernetesClusterDao.findById(Mockito.anyLong())).thenReturn(cluster); Assert.assertTrue(kubernetesClusterManager.removeVmsFromCluster(cmd).size() > 0); } - - @Test(expected = InvalidParameterValueException.class) - public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameContainsUpperCaseLetters() { - kubernetesClusterManager.validateKubernetesClusterName("clusterName"); - } - - @Test(expected = InvalidParameterValueException.class) - public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameExceedsMaxAllowedLength() { - kubernetesClusterManager.validateKubernetesClusterName("c".repeat(44)); - } - - @Test(expected = InvalidParameterValueException.class) - public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameStartsWithDigit() { - kubernetesClusterManager.validateKubernetesClusterName("1clustername"); - } - - @Test(expected = InvalidParameterValueException.class) - public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameContainsOneDigit() { - kubernetesClusterManager.validateKubernetesClusterName("1"); - } - - @Test(expected = InvalidParameterValueException.class) - public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameStartsWithNonAlphanumericCharacter() { - kubernetesClusterManager.validateKubernetesClusterName("-clustername"); - } - - @Test(expected = InvalidParameterValueException.class) - public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameContainsOneHyphen() { - kubernetesClusterManager.validateKubernetesClusterName("-"); - } - - @Test(expected = InvalidParameterValueException.class) - public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameEndsWithNonAlphanumericCharacter() { - kubernetesClusterManager.validateKubernetesClusterName("clustername-"); - } - - @Test(expected = InvalidParameterValueException.class) - public void validateKubernetesClusterNameTestThrowExceptionWhenClusterNameContainsNonAlphanumericCharactersOtherThanHyphen() { - kubernetesClusterManager.validateKubernetesClusterName("cluster$name"); - } - - @Test - public void validateKubernetesClusterNameTestValidateClusterNameWhenItCompliesWithTheNamingConvention() { - kubernetesClusterManager.validateKubernetesClusterName("c-" + "c".repeat(41)); - } - - @Test - public void validateKubernetesClusterNameTestValidateClusterNameWithOneCharacter() { - kubernetesClusterManager.validateKubernetesClusterName("k"); - } } 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 2703deaad649..0df7481f7dca 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 static String portRangeToString(final int portRange[]) { return Integer.toString(portRange[0]) + ":" + Integer.toString(portRange[1]); } + /** + * Validates a domain name. + * + *

Domain names must satisfy the following constraints: + *

+ * + * @param hostName The domain name to validate + * @param isHostName If true, verifies whether the domain name starts with a digit + * @return true if the domain name is valid, false otherwise + */ public static boolean verifyDomainNameLabel(final String hostName, final boolean isHostName) { - // must be between 1 and 63 characters long and may contain only the ASCII letters 'a' through 'z' (in a - // case-insensitive manner), - // the digits '0' through '9', and the hyphen ('-'). - // Can not start with a hyphen and digit, and must not end with a hyphen - // If it's a host name, don't allow to start with digit - if (hostName.length() > 63 || hostName.length() < 1) { s_logger.warn("Domain name label must be between 1 and 63 characters long"); return false; From 242b334d6225b4c178fe639f0b929329ef165fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Fri, 23 May 2025 21:25:38 -0300 Subject: [PATCH 3/7] add unit tests --- ...usterResourceModifierActionWorkerTest.java | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorkerTest.java 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 000000000000..dad7945612d4 --- /dev/null +++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorkerTest.java @@ -0,0 +1,123 @@ +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); + String normalizedPrefix = kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix(); + Assert.assertEquals(expectedPrefix, normalizedPrefix); + } + + @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); + String normalizedPrefix = kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix(); + Assert.assertEquals(expectedPrefix, normalizedPrefix); + } + + @Test + public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldNotStartWithADigit() { + String originalPrefix = "1 cluster"; + String expectedPrefix = "k8s-1cluster"; + + Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix); + Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix()); + } +} \ No newline at end of file From 1d46a7f06a11b467b1842e91d72450ecb5628b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Fri, 23 May 2025 21:27:21 -0300 Subject: [PATCH 4/7] add end of line --- .../KubernetesClusterResourceModifierActionWorkerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index dad7945612d4..35609a15cb2c 100644 --- 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 @@ -120,4 +120,4 @@ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldNotStart Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix); Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix()); } -} \ No newline at end of file +} From b6f79b719f441dbfc7f6646d4074e351e6dc141e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Fri, 23 May 2025 21:31:52 -0300 Subject: [PATCH 5/7] add license --- ...ClusterResourceModifierActionWorkerTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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 index 35609a15cb2c..3d96680e055d 100644 --- 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 @@ -1,3 +1,20 @@ +// 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; From 4128bc15b6e43e39ab55df683543983d812049a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Tue, 27 May 2025 16:38:48 -0300 Subject: [PATCH 6/7] apply suggestion --- .../KubernetesClusterResourceModifierActionWorker.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 1bf207b90404..667bc00605b9 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); } @@ -790,18 +792,17 @@ protected void setupKubernetesClusterVpcTierRules(IpAddress publicIp, Network ne * @see Kubernetes "Object Names and IDs" documentation */ protected String getKubernetesClusterNodeNamePrefix() { - int maxPrefixLength = 43; String prefix = kubernetesCluster.getName().toLowerCase(); if (NetUtils.verifyDomainNameLabel(prefix, true)) { - return StringUtils.truncate(prefix, maxPrefixLength); + return StringUtils.truncate(prefix, MAX_CLUSTER_PREFIX_LENGTH); } prefix = prefix.replaceAll("[^a-z0-9-]", ""); if (prefix.isEmpty()) { prefix = kubernetesCluster.getUuid(); } - return StringUtils.truncate("k8s-" + prefix, maxPrefixLength); + return StringUtils.truncate("k8s-" + prefix, MAX_CLUSTER_PREFIX_LENGTH); } protected KubernetesClusterVO updateKubernetesClusterEntry(final Long cores, final Long memory, final Long size, From 4fae5619146cf847e42260a9703de9b6d8a22bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Tue, 27 May 2025 16:39:13 -0300 Subject: [PATCH 7/7] remove unecessary variable usage in unit tests --- .../KubernetesClusterResourceModifierActionWorkerTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 index 3d96680e055d..c220a3468afb 100644 --- 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 @@ -113,8 +113,7 @@ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldOnlyIncl String expectedPrefix = "k8s-cluster-01"; Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix); - String normalizedPrefix = kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix(); - Assert.assertEquals(expectedPrefix, normalizedPrefix); + Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix()); } @Test @@ -125,8 +124,7 @@ public void getKubernetesClusterNodeNamePrefixTestNormalizedPrefixShouldContainC Mockito.when(kubernetesClusterMock.getUuid()).thenReturn(clusterUuid); Mockito.when(kubernetesClusterMock.getName()).thenReturn(originalPrefix); - String normalizedPrefix = kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix(); - Assert.assertEquals(expectedPrefix, normalizedPrefix); + Assert.assertEquals(expectedPrefix, kubernetesClusterResourceModifierActionWorker.getKubernetesClusterNodeNamePrefix()); } @Test