diff --git a/managed/devops/replicated.yml b/managed/devops/replicated.yml index a3b1be1a4805..9ddfbca20e2e 100644 --- a/managed/devops/replicated.yml +++ b/managed/devops/replicated.yml @@ -294,7 +294,7 @@ components: releases.path = "/opt/yugabyte/releases" docker.release = "/opt/yugabyte/release" thirdparty.packagePath = /opt/third-party - helm.package = "/opt/yugabyte/helm/yugabyte-latest.tgz" + helm.packagePath = "/opt/yugabyte/helm" health.check_interval_ms = 300000 health.status_interval_ms = 43200000 health.default_email = "YB_ALERTS_EMAIL_REPLACE" diff --git a/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/EditKubernetesUniverse.java b/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/EditKubernetesUniverse.java index 4ae271151a36..7c549e47d831 100644 --- a/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/EditKubernetesUniverse.java +++ b/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/EditKubernetesUniverse.java @@ -286,12 +286,14 @@ public void updateRemainingPods( PlacementInfoUtil.computeMasterAddresses( newPI, newPlacement.masters, taskParams().nodePrefix, provider, masterRpcPort); + String ybSoftwareVersion = taskParams().getPrimaryCluster().userIntent.ybSoftwareVersion; + upgradePodsTask( newPlacement, masterAddresses, currPlacement, serverType, - null, + ybSoftwareVersion, DEFAULT_WAIT_TIME_MS, masterChanged, tserverChanged); diff --git a/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/KubernetesTaskBase.java b/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/KubernetesTaskBase.java index 57c95e9f41b8..d086d035e826 100644 --- a/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/KubernetesTaskBase.java +++ b/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/KubernetesTaskBase.java @@ -64,6 +64,8 @@ public void createPodsTask( ServerType serverType, PlacementInfo activeZones) { + String ybSoftwareVersion = taskParams().getPrimaryCluster().userIntent.ybSoftwareVersion; + boolean edit = currPlacement != null; boolean isMultiAz = masterAddresses != null; @@ -151,7 +153,7 @@ public void createPodsTask( tempPI, azCode, masterAddresses, - null, + ybSoftwareVersion, serverType, config, masterPartition, @@ -189,6 +191,7 @@ public void createPodsTask( tempPI, azCode, masterAddresses, + ybSoftwareVersion, config)); // Add zone to active configs. @@ -324,6 +327,8 @@ public void deletePodsTask( KubernetesPlacement newPlacement, boolean userIntentChange) { + String ybSoftwareVersion = taskParams().getPrimaryCluster().userIntent.ybSoftwareVersion; + boolean edit = newPlacement != null; boolean isMultiAz = masterAddresses != null; @@ -370,7 +375,12 @@ public void deletePodsTask( newPlacement.masters.getOrDefault(azUUID, 0); helmDeletes.addTask( createKubernetesExecutorTask( - CommandType.HELM_UPGRADE, tempPI, azCode, masterAddresses, config)); + CommandType.HELM_UPGRADE, + tempPI, + azCode, + masterAddresses, + ybSoftwareVersion, + config)); podsWait.addTask( createKubernetesCheckPodNumTask( KubernetesCheckNumPod.CommandType.WAIT_FOR_PODS, @@ -472,22 +482,23 @@ public Set getPodsToRemove( // Create Kubernetes Executor task for creating the namespaces and pull secrets. public KubernetesCommandExecutor createKubernetesExecutorTask( KubernetesCommandExecutor.CommandType commandType, String az, Map config) { - return createKubernetesExecutorTask(commandType, null, az, null, config); + return createKubernetesExecutorTask(commandType, null, az, null, null, config); } // Create the Kubernetes Executor task for the helm deployments. (USED) public KubernetesCommandExecutor createKubernetesExecutorTask( - KubernetesCommandExecutor.CommandType commandType, + CommandType commandType, PlacementInfo pi, String az, String masterAddresses, + String ybSoftwareVersion, Map config) { return createKubernetesExecutorTaskForServerType( commandType, pi, az, masterAddresses, - null, + ybSoftwareVersion, ServerType.EITHER, config, 0 /* master partition */, diff --git a/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/UpgradeKubernetesUniverse.java b/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/UpgradeKubernetesUniverse.java index 575e0f4dcb69..608011e978de 100644 --- a/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/UpgradeKubernetesUniverse.java +++ b/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/UpgradeKubernetesUniverse.java @@ -124,14 +124,15 @@ private SubTaskGroupType getTaskSubGroupType() { } private void createUpgradeTask(UserIntent userIntent, Universe universe, PlacementInfo pi) { - String version = null; + String ybSoftwareVersion = null; boolean masterChanged = false; boolean tserverChanged = false; if (taskParams().taskType == UpgradeTaskType.Software) { - version = taskParams().ybSoftwareVersion; + ybSoftwareVersion = taskParams().ybSoftwareVersion; masterChanged = true; tserverChanged = true; } else { + ybSoftwareVersion = userIntent.ybSoftwareVersion; if (!taskParams().masterGFlags.equals(userIntent.masterGFlags)) { masterChanged = true; } @@ -164,7 +165,7 @@ private void createUpgradeTask(UserIntent userIntent, Universe universe, Placeme masterAddresses, null, ServerType.MASTER, - version, + ybSoftwareVersion, taskParams().sleepAfterMasterRestartMillis, masterChanged, tserverChanged); @@ -179,7 +180,7 @@ private void createUpgradeTask(UserIntent userIntent, Universe universe, Placeme masterAddresses, null, ServerType.TSERVER, - version, + ybSoftwareVersion, taskParams().sleepAfterTServerRestartMillis, false /* master change is false since it has already been upgraded.*/, tserverChanged); diff --git a/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/subtasks/KubernetesCommandExecutor.java b/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/subtasks/KubernetesCommandExecutor.java index 4d66cdb66dbc..4cfcd45d3ac7 100644 --- a/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/subtasks/KubernetesCommandExecutor.java +++ b/managed/src/main/java/com/yugabyte/yw/commissioner/tasks/subtasks/KubernetesCommandExecutor.java @@ -174,6 +174,7 @@ public void run() { overridesFile = this.generateHelmOverride(); response = kubernetesManager.helmInstall( + taskParams().ybSoftwareVersion, config, taskParams().providerUUID, taskParams().nodePrefix, @@ -185,7 +186,11 @@ public void run() { overridesFile = this.generateHelmOverride(); response = kubernetesManager.helmUpgrade( - config, taskParams().nodePrefix, taskParams().namespace, overridesFile); + taskParams().ybSoftwareVersion, + config, + taskParams().nodePrefix, + taskParams().namespace, + overridesFile); flag = true; break; case UPDATE_NUM_NODES: diff --git a/managed/src/main/java/com/yugabyte/yw/common/KubernetesManager.java b/managed/src/main/java/com/yugabyte/yw/common/KubernetesManager.java index eb46f46323c3..b32a1f5c1743 100644 --- a/managed/src/main/java/com/yugabyte/yw/common/KubernetesManager.java +++ b/managed/src/main/java/com/yugabyte/yw/common/KubernetesManager.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.ImmutableList; import com.google.inject.Inject; -import com.yugabyte.yw.models.Provider; +import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -16,17 +16,22 @@ @Singleton public class KubernetesManager { - public static final Logger LOG = LoggerFactory.getLogger(KubernetesManager.class); - private static final long DEFAULT_TIMEOUT_SECS = 300; + @Inject ReleaseManager releaseManager; @Inject ShellProcessHandler shellProcessHandler; @Inject play.Configuration appConfig; - private static String SERVICE_INFO_JSONPATH = + public static final Logger LOG = LoggerFactory.getLogger(KubernetesManager.class); + + private static final long DEFAULT_TIMEOUT_SECS = 300; + + private static final String SERVICE_INFO_JSONPATH = "{.spec.clusterIP}|" + "{.status.*.ingress[0].ip}|{.status.*.ingress[0].hostname}"; + private static final String LEGACY_HELM_CHART_FILENAME = "yugabyte-2.7-helm-legacy.tar.gz"; + public ShellResponse createNamespace(Map config, String universePrefix) { List commandList = ImmutableList.of("kubectl", "create", "namespace", universePrefix); return execCommand(config, commandList); @@ -54,17 +59,15 @@ public String getTimeout() { } public ShellResponse helmInstall( + String ybSoftwareVersion, Map config, UUID providerUUID, String universePrefix, String namespace, String overridesFile) { - String helmPackagePath = appConfig.getString("yb.helm.package"); - if (helmPackagePath == null || helmPackagePath.isEmpty()) { - throw new RuntimeException("Helm Package path not provided."); - } - Provider provider = Provider.get(providerUUID); - Map configProvider = provider.getConfig(); + + String helmPackagePath = this.getHelmPackagePath(ybSoftwareVersion); + List commandList = ImmutableList.of( "helm", @@ -180,11 +183,14 @@ public ShellResponse runGetSecret( } public ShellResponse helmUpgrade( - Map config, String universePrefix, String namespace, String overridesFile) { - String helmPackagePath = appConfig.getString("yb.helm.package"); - if (helmPackagePath == null || helmPackagePath.isEmpty()) { - throw new RuntimeException("Helm Package path not provided."); - } + String ybSoftwareVersion, + Map config, + String universePrefix, + String namespace, + String overridesFile) { + + String helmPackagePath = this.getHelmPackagePath(ybSoftwareVersion); + List commandList = ImmutableList.of( "helm", @@ -257,4 +263,38 @@ private ShellResponse execCommand(Map config, List comma String description = String.join(" ", command); return shellProcessHandler.run(command, config, description); } + + public String getHelmPackagePath(String ybSoftwareVersion) { + String helmPackagePath = null; + + // Get helm package filename from release metadata. + ReleaseManager.ReleaseMetadata releaseMetadata = + releaseManager.getReleaseByVersion(ybSoftwareVersion); + if (releaseMetadata != null) { + helmPackagePath = releaseMetadata.chartPath; + } + + if (helmPackagePath == null || helmPackagePath.isEmpty()) { + // TODO: The "legacy" helm chart is included in the yugaware container build to ensure that + // universes deployed using previous versions of the platform (that did not use versioned + // helm charts) will still be usable after upgrading to newer versions of the platform (that + // use versioned helm charts). We can (and should) remove this special case once all customers + // that use the k8s provider have upgraded their platforms and universes to versions > 2.7. + if (Util.compareYbVersions(ybSoftwareVersion, "2.8.0.0") < 0) { + helmPackagePath = + new File(appConfig.getString("yb.helm.packagePath"), LEGACY_HELM_CHART_FILENAME) + .toString(); + } else { + throw new RuntimeException("Helm Package path not found for release: " + ybSoftwareVersion); + } + } + + // Ensure helm package file actually exists. + File helmPackage = new File(helmPackagePath); + if (!helmPackage.exists()) { + throw new RuntimeException("Helm Package file not found: " + helmPackagePath); + } + + return helmPackagePath; + } } diff --git a/managed/src/main/java/com/yugabyte/yw/common/ReleaseManager.java b/managed/src/main/java/com/yugabyte/yw/common/ReleaseManager.java index edf36d8e6ce8..b43d5c2f2c83 100644 --- a/managed/src/main/java/com/yugabyte/yw/common/ReleaseManager.java +++ b/managed/src/main/java/com/yugabyte/yw/common/ReleaseManager.java @@ -55,6 +55,10 @@ public static class ReleaseMetadata { @ApiModelProperty(value = "Release file path") public String filePath; + // File path where the release helm chart is stored + @ApiModelProperty(value = "Helm chart path") + public String chartPath; + // Docker image tag corresponding to the release @ApiModelProperty(value = "Release image tag") public String imageTag; @@ -64,6 +68,7 @@ public static ReleaseMetadata fromLegacy(String version, Object metadata) { // convert those to new format. ReleaseMetadata rm = create(version); rm.filePath = (String) metadata; + rm.chartPath = ""; return rm; } @@ -75,44 +80,76 @@ public static ReleaseMetadata create(String version) { return rm; } + public ReleaseMetadata withFilePath(String filePath) { + this.filePath = filePath; + return this; + } + + public ReleaseMetadata withChartPath(String chartPath) { + this.chartPath = chartPath; + return this; + } + public String toString() { return getClass().getName() + " state: " + String.valueOf(state) + " filePath: " + String.valueOf(filePath) + + " chartPath: " + + String.valueOf(chartPath) + " imageTag: " + String.valueOf(imageTag); } } public static final Logger LOG = LoggerFactory.getLogger(ReleaseManager.class); + final PathMatcher ybPackageMatcher = - FileSystems.getDefault().getPathMatcher("glob:**yugabyte*.tar.gz"); - Predicate packagesFilter = p -> Files.isRegularFile(p) && ybPackageMatcher.matches(p); + FileSystems.getDefault().getPathMatcher("glob:**yugabyte*centos*.tar.gz"); + final Predicate ybPackageFilter = + p -> Files.isRegularFile(p) && ybPackageMatcher.matches(p); + + final PathMatcher ybChartMatcher = + FileSystems.getDefault().getPathMatcher("glob:**yugabyte-*-helm.tar.gz"); + final Predicate ybChartFilter = p -> Files.isRegularFile(p) && ybChartMatcher.matches(p); // This regex needs to support old style packages with -ee as well as new style packages without. // There are previously existing YW deployments that will have the old packages and users will // need to still be able to use said universes and their existing YB releases. final Pattern ybPackagePattern = Pattern.compile("[^.]+yugabyte-(?:ee-)?(.*)-centos(.*).tar.gz"); - public Map getLocalReleases(String localReleasePath) { - Map releaseMap = new HashMap<>(); + final Pattern ybHelmChartPattern = Pattern.compile("[^.]+yugabyte-(.*)-helm.tar.gz"); + public Map getReleaseFiles(String releasesPath, Predicate fileFilter) { + Map fileMap = new HashMap<>(); try { - // Return a map of version# and software package - releaseMap = - Files.walk(Paths.get(localReleasePath)) - .filter(packagesFilter) + fileMap = + Files.walk(Paths.get(releasesPath)) + .filter(fileFilter) .collect( Collectors.toMap( p -> p.getName(p.getNameCount() - 2).toString(), p -> p.toAbsolutePath().toString())); - LOG.debug("Local releases: [ {} ]", releaseMap.toString()); } catch (IOException e) { LOG.error(e.getMessage()); } - return releaseMap; + return fileMap; + } + + public Map getLocalReleases(String releasesPath) { + Map localReleases = new HashMap<>(); + Map releaseFiles = getReleaseFiles(releasesPath, ybPackageFilter); + Map releaseCharts = getReleaseFiles(releasesPath, ybChartFilter); + releaseFiles.forEach( + (version, filePath) -> { + ReleaseMetadata r = + ReleaseMetadata.create(version) + .withFilePath(filePath) + .withChartPath(releaseCharts.getOrDefault(version, "")); + localReleases.put(version, r); + }); + return localReleases; } public Map getReleaseMetadata() { @@ -148,18 +185,19 @@ public void addReleaseWithMetadata(String version, ReleaseMetadata metadata) { public void importLocalReleases() { String ybReleasesPath = appConfig.getString("yb.releases.path"); + String ybReleasePath = appConfig.getString("yb.docker.release"); + String ybHelmChartPath = appConfig.getString("yb.helm.packagePath"); if (ybReleasesPath != null && !ybReleasesPath.isEmpty()) { - moveDockerReleaseFile(ybReleasesPath); - Map localReleases = getLocalReleases(ybReleasesPath); + moveFiles(ybReleasePath, ybReleasesPath, ybPackagePattern); + moveFiles(ybHelmChartPath, ybReleasesPath, ybHelmChartPattern); + Map localReleases = getLocalReleases(ybReleasesPath); Map currentReleases = getReleaseMetadata(); localReleases.keySet().removeAll(currentReleases.keySet()); LOG.debug("Current releases: [ {} ]", currentReleases.keySet().toString()); LOG.debug("Local releases: [ {} ]", localReleases.keySet()); if (!localReleases.isEmpty()) { - LOG.info("Importing local releases: [ {} ]", localReleases.keySet().toString()); - localReleases.forEach( - (version, releaseFile) -> - currentReleases.put(version, ReleaseMetadata.fromLegacy(version, releaseFile))); + LOG.info("Importing local releases: [ {} ]", localReleases.toString()); + localReleases.forEach(currentReleases::put); configHelper.loadConfigToDB(ConfigHelper.ConfigType.SoftwareReleases, currentReleases); } } @@ -174,26 +212,26 @@ public void updateReleaseMetadata(String version, ReleaseMetadata newData) { } /** - * This method would move the yugabyte server package that we bundle with docker image to yb - * releases path. + * This method moves files that match a specific regex to a destination directory. * - * @param ybReleasesPath (str): Yugabyte releases path to create the move the files to. + * @param sourceDir (str): Source directory to move files from + * @param destinationDir (str): Destination directory to move files to + * @param fileRegex (str): Regular expression specifying files to move */ - private void moveDockerReleaseFile(String ybReleasesPath) { - String ybDockerRelease = appConfig.getString("yb.docker.release"); - if (ybDockerRelease == null || ybDockerRelease.isEmpty()) { + private static void moveFiles(String sourceDir, String destinationDir, Pattern fileRegex) { + if (sourceDir == null || sourceDir.isEmpty()) { return; } try { - Files.walk(Paths.get(ybDockerRelease)) + Files.walk(Paths.get(sourceDir)) .map(String::valueOf) - .map(ybPackagePattern::matcher) + .map(fileRegex::matcher) .filter(Matcher::matches) .forEach( match -> { File releaseFile = new File(match.group()); - File destinationFolder = new File(ybReleasesPath, match.group(1)); + File destinationFolder = new File(destinationDir, match.group(1)); File destinationFile = new File(destinationFolder, releaseFile.getName()); if (!destinationFolder.exists()) { destinationFolder.mkdir(); @@ -202,7 +240,7 @@ private void moveDockerReleaseFile(String ybReleasesPath) { Files.move(releaseFile.toPath(), destinationFile.toPath(), REPLACE_EXISTING); } catch (IOException e) { throw new RuntimeException( - "Unable to move docker release file " + "Unable to move release file " + releaseFile.toPath() + " to " + destinationFile); @@ -210,7 +248,7 @@ private void moveDockerReleaseFile(String ybReleasesPath) { }); } catch (IOException e) { LOG.error(e.getMessage()); - throw new RuntimeException("Unable to look up release files in " + ybDockerRelease); + throw new RuntimeException("Unable to look up release files in " + sourceDir); } } diff --git a/managed/src/main/java/com/yugabyte/yw/controllers/handlers/UniverseCRUDHandler.java b/managed/src/main/java/com/yugabyte/yw/controllers/handlers/UniverseCRUDHandler.java index a9408f95ddbf..2c88e6012c3a 100644 --- a/managed/src/main/java/com/yugabyte/yw/controllers/handlers/UniverseCRUDHandler.java +++ b/managed/src/main/java/com/yugabyte/yw/controllers/handlers/UniverseCRUDHandler.java @@ -21,6 +21,7 @@ import com.yugabyte.yw.commissioner.tasks.DestroyUniverse; import com.yugabyte.yw.commissioner.tasks.ReadOnlyClusterDelete; import com.yugabyte.yw.common.CertificateHelper; +import com.yugabyte.yw.common.KubernetesManager; import com.yugabyte.yw.common.PlacementInfoUtil; import com.yugabyte.yw.common.Util; import com.yugabyte.yw.common.YWServiceException; @@ -65,6 +66,8 @@ public class UniverseCRUDHandler { @Inject RuntimeConfigFactory runtimeConfigFactory; + @Inject KubernetesManager kubernetesManager; + /** * Function to Trim keys and values of the passed map. * @@ -151,6 +154,7 @@ public UniverseResp createUniverse(Customer customer, UniverseDefinitionTaskPara } catch (IllegalArgumentException e) { throw new YWServiceException(BAD_REQUEST, e.getMessage()); } + checkHelmChartExists(c.userIntent.ybSoftwareVersion); } // Set the node exporter config based on the provider @@ -372,6 +376,7 @@ private UUID updatePrimaryCluster( if (primaryCluster.userIntent.providerType.equals(Common.CloudType.kubernetes)) { taskType = TaskType.EditKubernetesUniverse; notHelm2LegacyOrBadRequest(u); + checkHelmChartExists(primaryCluster.userIntent.ybSoftwareVersion); } else { mergeNodeExporterInfo(u, taskParams); } @@ -902,6 +907,14 @@ public UUID upgrade(Customer customer, Universe universe, UpgradeParams taskPara + "Manually migrate the deployment to helm3 " + "and then mark the universe as helm 3 compatible."); } + + if (customerTaskType == CustomerTask.TaskType.UpgradeGflags) { + // UpgradeGflags does not change universe version. Check for current version of helm chart. + checkHelmChartExists( + universe.getUniverseDetails().getPrimaryCluster().userIntent.ybSoftwareVersion); + } else { + checkHelmChartExists(taskParams.ybSoftwareVersion); + } } taskParams.rootCA = checkValidRootCA(universe.getUniverseDetails().rootCA); @@ -990,4 +1003,12 @@ public UUID updateDiskSize( universe.name); return taskUUID; } + + private void checkHelmChartExists(String ybSoftwareVersion) { + try { + kubernetesManager.getHelmPackagePath(ybSoftwareVersion); + } catch (RuntimeException e) { + throw new YWServiceException(BAD_REQUEST, e.getMessage()); + } + } } diff --git a/managed/src/main/resources/application.conf b/managed/src/main/resources/application.conf index 44eb1f729909..34f0e4c00a23 100644 --- a/managed/src/main/resources/application.conf +++ b/managed/src/main/resources/application.conf @@ -54,8 +54,8 @@ yb { multiTenant = true releases.path = "/opt/yugabyte/releases" thirdparty.packagePath = /opt/third-party - helm.package = "" - helm.package = ${?HELM_PACKAGE_PATH} + helm.packagePath = "" + helm.packagePath = ${?HELM_PACKAGE_PATH} helm.timeout_secs = 900 # Interval at which to check the status of every universe. Default: 5 minutes. health.check_interval_ms = 300000 diff --git a/managed/src/main/resources/swagger.json b/managed/src/main/resources/swagger.json index d6975bfa2235..d33e1a506f2e 100644 --- a/managed/src/main/resources/swagger.json +++ b/managed/src/main/resources/swagger.json @@ -57,13 +57,13 @@ "type" : "string" }, "groupType" : { - "description" : "Alert definition group type", + "description" : "Alert definition group type.", "enum" : [ "CUSTOMER", "UNIVERSE" ], "readOnly" : true, "type" : "string" }, "groupUuid" : { - "description" : "Alert group Uuid", + "description" : "Alert group Uuid.", "format" : "uuid", "readOnly" : true, "type" : "string" @@ -84,6 +84,30 @@ "readOnly" : true, "type" : "string" }, + "nextNotificationTime" : { + "description" : "Time of the nex notification attempt.", + "format" : "date-time", + "readOnly" : true, + "type" : "string" + }, + "notificationAttemptTime" : { + "description" : "Time of the last notification attempt.", + "format" : "date-time", + "readOnly" : true, + "type" : "string" + }, + "notificationsFailed" : { + "description" : "Count of failures to send a notification.", + "format" : "int32", + "readOnly" : true, + "type" : "integer" + }, + "notifiedState" : { + "description" : "Alert state in last sent notification.", + "enum" : [ "CREATED", "ACTIVE", "ACKNOWLEDGED", "RESOLVED" ], + "readOnly" : true, + "type" : "string" + }, "resolvedTime" : { "description" : "Resolved Date time info.", "format" : "date-time", @@ -2874,6 +2898,10 @@ "Release data" : { "description" : "Release data", "properties" : { + "chartPath" : { + "description" : "Helm chart path", + "type" : "string" + }, "filePath" : { "description" : "Release file path", "type" : "string" @@ -8793,4 +8821,4 @@ }, { "name" : "Users" } ] -} +} \ No newline at end of file diff --git a/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/CreateKubernetesUniverseTest.java b/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/CreateKubernetesUniverseTest.java index fb2f00e60c91..21644dd13bda 100644 --- a/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/CreateKubernetesUniverseTest.java +++ b/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/CreateKubernetesUniverseTest.java @@ -66,6 +66,7 @@ public class CreateKubernetesUniverseTest extends CommissionerBaseTest { YBClient mockClient; String nodePrefix = "demo-universe"; + String ybSoftwareVersion = "1.0.0"; String nodePrefix1, nodePrefix2, nodePrefix3; String ns, ns1, ns2, ns3; @@ -89,7 +90,7 @@ private void setupUniverseMultiAZ( userIntent.masterGFlags = new HashMap<>(); userIntent.tserverGFlags = new HashMap<>(); userIntent.universeName = "demo-universe"; - userIntent.ybSoftwareVersion = "1.0.0"; + userIntent.ybSoftwareVersion = ybSoftwareVersion; userIntent.enableYEDIS = enabledYEDIS; defaultUniverse = createUniverse(defaultCustomer.getCustomerId()); Universe.saveDetails( @@ -160,7 +161,7 @@ private void setupUniverse(boolean setMasters, boolean enabledYEDIS, boolean set userIntent.masterGFlags = new HashMap<>(); userIntent.tserverGFlags = new HashMap<>(); userIntent.universeName = "demo-universe"; - userIntent.ybSoftwareVersion = "1.0.0"; + userIntent.ybSoftwareVersion = ybSoftwareVersion; userIntent.enableYEDIS = enabledYEDIS; defaultUniverse = createUniverse(defaultCustomer.getCustomerId()); Universe.saveDetails( @@ -220,7 +221,7 @@ private void setupUniverse(boolean setMasters, boolean enabledYEDIS, boolean set private void setupCommon() { ShellResponse response = new ShellResponse(); when(mockKubernetesManager.createNamespace(anyMap(), any())).thenReturn(response); - when(mockKubernetesManager.helmInstall(anyMap(), any(), any(), any(), any())) + when(mockKubernetesManager.helmInstall(any(), anyMap(), any(), any(), any(), any())) .thenReturn(response); // Table RPCs. mockClient = mock(YBClient.class); @@ -369,6 +370,7 @@ private void testCreateKubernetesUniverseSuccessMultiAZBase(boolean setNamespace verify(mockKubernetesManager, times(1)) .helmInstall( + eq(ybSoftwareVersion), eq(config1), eq(defaultProvider.uuid), eq(nodePrefix1), @@ -376,6 +378,7 @@ private void testCreateKubernetesUniverseSuccessMultiAZBase(boolean setNamespace expectedOverrideFile.capture()); verify(mockKubernetesManager, times(1)) .helmInstall( + eq(ybSoftwareVersion), eq(config2), eq(defaultProvider.uuid), eq(nodePrefix2), @@ -383,6 +386,7 @@ private void testCreateKubernetesUniverseSuccessMultiAZBase(boolean setNamespace expectedOverrideFile.capture()); verify(mockKubernetesManager, times(1)) .helmInstall( + eq(ybSoftwareVersion), eq(config3), eq(defaultProvider.uuid), eq(nodePrefix3), @@ -435,6 +439,7 @@ private void testCreateKubernetesUniverseSuccessSingleAZBase(boolean setNamespac verify(mockKubernetesManager, times(1)) .helmInstall( + eq(ybSoftwareVersion), eq(config), eq(defaultProvider.uuid), eq(nodePrefix), diff --git a/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/EditKubernetesUniverseTest.java b/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/EditKubernetesUniverseTest.java index d053aebbfa47..4e1ad0d15a02 100644 --- a/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/EditKubernetesUniverseTest.java +++ b/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/EditKubernetesUniverseTest.java @@ -67,13 +67,15 @@ public class EditKubernetesUniverseTest extends CommissionerBaseTest { ShellResponse dummyShellResponse; String nodePrefix = "demo-universe"; + String ybSoftwareVersion = "1.0.0"; Map config = new HashMap(); private void setup() { ShellResponse responseEmpty = new ShellResponse(); ShellResponse responsePod = new ShellResponse(); - when(mockKubernetesManager.helmUpgrade(any(), any(), any(), any())).thenReturn(responseEmpty); + when(mockKubernetesManager.helmUpgrade(any(), any(), any(), any(), any())) + .thenReturn(responseEmpty); responsePod.message = "{\"status\": { \"phase\": \"Running\", \"conditions\": [{\"status\": \"True\"}]}}"; when(mockKubernetesManager.getPodStatus(any(), any(), any())).thenReturn(responsePod); @@ -114,7 +116,7 @@ private void setupUniverseSingleAZ(boolean setMasters) { userIntent.masterGFlags = new HashMap<>(); userIntent.tserverGFlags = new HashMap<>(); userIntent.universeName = "demo-universe"; - userIntent.ybSoftwareVersion = "old-version"; + userIntent.ybSoftwareVersion = ybSoftwareVersion; defaultUniverse = createUniverse(defaultCustomer.getCustomerId()); Universe.saveDetails( defaultUniverse.universeUUID, @@ -281,6 +283,7 @@ public JsonNode parseShellResponseAsJson(ShellResponse response) { public void testAddNode() { setupUniverseSingleAZ(/* Create Masters */ true); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedOverrideFile = ArgumentCaptor.forClass(String.class); @@ -336,6 +339,7 @@ public void testAddNode() { verify(mockKubernetesManager, times(1)) .helmUpgrade( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), @@ -344,6 +348,7 @@ public void testAddNode() { .getPodInfos( expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(nodePrefix, expectedNodePrefix.getValue()); assertEquals(nodePrefix, expectedNamespace.getValue()); @@ -362,6 +367,7 @@ public void testRemoveNode() { setupUniverseSingleAZ(/* Create Masters */ true); ArgumentCaptor expectedUniverseUUID = ArgumentCaptor.forClass(UUID.class); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedOverrideFile = ArgumentCaptor.forClass(String.class); @@ -401,6 +407,7 @@ public void testRemoveNode() { verify(mockKubernetesManager, times(1)) .helmUpgrade( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), @@ -409,6 +416,7 @@ public void testRemoveNode() { .getPodInfos( expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(nodePrefix, expectedNodePrefix.getValue()); assertEquals(nodePrefix, expectedNamespace.getValue()); @@ -429,6 +437,7 @@ public void testRemoveNode() { public void testChangeInstanceType() { setupUniverseSingleAZ(/* Create Masters */ true); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedUniverseUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -476,6 +485,7 @@ public void testChangeInstanceType() { verify(mockKubernetesManager, times(3)) .helmUpgrade( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), @@ -487,6 +497,7 @@ public void testChangeInstanceType() { .getPodInfos( expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(nodePrefix, expectedNodePrefix.getValue()); assertEquals(nodePrefix, expectedNamespace.getValue()); diff --git a/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/UpgradeKubernetesUniverseTest.java b/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/UpgradeKubernetesUniverseTest.java index d1ce9f4dab6a..92a952e61c8e 100644 --- a/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/UpgradeKubernetesUniverseTest.java +++ b/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/UpgradeKubernetesUniverseTest.java @@ -67,6 +67,8 @@ public class UpgradeKubernetesUniverseTest extends CommissionerBaseTest { ShellResponse dummyShellResponse; String nodePrefix = "demo-universe"; + String ybSoftwareVersionOld = "old-version"; + String ybSoftwareVersionNew = "new-version"; Map config = new HashMap(); @@ -77,7 +79,7 @@ private void setupUniverse( userIntent.masterGFlags = new HashMap<>(); userIntent.tserverGFlags = new HashMap<>(); userIntent.universeName = "demo-universe"; - userIntent.ybSoftwareVersion = "old-version"; + userIntent.ybSoftwareVersion = ybSoftwareVersionOld; defaultUniverse = createUniverse(defaultCustomer.getCustomerId()); config.put("KUBECONFIG", "test"); defaultProvider.setConfig(config); @@ -92,7 +94,8 @@ private void setupUniverse( ShellResponse responseEmpty = new ShellResponse(); ShellResponse responsePod = new ShellResponse(); - when(mockKubernetesManager.helmUpgrade(any(), any(), any(), any())).thenReturn(responseEmpty); + when(mockKubernetesManager.helmUpgrade(any(), any(), any(), any(), any())) + .thenReturn(responseEmpty); Master.SysClusterConfigEntryPB.Builder configBuilder = Master.SysClusterConfigEntryPB.newBuilder().setVersion(2); @@ -401,6 +404,7 @@ public void testSoftwareUpgradeSingleAZ() { setupUniverseSingleAZ(/* Create Masters */ false); ArgumentCaptor expectedUniverseUUID = ArgumentCaptor.forClass(UUID.class); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedOverrideFile = ArgumentCaptor.forClass(String.class); @@ -415,6 +419,7 @@ public void testSoftwareUpgradeSingleAZ() { verify(mockKubernetesManager, times(6)) .helmUpgrade( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), @@ -426,6 +431,7 @@ public void testSoftwareUpgradeSingleAZ() { .getPodInfos( expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture()); + assertEquals(ybSoftwareVersionNew, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(nodePrefix, expectedNodePrefix.getValue()); assertEquals(nodePrefix, expectedNamespace.getValue()); @@ -443,6 +449,7 @@ public void testGFlagUpgradeSingleAZ() { setupUniverseSingleAZ(/* Create Masters */ false); ArgumentCaptor expectedUniverseUUID = ArgumentCaptor.forClass(UUID.class); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedOverrideFile = ArgumentCaptor.forClass(String.class); @@ -458,6 +465,7 @@ public void testGFlagUpgradeSingleAZ() { verify(mockKubernetesManager, times(6)) .helmUpgrade( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), @@ -469,6 +477,7 @@ public void testGFlagUpgradeSingleAZ() { .getPodInfos( expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture()); + assertEquals(ybSoftwareVersionOld, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(nodePrefix, expectedNodePrefix.getValue()); assertEquals(nodePrefix, expectedNamespace.getValue()); @@ -486,6 +495,7 @@ public void testSoftwareUpgradeMultiAZ() { setupUniverseMultiAZ(/* Create Masters */ false); ArgumentCaptor expectedUniverseUUID = ArgumentCaptor.forClass(UUID.class); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedOverrideFile = ArgumentCaptor.forClass(String.class); @@ -495,11 +505,12 @@ public void testSoftwareUpgradeMultiAZ() { String overrideFileRegex = "(.*)" + defaultUniverse.universeUUID + "(.*).yml"; UpgradeKubernetesUniverse.Params taskParams = new UpgradeKubernetesUniverse.Params(); - taskParams.ybSoftwareVersion = "new-version"; + taskParams.ybSoftwareVersion = ybSoftwareVersionNew; TaskInfo taskInfo = submitTask(taskParams, UpgradeTaskType.Software); verify(mockKubernetesManager, times(6)) .helmUpgrade( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), @@ -511,6 +522,7 @@ public void testSoftwareUpgradeMultiAZ() { .getPodInfos( expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture()); + assertEquals(ybSoftwareVersionNew, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertTrue(expectedNodePrefix.getValue().contains(nodePrefix)); assertTrue(expectedNamespace.getValue().contains(nodePrefix)); @@ -528,6 +540,7 @@ public void testGFlagUpgradeMultiAZ() { setupUniverseMultiAZ(/* Create Masters */ false); ArgumentCaptor expectedUniverseUUID = ArgumentCaptor.forClass(UUID.class); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedOverrideFile = ArgumentCaptor.forClass(String.class); @@ -543,6 +556,7 @@ public void testGFlagUpgradeMultiAZ() { verify(mockKubernetesManager, times(6)) .helmUpgrade( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), @@ -554,6 +568,7 @@ public void testGFlagUpgradeMultiAZ() { .getPodInfos( expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture()); + assertEquals(ybSoftwareVersionOld, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertTrue(expectedNodePrefix.getValue().contains(nodePrefix)); assertTrue(expectedNamespace.getValue().contains(nodePrefix)); diff --git a/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/subtasks/KubernetesCommandExecutorTest.java b/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/subtasks/KubernetesCommandExecutorTest.java index 66f566f47bac..9b0b0862eb1f 100644 --- a/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/subtasks/KubernetesCommandExecutorTest.java +++ b/managed/src/test/java/com/yugabyte/yw/commissioner/tasks/subtasks/KubernetesCommandExecutorTest.java @@ -155,6 +155,7 @@ private KubernetesCommandExecutor createExecutor( KubernetesCommandExecutor kubernetesCommandExecutor = AbstractTaskBase.createTask(KubernetesCommandExecutor.class); KubernetesCommandExecutor.Params params = new KubernetesCommandExecutor.Params(); + params.ybSoftwareVersion = ybSoftwareVersion; params.providerUUID = defaultProvider.uuid; params.commandType = commandType; params.config = config; @@ -172,6 +173,7 @@ private KubernetesCommandExecutor createExecutor( KubernetesCommandExecutor kubernetesCommandExecutor = AbstractTaskBase.createTask(KubernetesCommandExecutor.class); KubernetesCommandExecutor.Params params = new KubernetesCommandExecutor.Params(); + params.ybSoftwareVersion = ybSoftwareVersion; params.providerUUID = defaultProvider.uuid; params.commandType = commandType; params.nodePrefix = defaultUniverse.getUniverseDetails().nodePrefix; @@ -355,6 +357,7 @@ public void testHelmInstall() throws IOException { kubernetesCommandExecutor.run(); assertEquals(hackPlacementUUID, defaultUniverse.getUniverseDetails().getPrimaryCluster().uuid); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -362,11 +365,13 @@ public void testHelmInstall() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(hackPlacementUUID, defaultUniverse.getUniverseDetails().getPrimaryCluster().uuid); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); @@ -397,6 +402,7 @@ public void testHelmInstallIPV6() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -404,11 +410,13 @@ public void testHelmInstallIPV6() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -437,6 +445,7 @@ public void testHelmInstallWithoutLoadbalancer() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -444,11 +453,13 @@ public void testHelmInstallWithoutLoadbalancer() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -478,6 +489,7 @@ public void testHelmInstallWithGflags() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -485,11 +497,13 @@ public void testHelmInstallWithGflags() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -523,6 +537,7 @@ public void testHelmInstallWithTLS() throws IOException { kubernetesCommandExecutor.taskParams().rootCA = defaultCert.uuid; kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -530,11 +545,13 @@ public void testHelmInstallWithTLS() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -568,6 +585,7 @@ public void testHelmInstallWithTLSNodeToNode() throws IOException { kubernetesCommandExecutor.taskParams().rootCA = defaultCert.uuid; kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -575,11 +593,13 @@ public void testHelmInstallWithTLSNodeToNode() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -613,6 +633,7 @@ public void testHelmInstallWithTLSClientToServer() throws IOException { kubernetesCommandExecutor.taskParams().rootCA = defaultCert.uuid; kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -620,11 +641,13 @@ public void testHelmInstallWithTLSClientToServer() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -647,6 +670,7 @@ private void testHelmInstallForInstanceType(String instanceType) throws IOExcept KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -654,11 +678,13 @@ private void testHelmInstallForInstanceType(String instanceType) throws IOExcept ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -703,6 +729,7 @@ public void testHelmInstallForAnnotations() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -710,11 +737,13 @@ public void testHelmInstallForAnnotations() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -743,6 +772,7 @@ public void testHelmInstallForAnnotationsRegion() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -750,11 +780,13 @@ public void testHelmInstallForAnnotationsRegion() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -782,6 +814,7 @@ public void testHelmInstallForAnnotationsAZ() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -789,11 +822,13 @@ public void testHelmInstallForAnnotationsAZ() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -823,6 +858,7 @@ public void testHelmInstallForAnnotationsPrecendence() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -830,11 +866,13 @@ public void testHelmInstallForAnnotationsPrecendence() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -861,6 +899,7 @@ public void testHelmInstallResourceOverrideMerge() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -868,11 +907,13 @@ public void testHelmInstallResourceOverrideMerge() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -930,6 +971,7 @@ public void testHelmInstallWithStorageClass() throws IOException { KubernetesCommandExecutor.CommandType.HELM_INSTALL, /* set namespace */ true); kubernetesCommandExecutor.run(); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -937,11 +979,13 @@ public void testHelmInstallWithStorageClass() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), expectedNamespace.capture(), expectedOverrideFile.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(defaultUniverse.getUniverseDetails().nodePrefix, expectedNodePrefix.getValue()); @@ -1192,6 +1236,7 @@ public void testHelmInstallLegacy() throws IOException { kubernetesCommandExecutor.run(); assertEquals(hackPlacementUUID, defaultUniverse.getUniverseDetails().getPrimaryCluster().uuid); + ArgumentCaptor expectedYbSoftwareVersion = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedProviderUUID = ArgumentCaptor.forClass(UUID.class); ArgumentCaptor expectedNodePrefix = ArgumentCaptor.forClass(String.class); ArgumentCaptor expectedNamespace = ArgumentCaptor.forClass(String.class); @@ -1199,6 +1244,7 @@ public void testHelmInstallLegacy() throws IOException { ArgumentCaptor expectedConfig = ArgumentCaptor.forClass(HashMap.class); verify(kubernetesManager, times(1)) .helmInstall( + expectedYbSoftwareVersion.capture(), expectedConfig.capture(), expectedProviderUUID.capture(), expectedNodePrefix.capture(), @@ -1207,6 +1253,7 @@ public void testHelmInstallLegacy() throws IOException { verify(kubernetesManager, times(1)) .getServices( expectedConfig.capture(), expectedNodePrefix.capture(), expectedNamespace.capture()); + assertEquals(ybSoftwareVersion, expectedYbSoftwareVersion.getValue()); assertEquals(config, expectedConfig.getValue()); assertEquals(hackPlacementUUID, defaultUniverse.getUniverseDetails().getPrimaryCluster().uuid); assertEquals(defaultProvider.uuid, expectedProviderUUID.getValue()); diff --git a/managed/src/test/java/com/yugabyte/yw/common/KubernetesManagerTest.java b/managed/src/test/java/com/yugabyte/yw/common/KubernetesManagerTest.java index 8494b31de5fb..4d34cf7f913b 100644 --- a/managed/src/test/java/com/yugabyte/yw/common/KubernetesManagerTest.java +++ b/managed/src/test/java/com/yugabyte/yw/common/KubernetesManagerTest.java @@ -2,6 +2,7 @@ package com.yugabyte.yw.common; +import static com.yugabyte.yw.common.TestHelper.createTempFile; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.anyMap; @@ -14,9 +15,13 @@ import com.yugabyte.yw.commissioner.tasks.subtasks.KubernetesCommandExecutor; import com.yugabyte.yw.models.Customer; import com.yugabyte.yw.models.Provider; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import org.apache.commons.io.FileUtils; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,6 +48,8 @@ public class KubernetesManagerTest extends FakeDBApplication { ArgumentCaptor description; Map configProvider = new HashMap(); + static String TMP_CHART_PATH = "/tmp/yugaware_tests/KubernetesManagerTest/charts"; + @Before public void setUp() { defaultCustomer = ModelFactory.testCustomer(); @@ -54,9 +61,20 @@ public void setUp() { command = ArgumentCaptor.forClass(ArrayList.class); config = ArgumentCaptor.forClass(HashMap.class); description = ArgumentCaptor.forClass(String.class); + new File(TMP_CHART_PATH).mkdirs(); + } + + @After + public void tearDown() throws IOException { + FileUtils.deleteDirectory(new File(TMP_CHART_PATH)); } private void runCommand(KubernetesCommandExecutor.CommandType commandType) { + runCommand(commandType, "2.8.0.0-b1"); + } + + private void runCommand( + KubernetesCommandExecutor.CommandType commandType, String ybSoftwareVersion) { ShellResponse response = new ShellResponse(); when(shellProcessHandler.run(anyList(), anyMap(), anyString())).thenReturn(response); @@ -64,6 +82,7 @@ private void runCommand(KubernetesCommandExecutor.CommandType commandType) { switch (commandType) { case HELM_INSTALL: kubernetesManager.helmInstall( + ybSoftwareVersion, configProvider, defaultProvider.uuid, "demo-universe", @@ -72,7 +91,11 @@ private void runCommand(KubernetesCommandExecutor.CommandType commandType) { break; case HELM_UPGRADE: kubernetesManager.helmUpgrade( - configProvider, "demo-universe", "demo-namespace", "/tmp/override.yml"); + ybSoftwareVersion, + configProvider, + "demo-universe", + "demo-namespace", + "/tmp/override.yml"); break; case POD_INFO: kubernetesManager.getPodInfos(configProvider, "demo-universe", "demo-namespace"); @@ -92,7 +115,11 @@ private void runCommand(KubernetesCommandExecutor.CommandType commandType) { @Test public void testHelmUpgrade() { - when(mockAppConfig.getString("yb.helm.package")).thenReturn("/my/helm.tgz"); + when(mockReleaseManager.getReleaseByVersion("2.8.0.0-b1")) + .thenReturn( + ReleaseManager.ReleaseMetadata.create("2.8.0.0-b1") + .withChartPath(TMP_CHART_PATH + "/yugabyte-2.8.0.0-b1-helm.tar.gz")); + createTempFile(TMP_CHART_PATH, "yugabyte-2.8.0.0-b1-helm.tar.gz", "Sample helm chart data"); when(mockAppConfig.getLong("yb.helm.timeout_secs")).thenReturn((long) 600); runCommand(KubernetesCommandExecutor.CommandType.HELM_UPGRADE); assertEquals( @@ -100,7 +127,7 @@ public void testHelmUpgrade() { "helm", "upgrade", "demo-universe", - "/my/helm.tgz", + "/tmp/yugaware_tests/KubernetesManagerTest/charts/yugabyte-2.8.0.0-b1-helm.tar.gz", "-f", "/tmp/override.yml", "--namespace", @@ -114,14 +141,18 @@ public void testHelmUpgrade() { @Test public void testHelmUpgradeNoTimeout() { - when(mockAppConfig.getString("yb.helm.package")).thenReturn("/my/helm.tgz"); + when(mockReleaseManager.getReleaseByVersion("2.8.0.0-b1")) + .thenReturn( + ReleaseManager.ReleaseMetadata.create("2.8.0.0-b1") + .withChartPath(TMP_CHART_PATH + "/yugabyte-2.8.0.0-b1-helm.tar.gz")); + createTempFile(TMP_CHART_PATH, "yugabyte-2.8.0.0-b1-helm.tar.gz", "Sample helm chart data"); runCommand(KubernetesCommandExecutor.CommandType.HELM_UPGRADE); assertEquals( ImmutableList.of( "helm", "upgrade", "demo-universe", - "/my/helm.tgz", + "/tmp/yugaware_tests/KubernetesManagerTest/charts/yugabyte-2.8.0.0-b1-helm.tar.gz", "-f", "/tmp/override.yml", "--namespace", @@ -138,13 +169,17 @@ public void testHelmUpgradeFailWithNoConfig() { try { runCommand(KubernetesCommandExecutor.CommandType.HELM_UPGRADE); } catch (RuntimeException e) { - assertEquals("Helm Package path not provided.", e.getMessage()); + assertEquals("Helm Package path not found for release: 2.8.0.0-b1", e.getMessage()); } } @Test public void helmInstallWithRequiredConfig() { - when(mockAppConfig.getString("yb.helm.package")).thenReturn("/my/helm.tgz"); + when(mockReleaseManager.getReleaseByVersion("2.8.0.0-b1")) + .thenReturn( + ReleaseManager.ReleaseMetadata.create("2.8.0.0-b1") + .withChartPath(TMP_CHART_PATH + "/yugabyte-2.8.0.0-b1-helm.tar.gz")); + createTempFile(TMP_CHART_PATH, "yugabyte-2.8.0.0-b1-helm.tar.gz", "Sample helm chart data"); when(mockAppConfig.getLong("yb.helm.timeout_secs")).thenReturn((long) 600); runCommand(KubernetesCommandExecutor.CommandType.HELM_INSTALL); assertEquals( @@ -152,7 +187,7 @@ public void helmInstallWithRequiredConfig() { "helm", "install", "demo-universe", - "/my/helm.tgz", + "/tmp/yugaware_tests/KubernetesManagerTest/charts/yugabyte-2.8.0.0-b1-helm.tar.gz", "--namespace", "demo-namespace", "-f", @@ -166,14 +201,18 @@ public void helmInstallWithRequiredConfig() { @Test public void helmInstallWithNoTimeout() { - when(mockAppConfig.getString("yb.helm.package")).thenReturn("/my/helm.tgz"); + when(mockReleaseManager.getReleaseByVersion("2.8.0.0-b1")) + .thenReturn( + ReleaseManager.ReleaseMetadata.create("2.8.0.0-b1") + .withChartPath(TMP_CHART_PATH + "/yugabyte-2.8.0.0-b1-helm.tar.gz")); + createTempFile(TMP_CHART_PATH, "yugabyte-2.8.0.0-b1-helm.tar.gz", "Sample helm chart data"); runCommand(KubernetesCommandExecutor.CommandType.HELM_INSTALL); assertEquals( ImmutableList.of( "helm", "install", "demo-universe", - "/my/helm.tgz", + "/tmp/yugaware_tests/KubernetesManagerTest/charts/yugabyte-2.8.0.0-b1-helm.tar.gz", "--namespace", "demo-namespace", "-f", @@ -190,10 +229,34 @@ public void helmInstallWithoutRequiredConfig() { try { runCommand(KubernetesCommandExecutor.CommandType.HELM_INSTALL); } catch (RuntimeException e) { - assertEquals("Helm Package path not provided.", e.getMessage()); + assertEquals("Helm Package path not found for release: 2.8.0.0-b1", e.getMessage()); } } + // TODO: Delete this test once all k8s customers are upgraded past 2.7 and legacy helm chart is no + // longer required + @Test + public void helmInstallWithLegacyVersion() { + when(mockAppConfig.getString("yb.helm.packagePath")).thenReturn(TMP_CHART_PATH); + createTempFile(TMP_CHART_PATH, "yugabyte-2.7-helm-legacy.tar.gz", "Sample helm chart data"); + runCommand(KubernetesCommandExecutor.CommandType.HELM_INSTALL, "2.7.0.0-b1"); + assertEquals( + ImmutableList.of( + "helm", + "install", + "demo-universe", + "/tmp/yugaware_tests/KubernetesManagerTest/charts/yugabyte-2.7-helm-legacy.tar.gz", + "--namespace", + "demo-namespace", + "-f", + "/tmp/override.yml", + "--timeout", + "300s", + "--wait"), + command.getValue()); + assertEquals(config.getValue(), configProvider); + } + @Test public void getPodInfos() { runCommand(KubernetesCommandExecutor.CommandType.POD_INFO); diff --git a/managed/src/test/java/com/yugabyte/yw/common/ReleaseManagerTest.java b/managed/src/test/java/com/yugabyte/yw/common/ReleaseManagerTest.java index fa68633f3ea7..236a0127ef33 100644 --- a/managed/src/test/java/com/yugabyte/yw/common/ReleaseManagerTest.java +++ b/managed/src/test/java/com/yugabyte/yw/common/ReleaseManagerTest.java @@ -8,6 +8,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -63,14 +64,15 @@ public void tearDown() throws IOException { private void createDummyReleases( List versions, boolean multipleRepos, boolean inDockerPath) { - createDummyReleases(versions, multipleRepos, inDockerPath, true); + createDummyReleases(versions, multipleRepos, inDockerPath, true, true); } private void createDummyReleases( List versions, boolean multipleRepos, boolean inDockerPath, - boolean hasEnterpriseStr) { + boolean hasEnterpriseStr, + boolean withHelmChart) { versions.forEach( (version) -> { String versionPath = String.format("%s/%s", TMP_STORAGE_PATH, version); @@ -85,6 +87,10 @@ private void createDummyReleases( createTempFile( versionPath, "devops.xyz." + version + "-centos-x86_64.tar.gz", "Sample data"); } + if (withHelmChart) { + createTempFile( + versionPath, "yugabyte-" + version + "-helm.tar.gz", "Sample helm chart data"); + } }); } @@ -92,35 +98,36 @@ private void createDummyReleases( public void testGetLocalReleasesWithValidPath() { List versions = ImmutableList.of("0.0.1", "0.0.2", "0.0.3"); createDummyReleases(versions, false, false); - Map releases = releaseManager.getLocalReleases(TMP_STORAGE_PATH); + Map releases = + releaseManager.getLocalReleases(TMP_STORAGE_PATH); assertEquals(3, releases.size()); - - releases - .keySet() - .forEach( - (version) -> { - assertTrue(versions.contains(version)); - }); + releases.forEach( + (version, release) -> { + assertTrue(versions.contains(version)); + assertNotNull(release.filePath); + assertNotNull(release.chartPath); + }); } @Test public void testGetLocalReleasesWithMultipleFilesValidPath() { List versions = ImmutableList.of("0.0.1", "0.0.2", "0.0.3"); createDummyReleases(versions, true, false); - Map releases = releaseManager.getLocalReleases(TMP_STORAGE_PATH); + Map releases = + releaseManager.getLocalReleases(TMP_STORAGE_PATH); assertEquals(3, releases.size()); - - releases - .keySet() - .forEach( - (version) -> { - assertTrue(versions.contains(version)); - }); + releases.forEach( + (version, release) -> { + assertTrue(versions.contains(version)); + assertNotNull(release.filePath); + assertNotNull(release.chartPath); + }); } @Test public void testGetLocalReleasesWithInvalidPath() { - Map releases = releaseManager.getLocalReleases("/foo/bar"); + Map releases = + releaseManager.getLocalReleases("/foo/bar"); assertTrue(releases.isEmpty()); } @@ -130,16 +137,20 @@ public void testLoadReleasesWithoutReleasePath() { Mockito.verify(configHelper, times(0)).loadConfigToDB(any(), anyMap()); } - private void assertReleases(Map expectedMap, HashMap releases) { - assertEquals(expectedMap.size(), releases.size()); - for (Object version : releases.keySet()) { - assertTrue(expectedMap.containsKey(version)); - Object expectedFile = expectedMap.get(version); - JsonNode releaseJson = Json.toJson(releases.get(version)); - assertValue(releaseJson, "filePath", expectedFile.toString()); - assertValue(releaseJson, "imageTag", version.toString()); - assertValue(releaseJson, "state", "ACTIVE"); - } + private void assertReleases( + Map expected, + Map actual) { + assertEquals(expected.size(), actual.size()); + expected.forEach( + (version, expectedRelease) -> { + assertTrue(actual.containsKey(version)); + ReleaseManager.ReleaseMetadata actualRelease = actual.get(version); + assertEquals(version, actualRelease.imageTag); + assertEquals(expectedRelease.imageTag, actualRelease.imageTag); + assertEquals(expectedRelease.state, actualRelease.state); + assertEquals(expectedRelease.filePath, actualRelease.filePath); + assertEquals(expectedRelease.chartPath, actualRelease.chartPath); + }); } @Test @@ -157,7 +168,32 @@ public void testLoadReleasesWithReleasePath() { .loadConfigToDB(configType.capture(), releaseMap.capture()); Map expectedMap = ImmutableMap.of( - "0.0.1", TMP_STORAGE_PATH + "/0.0.1/yugabyte-ee-0.0.1-centos-x86_64.tar.gz"); + "0.0.1", + ReleaseManager.ReleaseMetadata.create("0.0.1") + .withFilePath(TMP_STORAGE_PATH + "/0.0.1/yugabyte-ee-0.0.1-centos-x86_64.tar.gz") + .withChartPath(TMP_STORAGE_PATH + "/0.0.1/yugabyte-0.0.1-helm.tar.gz")); + assertReleases(expectedMap, releaseMap.getValue()); + } + + @Test + public void testLoadReleasesWithoutChart() { + when(appConfig.getString("yb.releases.path")).thenReturn(TMP_STORAGE_PATH); + List versions = ImmutableList.of("0.0.1"); + createDummyReleases(versions, false, false, true, false); + releaseManager.importLocalReleases(); + + ArgumentCaptor configType; + ArgumentCaptor releaseMap; + configType = ArgumentCaptor.forClass(ConfigHelper.ConfigType.class); + releaseMap = ArgumentCaptor.forClass(HashMap.class); + Mockito.verify(configHelper, times(1)) + .loadConfigToDB(configType.capture(), releaseMap.capture()); + Map expectedMap = + ImmutableMap.of( + "0.0.1", + ReleaseManager.ReleaseMetadata.create("0.0.1") + .withFilePath(TMP_STORAGE_PATH + "/0.0.1/yugabyte-ee-0.0.1-centos-x86_64.tar.gz") + .withChartPath("")); assertReleases(expectedMap, releaseMap.getValue()); } @@ -165,12 +201,13 @@ public void testLoadReleasesWithReleasePath() { public void testLoadReleasesWithReleaseAndDockerPath() { when(appConfig.getString("yb.releases.path")).thenReturn(TMP_STORAGE_PATH); when(appConfig.getString("yb.docker.release")).thenReturn(TMP_DOCKER_STORAGE_PATH); + when(appConfig.getString("yb.helm.packagePath")).thenReturn(TMP_DOCKER_STORAGE_PATH); List versions = ImmutableList.of("0.0.1"); createDummyReleases(versions, false, false); List dockerVersions = ImmutableList.of("0.0.2-b2"); createDummyReleases(dockerVersions, false, true); List dockerVersionsWithoutEe = ImmutableList.of("0.0.3-b3"); - createDummyReleases(dockerVersionsWithoutEe, false, true, false); + createDummyReleases(dockerVersionsWithoutEe, false, true, false, true); releaseManager.importLocalReleases(); ArgumentCaptor configType; ArgumentCaptor releaseMap; @@ -180,15 +217,24 @@ public void testLoadReleasesWithReleaseAndDockerPath() { .loadConfigToDB(configType.capture(), releaseMap.capture()); Map expectedMap = ImmutableMap.of( - "0.0.1", TMP_STORAGE_PATH + "/0.0.1/yugabyte-ee-0.0.1-centos-x86_64.tar.gz", - "0.0.2-b2", TMP_STORAGE_PATH + "/0.0.2-b2/yugabyte-ee-0.0.2-b2-centos-x86_64.tar.gz", - "0.0.3-b3", TMP_STORAGE_PATH + "/0.0.3-b3/yugabyte-0.0.3-b3-centos-x86_64.tar.gz"); + "0.0.1", + ReleaseManager.ReleaseMetadata.create("0.0.1") + .withFilePath( + TMP_STORAGE_PATH + "/0.0.1/yugabyte-ee-0.0.1-centos-x86_64.tar.gz") + .withChartPath(TMP_STORAGE_PATH + "/0.0.1/yugabyte-0.0.1-helm.tar.gz"), + "0.0.2-b2", + ReleaseManager.ReleaseMetadata.create("0.0.2-b2") + .withFilePath( + TMP_STORAGE_PATH + "/0.0.2-b2/yugabyte-ee-0.0.2-b2-centos-x86_64.tar.gz") + .withChartPath(TMP_STORAGE_PATH + "/0.0.2-b2/yugabyte-0.0.2-b2-helm.tar.gz"), + "0.0.3-b3", + ReleaseManager.ReleaseMetadata.create("0.0.3-b3") + .withFilePath( + TMP_STORAGE_PATH + "/0.0.3-b3/yugabyte-0.0.3-b3-centos-x86_64.tar.gz") + .withChartPath(TMP_STORAGE_PATH + "/0.0.3-b3/yugabyte-0.0.3-b3-helm.tar.gz")); assertEquals(SoftwareReleases, configType.getValue()); assertReleases(expectedMap, releaseMap.getValue()); - File dockerStoragePath = new File(TMP_DOCKER_STORAGE_PATH); - File[] files = dockerStoragePath.listFiles(); - assertEquals(0, files.length); } @Test @@ -208,13 +254,13 @@ public void testLoadReleasesWithReleaseAndDockerPathDuplicate() { .loadConfigToDB(configType.capture(), releaseMap.capture()); Map expectedMap = ImmutableMap.of( - "0.0.2-b2", TMP_STORAGE_PATH + "/0.0.2-b2/yugabyte-ee-0.0.2-b2-centos-x86_64.tar.gz"); + "0.0.2-b2", + ReleaseManager.ReleaseMetadata.create("0.0.2-b2") + .withFilePath( + TMP_STORAGE_PATH + "/0.0.2-b2/yugabyte-ee-0.0.2-b2-centos-x86_64.tar.gz") + .withChartPath(TMP_STORAGE_PATH + "/0.0.2-b2/yugabyte-0.0.2-b2-helm.tar.gz")); assertReleases(expectedMap, releaseMap.getValue()); assertEquals(SoftwareReleases, configType.getValue()); - - File dockerStoragePath = new File(TMP_DOCKER_STORAGE_PATH); - File[] files = dockerStoragePath.listFiles(); - assertEquals(0, files.length); } @Test diff --git a/managed/src/test/java/com/yugabyte/yw/controllers/UniverseControllerTestBase.java b/managed/src/test/java/com/yugabyte/yw/controllers/UniverseControllerTestBase.java index 2010221ca1da..eac5235fadc8 100644 --- a/managed/src/test/java/com/yugabyte/yw/controllers/UniverseControllerTestBase.java +++ b/managed/src/test/java/com/yugabyte/yw/controllers/UniverseControllerTestBase.java @@ -26,6 +26,7 @@ import com.yugabyte.yw.commissioner.HealthChecker; import com.yugabyte.yw.common.ApiHelper; import com.yugabyte.yw.common.ModelFactory; +import com.yugabyte.yw.common.ReleaseManager; import com.yugabyte.yw.common.ShellProcessHandler; import com.yugabyte.yw.common.YcqlQueryExecutor; import com.yugabyte.yw.common.YsqlQueryExecutor; @@ -97,6 +98,7 @@ public class UniverseControllerTestBase extends WithApplication { protected PlayCacheSessionStore mockSessionStore; protected AlertConfigurationWriter mockAlertConfigurationWriter; protected Config mockRuntimeConfig; + protected ReleaseManager mockReleaseManager; @Override protected Application provideApplication() { @@ -114,6 +116,7 @@ protected Application provideApplication() { mockSessionStore = mock(PlayCacheSessionStore.class); mockAlertConfigurationWriter = mock(AlertConfigurationWriter.class); mockRuntimeConfig = mock(Config.class); + mockReleaseManager = mock(ReleaseManager.class); healthChecker = mock(HealthChecker.class); when(mockRuntimeConfig.getBoolean("yb.cloud.enabled")).thenReturn(false); @@ -138,6 +141,7 @@ protected Application provideApplication() { .overrides( bind(RuntimeConfigFactory.class) .toInstance(new DummyRuntimeConfigFactoryImpl(mockRuntimeConfig))) + .overrides(bind(ReleaseManager.class).toInstance(mockReleaseManager)) .overrides(bind(HealthChecker.class).toInstance(healthChecker)) .build(); } diff --git a/managed/src/test/java/com/yugabyte/yw/controllers/UniverseCreateControllerTestBase.java b/managed/src/test/java/com/yugabyte/yw/controllers/UniverseCreateControllerTestBase.java index 821ba35374e7..880a6838f4b0 100644 --- a/managed/src/test/java/com/yugabyte/yw/controllers/UniverseCreateControllerTestBase.java +++ b/managed/src/test/java/com/yugabyte/yw/controllers/UniverseCreateControllerTestBase.java @@ -18,6 +18,7 @@ import static com.yugabyte.yw.common.AssertHelper.assertYWSE; import static com.yugabyte.yw.common.FakeApiHelper.doRequestWithAuthTokenAndBody; import static com.yugabyte.yw.common.PlacementInfoUtil.updateUniverseDefinition; +import static com.yugabyte.yw.common.TestHelper.createTempFile; import static com.yugabyte.yw.forms.UniverseConfigureTaskParams.ClusterOperationType.CREATE; import static junit.framework.TestCase.assertFalse; import static org.hamcrest.CoreMatchers.allOf; @@ -45,6 +46,7 @@ import com.yugabyte.yw.commissioner.Common; import com.yugabyte.yw.common.ModelFactory; import com.yugabyte.yw.common.PlacementInfoUtil; +import com.yugabyte.yw.common.ReleaseManager; import com.yugabyte.yw.forms.NodeInstanceFormData; import com.yugabyte.yw.forms.UniverseDefinitionTaskParams; import com.yugabyte.yw.forms.UniverseTaskParams; @@ -60,12 +62,16 @@ import com.yugabyte.yw.models.helpers.PlacementInfo.PlacementAZ; import com.yugabyte.yw.models.helpers.TaskType; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.UUID; import junitparams.JUnitParamsRunner; import junitparams.Parameters; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -75,6 +81,21 @@ @RunWith(JUnitParamsRunner.class) public abstract class UniverseCreateControllerTestBase extends UniverseControllerTestBase { + + static String TMP_CHART_PATH = "/tmp/yugaware_tests/UniverseUiOnlyControllerTest/charts"; + + @Before + public void setUp() { + super.setUp(); + new File(TMP_CHART_PATH).mkdirs(); + } + + @After + public void tearDown() throws IOException { + FileUtils.deleteDirectory(new File(TMP_CHART_PATH)); + super.tearDown(); + } + public abstract Result sendCreateRequest(ObjectNode bodyJson); public abstract Result sendPrimaryCreateConfigureRequest(ObjectNode topJson); @@ -396,6 +417,11 @@ public void testUniverseCreateDeviceInfoValidation( when(mockCommissioner.submit( Matchers.any(TaskType.class), Matchers.any(UniverseDefinitionTaskParams.class))) .thenReturn(fakeTaskUUID); + when(mockReleaseManager.getReleaseByVersion("1.0.0")) + .thenReturn( + ReleaseManager.ReleaseMetadata.create("1.0.0") + .withChartPath(TMP_CHART_PATH + "/yugabyte-1.0.0-helm.tar.gz")); + createTempFile(TMP_CHART_PATH, "yugabyte-1.0.0-helm.tar.gz", "Sample helm chart data"); Provider p; switch (cloudType) { @@ -436,7 +462,8 @@ public void testUniverseCreateDeviceInfoValidation( .put("replicationFactor", 3) .put("numNodes", 3) .put("provider", p.uuid.toString()) - .put("accessKeyCode", accessKeyCode); + .put("accessKeyCode", accessKeyCode) + .put("ybSoftwareVersion", "1.0.0"); ArrayNode regionList = Json.newArray().add(r.uuid.toString()); userIntentJson.set("regionList", regionList); ObjectNode deviceInfo = diff --git a/managed/src/test/resources/dev.expected.conf b/managed/src/test/resources/dev.expected.conf index 295c0887b818..fde0a48d30ad 100644 --- a/managed/src/test/resources/dev.expected.conf +++ b/managed/src/test/resources/dev.expected.conf @@ -119,7 +119,7 @@ yb.health.default_tls = false yb.health.ses_email_password = "RESOLVED_YB_ALERTS_PASSWORD" yb.health.ses_email_username = "RESOLVED_YB_ALERTS_USERNAME" yb.health.status_interval_ms = 43200000 -yb.helm.package = "RESOLVED_HELM_PACKAGE_PATH" +yb.helm.packagePath = "RESOLVED_HELM_PACKAGE_PATH" yb.helm.timeout_secs = 900 yb.log.logEnvVars = false yb.metrics.host = localhost diff --git a/managed/src/test/resources/helm.expected.conf b/managed/src/test/resources/helm.expected.conf index 33b10fa895a4..fef86648602d 100644 --- a/managed/src/test/resources/helm.expected.conf +++ b/managed/src/test/resources/helm.expected.conf @@ -120,7 +120,7 @@ yb.health.default_tls = false yb.health.ses_email_password = "helm#$password%" yb.health.ses_email_username = "alerts.helm.username" yb.health.status_interval_ms = 43200000 -yb.helm.package = "/opt/yugabyte/helm/yugabyte-latest.tgz" +yb.helm.packagePath = "/opt/yugabyte/helm" yb.helm.timeout_secs = 900 yb.metrics.host = localhost yb.metrics.url = "http://127.0.0.1:9090/api/v1" diff --git a/managed/src/test/resources/replicated.expected.conf b/managed/src/test/resources/replicated.expected.conf index bbe3c9c346db..13c53151583b 100644 --- a/managed/src/test/resources/replicated.expected.conf +++ b/managed/src/test/resources/replicated.expected.conf @@ -118,7 +118,7 @@ yb.health.default_tls = false yb.health.ses_email_password = "alerts#pass%" yb.health.ses_email_username = "alerts.username" yb.health.status_interval_ms = 43200000 -yb.helm.package = "/opt/yugabyte/helm/yugabyte-latest.tgz" +yb.helm.packagePath = "/opt/yugabyte/helm" yb.metrics.host = "5.6.7.8" yb.metrics.url = "http://5.6.7.8:9090/api/v1" yb.metrics.management.url = "http://5.6.7.8:9090/-" diff --git a/managed/src/test/resources/test.helm.params.conf b/managed/src/test/resources/test.helm.params.conf index cc7c24e3a7b9..0176aaa8b0e5 100644 --- a/managed/src/test/resources/test.helm.params.conf +++ b/managed/src/test/resources/test.helm.params.conf @@ -46,7 +46,7 @@ yb { docker.release = "/opt/yugabyte/release" # TODO(bogdan): need this extra level for installing from local... thirdparty.packagePath = /opt/third-party/third-party - helm.package = "/opt/yugabyte/helm/yugabyte-latest.tgz" + helm.packagePath = "/opt/yugabyte/helm" helm.timeout_secs = 900 health.check_interval_ms = 300000 health.status_interval_ms = 43200000 diff --git a/managed/src/test/resources/test.replicated.params.conf b/managed/src/test/resources/test.replicated.params.conf index 451f3f841bb3..9fca778a9580 100644 --- a/managed/src/test/resources/test.replicated.params.conf +++ b/managed/src/test/resources/test.replicated.params.conf @@ -72,7 +72,7 @@ yb { releases.path = "/opt/yugabyte/releases" docker.release = "/opt/yugabyte/release" thirdparty.packagePath = /opt/third-party - helm.package = "/opt/yugabyte/helm/yugabyte-latest.tgz" + helm.packagePath = "/opt/yugabyte/helm" health.check_interval_ms = 300000 health.status_interval_ms = 43200000 health.default_email = "repl.alerts@yugabyte.com" diff --git a/managed/ui/src/components/releases/ReleaseList/ReleaseList.js b/managed/ui/src/components/releases/ReleaseList/ReleaseList.js index 5297d50a0067..b37c2636be7a 100644 --- a/managed/ui/src/components/releases/ReleaseList/ReleaseList.js +++ b/managed/ui/src/components/releases/ReleaseList/ReleaseList.js @@ -1,16 +1,15 @@ // Copyright (c) YugaByte, Inc. -import React, { Component } from 'react'; -import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table'; -import { DropdownButton } from 'react-bootstrap'; +import React, {Component} from 'react'; +import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table'; +import {DropdownButton} from 'react-bootstrap'; -import { YBPanelItem } from '../../../components/panels'; -import { YBButton, YBTextInput } from '../../../components/common/forms/fields'; -import { TableAction } from '../../../components/tables'; -import { YBLoadingCircleIcon } from '../../../components/common/indicators'; -import { getPromiseState } from '../../../utils/PromiseUtils'; -import { isAvailable } from '../../../utils/LayoutUtils'; -import { showOrRedirect } from '../../../utils/LayoutUtils'; +import {YBPanelItem} from '../../../components/panels'; +import {YBButton, YBTextInput} from '../../../components/common/forms/fields'; +import {TableAction} from '../../../components/tables'; +import {YBLoadingCircleIcon} from '../../../components/common/indicators'; +import {getPromiseState} from '../../../utils/PromiseUtils'; +import {isAvailable, showOrRedirect} from '../../../utils/LayoutUtils'; import './ReleaseList.scss'; @@ -218,10 +217,19 @@ export default class ReleaseList extends Component { tdStyle={{ whiteSpace: 'normal' }} columnClassName="no-border name-column" className="no-border" - width="550px" + width="225px" > File Path + + Chart Path +