Skip to content

draft: Validate naming of Kubernetes clusters #10778

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: 4.19
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -746,9 +746,7 @@
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);

Check warning on line 749 in plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java#L749

Added line #L749 was not covered by tests

if (controlNodeCount < 1) {
throw new InvalidParameterValueException("Invalid cluster control nodes count: " + controlNodeCount);
Expand Down Expand Up @@ -838,6 +836,34 @@
}
}

/**
* Checks whether Kubernetes cluster name complies with the Kubernetes naming convention; throws an {@link InvalidParameterValueException} otherwise.
* @see <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/names/"> Kubernetes documentation </a>
*
* @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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -773,18 +773,7 @@
}

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();

Check warning on line 776 in plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java

View check run for this annotation

Codecov / codecov/patch

plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java#L776

Added line #L776 was not covered by tests
}

protected KubernetesClusterVO updateKubernetesClusterEntry(final Long cores, final Long memory, final Long size,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Loading