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 4da80278f54..046121f4cb7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -322,6 +322,7 @@ public class ApiConstants { public static final String NIC_PACKED_VIRTQUEUES_ENABLED = "nicpackedvirtqueuesenabled"; public static final String NEW_START_IP = "newstartip"; public static final String NEW_END_IP = "newendip"; + public static final String KUBERNETES_NODE_VERSION = "kubernetesnodeversion"; public static final String NUM_RETRIES = "numretries"; public static final String OFFER_HA = "offerha"; public static final String OS_DISTRIBUTION = "osdistribution"; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java index a25d25d650f..cef5cdae2f4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java @@ -34,6 +34,11 @@ public class KubernetesUserVmResponse extends UserVmResponse { @Param(description = "If the VM is an etcd node") private boolean isEtcdNode; + @SerializedName(ApiConstants.KUBERNETES_NODE_VERSION) + @Param(description = "Kubernetes version of the node") + private String nodeVersion; + + public void setExternalNode(boolean externalNode) { isExternalNode = externalNode; } @@ -41,4 +46,6 @@ public class KubernetesUserVmResponse extends UserVmResponse { public void setEtcdNode(boolean etcdNode) { isEtcdNode = etcdNode; } + + public void setNodeVersion(String nodeVersion) { this.nodeVersion = nodeVersion;} } diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql index 59e3d8c9e39..3fb0909831a 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql @@ -338,6 +338,7 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_template_i CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','etcd_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the VM is an etcd node"'); CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','external_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node was imported into the Kubernetes cluster"'); CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','manual_upgrade', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node is marked for manual upgrade and excluded from the Kubernetes cluster upgrade operation"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','kubernetes_node_version', 'varchar(40) COMMENT "version of k8s the cluster node is on"'); ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_service_offering_id` FOREIGN KEY `fk_cluster__control_service_offering_id`(`control_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE; ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_service_offering_id` FOREIGN KEY `fk_cluster__worker_service_offering_id`(`worker_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE; 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 b301347e549..7dffa72403e 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 @@ -756,6 +756,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne } kubernetesUserVmResponse.setExternalNode(vmMapVO.isExternalNode()); kubernetesUserVmResponse.setEtcdNode(vmMapVO.isEtcdNode()); + kubernetesUserVmResponse.setNodeVersion(vmMapVO.getNodeVersion()); vmResponses.add(kubernetesUserVmResponse); } } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVmMapVO.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVmMapVO.java index eace7254081..6c45c63e16d 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVmMapVO.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVmMapVO.java @@ -51,6 +51,9 @@ public class KubernetesClusterVmMapVO implements KubernetesClusterVmMap { @Column(name = "manual_upgrade") boolean manualUpgrade; + @Column(name = "kubernetes_node_version") + String nodeVersion; + public KubernetesClusterVmMapVO() { } @@ -116,4 +119,12 @@ public class KubernetesClusterVmMapVO implements KubernetesClusterVmMap { public void setManualUpgrade(boolean manualUpgrade) { this.manualUpgrade = manualUpgrade; } + + public String getNodeVersion() { + return nodeVersion; + } + + public void setNodeVersion(String nodeVersion) { + this.nodeVersion = nodeVersion; + } } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java index b02504560ea..961ae1050ce 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java @@ -363,6 +363,7 @@ public class KubernetesClusterActionWorker { protected KubernetesClusterVmMapVO addKubernetesClusterVm(final long kubernetesClusterId, final long vmId, boolean isControlNode, boolean isExternalNode, boolean isEtcdNode, boolean markForManualUpgrade) { + KubernetesSupportedVersion kubernetesVersion = kubernetesSupportedVersionDao.findById(kubernetesCluster.getKubernetesVersionId()); return Transaction.execute(new TransactionCallback() { @Override public KubernetesClusterVmMapVO doInTransaction(TransactionStatus status) { @@ -370,6 +371,7 @@ public class KubernetesClusterActionWorker { newClusterVmMap.setExternalNode(isExternalNode); newClusterVmMap.setManualUpgrade(markForManualUpgrade); newClusterVmMap.setEtcdNode(isEtcdNode); + newClusterVmMap.setNodeVersion(kubernetesVersion.getSemanticVersion()); kubernetesClusterVmMapDao.persist(newClusterVmMap); return newClusterVmMap; } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterUpgradeWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterUpgradeWorker.java index f80a5f0a16d..d8ef426d3f0 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterUpgradeWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterUpgradeWorker.java @@ -147,7 +147,7 @@ public class KubernetesClusterUpgradeWorker extends KubernetesClusterActionWorke logTransitStateDetachIsoAndThrow(Level.ERROR, String.format("Failed to upgrade Kubernetes cluster : %s, unable to get control Kubernetes node on VM : %s in ready state", kubernetesCluster.getName(), vm.getDisplayName()), kubernetesCluster, clusterVMs, KubernetesCluster.Event.OperationFailed, null); } } - if (!KubernetesClusterUtil.clusterNodeVersionMatches(upgradeVersion.getSemanticVersion(), publicIpAddress, sshPort, getControlNodeLoginUser(), getManagementServerSshPublicKeyFile(), hostName, upgradeTimeoutTime, 15000)) { + if (!KubernetesClusterUtil.clusterNodeVersionMatches(upgradeVersion.getSemanticVersion(), publicIpAddress, sshPort, getControlNodeLoginUser(), getManagementServerSshPublicKeyFile(), hostName, upgradeTimeoutTime, 15000, vm.getId(), kubernetesClusterVmMapDao)) { logTransitStateDetachIsoAndThrow(Level.ERROR, String.format("Failed to upgrade Kubernetes cluster : %s, unable to get Kubernetes node on VM : %s upgraded to version %s", kubernetesCluster.getName(), vm.getDisplayName(), upgradeVersion.getSemanticVersion()), kubernetesCluster, clusterVMs, KubernetesCluster.Event.OperationFailed, null); } if (logger.isInfoEnabled()) { diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java index 3ed8b3eb68a..4b4e3f40b5a 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java @@ -31,6 +31,8 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; +import com.cloud.kubernetes.cluster.KubernetesClusterVmMapVO; +import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao; import org.apache.cloudstack.utils.security.SSLUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; @@ -333,7 +335,7 @@ public class KubernetesClusterUtil { final String ipAddress, final int port, final String user, final File sshKeyFile, final String hostName, - final long timeoutTime, final long waitDuration) { + final long timeoutTime, final long waitDuration, final long vmId, KubernetesClusterVmMapDao vmMapDao) { int retry = 10; while (System.currentTimeMillis() < timeoutTime && retry-- > 0) { if (LOGGER.isDebugEnabled()) { @@ -345,7 +347,10 @@ public class KubernetesClusterUtil { user, sshKeyFile, null, String.format(CLUSTER_NODE_VERSION_COMMAND, hostName.toLowerCase()), 10000, 10000, 20000); - if (clusterNodeVersionMatches(result, version)) { + Pair clusterVersionMatchesAndValue = clusterNodeVersionMatches(result, version); + if (Boolean.TRUE.equals(clusterVersionMatchesAndValue.first())) { + KubernetesClusterVmMapVO vmMapVO = vmMapDao.findById(vmId); + vmMapVO.setNodeVersion(clusterVersionMatchesAndValue.second()); return true; } } catch (Exception e) { @@ -362,11 +367,11 @@ public class KubernetesClusterUtil { return false; } - protected static boolean clusterNodeVersionMatches(final Pair result, final String version) { + protected static Pair clusterNodeVersionMatches(final Pair result, final String version) { if (result == null || Boolean.FALSE.equals(result.first()) || StringUtils.isBlank(result.second())) { - return false; + return new Pair<>(false, null); } String response = result.second(); - return response.contains(String.format("v%s", version)); + return new Pair<>(response.contains(String.format("v%s", version)), response); } } diff --git a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtilTest.java b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtilTest.java index 31363dbd1a1..329f9b0e42a 100644 --- a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtilTest.java +++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtilTest.java @@ -27,14 +27,14 @@ public class KubernetesClusterUtilTest { private void executeThrowAndTestVersionMatch() { Pair resultPair = null; - boolean result = KubernetesClusterUtil.clusterNodeVersionMatches(resultPair, "1.24.0"); - Assert.assertFalse(result); + Pair result = KubernetesClusterUtil.clusterNodeVersionMatches(resultPair, "1.24.0"); + Assert.assertFalse(result.first()); } private void executeAndTestVersionMatch(boolean status, String response, boolean expectedResult) { Pair resultPair = new Pair<>(status, response); - boolean result = KubernetesClusterUtil.clusterNodeVersionMatches(resultPair, "1.24.0"); - Assert.assertEquals(expectedResult, result); + Pair result = KubernetesClusterUtil.clusterNodeVersionMatches(resultPair, "1.24.0"); + Assert.assertEquals(expectedResult, result.first()); } @Test diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 65b44209b38..76ca32b6999 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1458,6 +1458,7 @@ "label.no.items": "No available Items", "label.no.matching.offering": "No matching offering found", "label.no.matching.network": "No matching Networks found", +"label.node.version": "Node version", "label.noderootdisksize": "Node root disk size (in GB)", "label.nodiskcache": "No disk cache", "label.none": "None", diff --git a/ui/src/views/compute/KubernetesServiceTab.vue b/ui/src/views/compute/KubernetesServiceTab.vue index 8e6954c89c9..25e8699c570 100644 --- a/ui/src/views/compute/KubernetesServiceTab.vue +++ b/ui/src/views/compute/KubernetesServiceTab.vue @@ -117,6 +117,9 @@ {{ cksSshStartingPort + index }} {{ parseInt(etcdSshPort) + parseInt(getEtcdIndex(record.name)) }} +