Skip to content

Commit

Permalink
[PLAT-13644] Edit universe v2
Browse files Browse the repository at this point in the history
Summary:
Implementation of edit universe in v2. Only a sub-set of the properties in create spec are editable. So creating an EditSpec for such objects. OTOH, for some objects, all the properties are editable. So using the create Spec for such objets in the edit request payload.

CRUD of ReadReplica clusters will be taken up in a subsequent diff.

Test Plan: Unit test.

Reviewers: dshubin, sanketh, #yba-api-review!, skurapati, dkumar

Reviewed By: dshubin

Subscribers: yugaware

Differential Revision: https://phorge.dev.yugabyte.com/D34782
  • Loading branch information
subramanian-neelakantan committed May 13, 2024
1 parent 718f58d commit ea27a38
Show file tree
Hide file tree
Showing 15 changed files with 1,513 additions and 1,021 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import api.v2.handlers.UniverseManagementHandler;
import api.v2.models.UniverseCreateSpec;
import api.v2.models.UniverseEditSpec;
import api.v2.models.UniverseResp;
import api.v2.models.YBPTask;
import com.google.inject.Inject;
Expand All @@ -23,4 +24,11 @@ public YBPTask createUniverse(Request request, UUID cUUID, UniverseCreateSpec un
throws Exception {
return universeHandler.createUniverse(cUUID, universeSpec);
}

@Override
public YBPTask editUniverse(
Request request, UUID cUUID, UUID uniUUID, UniverseEditSpec universeEditSpec)
throws Exception {
return universeHandler.editUniverse(cUUID, uniUUID, universeEditSpec);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,26 @@
import api.v2.mappers.UniverseDefinitionTaskParamsMapper;
import api.v2.mappers.UniverseRespMapper;
import api.v2.models.UniverseCreateSpec;
import api.v2.models.UniverseEditSpec;
import api.v2.models.UniverseResp;
import api.v2.models.YBPTask;
import api.v2.utils.ApiControllerUtils;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.inject.Inject;
import com.yugabyte.yw.commissioner.Commissioner;
import com.yugabyte.yw.commissioner.Common;
import com.yugabyte.yw.common.PlacementInfoUtil;
import com.yugabyte.yw.common.config.RuntimeConfigFactory;
import com.yugabyte.yw.controllers.handlers.UniverseCRUDHandler;
import com.yugabyte.yw.controllers.handlers.UniverseCRUDHandler.OpType;
import com.yugabyte.yw.forms.UniverseConfigureTaskParams;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams.Cluster;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams.ClusterType;
import com.yugabyte.yw.models.Customer;
import com.yugabyte.yw.models.Universe;
import com.yugabyte.yw.models.helpers.CommonUtils;
import com.yugabyte.yw.models.helpers.TaskType;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import play.libs.Json;
Expand All @@ -25,6 +32,7 @@
public class UniverseManagementHandler extends ApiControllerUtils {
@Inject private RuntimeConfigFactory runtimeConfigFactory;
@Inject private UniverseCRUDHandler universeCRUDHandler;
@Inject private Commissioner commissioner;

public UniverseResp getUniverse(UUID cUUID, UUID uniUUID) {
Customer customer = Customer.getOrBadRequest(cUUID);
Expand All @@ -44,7 +52,6 @@ public YBPTask createUniverse(UUID cUUID, UniverseCreateSpec universeSpec) {
"Create Universe with v2 spec: {}",
Json.prettyPrint(CommonUtils.maskConfig((ObjectNode) Json.toJson(universeSpec))));
// map universeSpec to v1 universe details
// create universe with v1 spec
UniverseDefinitionTaskParams v1DefnParams =
UniverseDefinitionTaskParamsMapper.INSTANCE.toV1UniverseDefinitionTaskParamsFromCreateSpec(
universeSpec);
Expand All @@ -53,6 +60,7 @@ public YBPTask createUniverse(UUID cUUID, UniverseCreateSpec universeSpec) {
log.debug(
"Create Universe translated to v1 spec: {}",
Json.prettyPrint(CommonUtils.maskConfig((ObjectNode) Json.toJson(v1Params))));
// create universe with v1 spec
v1Params.clusterOperation = UniverseConfigureTaskParams.ClusterOperationType.CREATE;
v1Params.currentClusterType = ClusterType.PRIMARY;
universeCRUDHandler.configure(customer, v1Params);
Expand All @@ -65,4 +73,55 @@ public YBPTask createUniverse(UUID cUUID, UniverseCreateSpec universeSpec) {
universeCRUDHandler.createUniverse(customer, v1Params);
return new YBPTask().resourceUuid(universeResp.universeUUID).taskUuid(universeResp.taskUUID);
}

public YBPTask editUniverse(UUID cUUID, UUID uniUUID, UniverseEditSpec universeEditSpec) {
Customer customer = Customer.getOrBadRequest(cUUID);
Universe dbUniverse = Universe.getOrBadRequest(uniUUID);
log.info(
"Edit Universe with v2 spec: {}",
Json.prettyPrint(CommonUtils.maskConfig((ObjectNode) Json.toJson(universeEditSpec))));
// map universeEditSpec to v1 universe details
UniverseDefinitionTaskParams v1DefnParams =
UniverseDefinitionTaskParamsMapper.INSTANCE.toV1UniverseDefinitionTaskParamsFromEditSpec(
universeEditSpec, dbUniverse.getUniverseDetails());
UniverseConfigureTaskParams v1Params =
UniverseDefinitionTaskParamsMapper.INSTANCE.toUniverseConfigureTaskParams(v1DefnParams);
// v1Params.setUniverseUUID(uniUUID);
log.debug(
"Edit Universe translated to v1 spec: {}",
Json.prettyPrint(CommonUtils.maskConfig((ObjectNode) Json.toJson(v1Params))));
// edit universe with v1 spec
v1Params.clusterOperation = UniverseConfigureTaskParams.ClusterOperationType.EDIT;
// TODO: Handle ASYNC cluster edit
v1Params.currentClusterType = ClusterType.PRIMARY;
universeCRUDHandler.configure(customer, v1Params);
universeCRUDHandler.checkGeoPartitioningParameters(customer, v1Params, OpType.UPDATE);

Cluster primaryCluster = v1Params.getPrimaryCluster();
for (Cluster readOnlyCluster : dbUniverse.getUniverseDetails().getReadOnlyClusters()) {
UniverseCRUDHandler.validateConsistency(primaryCluster, readOnlyCluster);
}

TaskType taskType = TaskType.EditUniverse;
if (primaryCluster.userIntent.providerType.equals(Common.CloudType.kubernetes)) {
taskType = TaskType.EditKubernetesUniverse;
universeCRUDHandler.notHelm2LegacyOrBadRequest(dbUniverse);
universeCRUDHandler.checkHelmChartExists(primaryCluster.userIntent.ybSoftwareVersion);
} else {
universeCRUDHandler.mergeNodeExporterInfo(dbUniverse, v1Params);
}
for (Cluster cluster : v1Params.clusters) {
PlacementInfoUtil.updatePlacementInfo(
v1Params.getNodesInCluster(cluster.uuid), cluster.placementInfo);
}
v1Params.rootCA = universeCRUDHandler.checkValidRootCA(dbUniverse.getUniverseDetails().rootCA);
UUID taskUUID = commissioner.submit(taskType, v1Params);
log.info(
"Submitted {} for {} : {}, task uuid = {}.",
taskType,
uniUUID,
dbUniverse.getName(),
taskUUID);
return new YBPTask().resourceUuid(uniUUID).taskUuid(taskUUID);
}
}
25 changes: 25 additions & 0 deletions managed/src/main/java/api/v2/mappers/ClusterMapper.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Copyright (c) YugaByte, Inc.
package api.v2.mappers;

import api.v2.models.ClusterEditSpec;
import api.v2.models.ClusterInfo;
import api.v2.models.ClusterSpec;
import api.v2.models.PlacementAZ;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams.Cluster;
import com.yugabyte.yw.models.helpers.PlacementInfo;
import java.util.List;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValueCheckStrategy;

@Mapper(
Expand Down Expand Up @@ -37,6 +40,28 @@ public interface ClusterMapper {
@Mapping(target = ".", source = "userIntent")
ClusterInfo toV2ClusterInfo(Cluster v1Cluster);

@Mapping(target = "userIntent", source = ".")
// @Mapping(target = "userIntent.deviceInfo", source = "storageSpec")
@Mapping(target = "placementInfo", source = "placementSpec")
Cluster toV1ClusterFromClusterEditSpec(
ClusterEditSpec clusterEditSpec, @MappingTarget Cluster v1Cluster);

default List<Cluster> toV1ClusterFromClusterEditSpecList(
List<ClusterEditSpec> clusterEditSpecList, @MappingTarget List<Cluster> v1Clusters) {
if (clusterEditSpecList == null) {
return v1Clusters;
}
for (ClusterEditSpec clusterEditSpec : clusterEditSpecList) {
Cluster v1Cluster =
v1Clusters.stream()
.filter(c -> c.uuid.equals(clusterEditSpec.getUuid()))
.findAny()
.orElseThrow();
toV1ClusterFromClusterEditSpec(clusterEditSpec, v1Cluster);
}
return v1Clusters;
}

// used implicitly in above mapping
@Mapping(target = "numNodesInAZ", source = "numNodesInAz")
@Mapping(target = "isAffinitized", source = "leaderAffinity")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams.Cluster;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams.UserIntent;
import com.yugabyte.yw.models.Provider;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public abstract class UniverseDefinitionTaskParamsDecorator
implements UniverseDefinitionTaskParamsMapper {
Expand Down Expand Up @@ -69,11 +71,15 @@ public UniverseSpec toV2UniverseSpec(UniverseDefinitionTaskParams v1UniverseTask
@Override
public UniverseDefinitionTaskParams toV1UniverseDefinitionTaskParams(UniverseSpec universeSpec) {
UniverseDefinitionTaskParams params = delegate.toV1UniverseDefinitionTaskParams(universeSpec);
// find out the provider type of universe
// set universeName, ysqlPassword, ycqlPassword into all cluster's userIntent
if (params.clusters != null) {
for (Cluster cluster : params.clusters) {
if (cluster.userIntent != null) {
cluster.userIntent.universeName = universeSpec.getName();
Provider clusterProvider =
Provider.getOrBadRequest(UUID.fromString(cluster.userIntent.provider));
cluster.userIntent.providerType = clusterProvider.getCloudCode();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import api.v2.models.NodeDetails;
import api.v2.models.NodeDetails.MasterStateEnum;
import api.v2.models.UniverseCreateSpec;
import api.v2.models.UniverseEditSpec;
import api.v2.models.UniverseInfo;
import api.v2.models.UniverseSpec;
import api.v2.models.YcqlSpec;
Expand All @@ -28,6 +29,7 @@
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.ValueMapping;
import org.mapstruct.ValueMappings;
import org.mapstruct.control.DeepClone;
Expand Down Expand Up @@ -87,6 +89,10 @@ public KubernetesGFlagsUpgradeParams toKubernetesGFlagsUpgradeParams(
UniverseDefinitionTaskParams toV1UniverseDefinitionTaskParamsFromCreateSpec(
UniverseCreateSpec universeCreateSpec);

UniverseDefinitionTaskParams toV1UniverseDefinitionTaskParamsFromEditSpec(
UniverseEditSpec universeEditSpec,
@MappingTarget UniverseDefinitionTaskParams v1UniverseDefinitionTaskParams);

@Mapping(target = "universeUuid", source = "universeUUID")
@Mapping(target = "updatingTaskUuid", source = "updatingTaskUUID")
@Mapping(target = "encryptionAtRestInfo", source = "encryptionAtRestConfig")
Expand Down
7 changes: 7 additions & 0 deletions managed/src/main/java/api/v2/mappers/UserIntentMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package api.v2.mappers;

import api.v2.models.AvailabilityZoneGFlags;
import api.v2.models.ClusterEditSpec;
import api.v2.models.ClusterGFlags;
import api.v2.models.ClusterNetworkingSpec;
import api.v2.models.ClusterSpec;
Expand All @@ -21,6 +22,7 @@
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.ValueMapping;
import org.mapstruct.ValueMappings;

Expand Down Expand Up @@ -79,6 +81,11 @@ default ClusterGFlags specificGFlagsToClusterGFlags(
@Mapping(target = "specificGFlags", source = "clusterSpec")
UserIntent toV1UserIntent(ClusterSpec clusterSpec);

@Mapping(target = "deviceInfo", source = "storageSpec")
@Mapping(target = ".", source = "providerSpec")
UserIntent toV1UserIntentFromClusterEditSpec(
ClusterEditSpec clusterEditSpec, @MappingTarget UserIntent userIntent);

@InheritInverseConfiguration
StorageType mapStorageTypeEnum(StorageTypeEnum storageType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public class UniverseCRUDHandler {

@Inject CertificateHelper certificateHelper;

private enum OpType {
public enum OpType {
CONFIGURE,
CREATE,
UPDATE
Expand Down Expand Up @@ -350,7 +350,7 @@ public void configure(Customer customer, UniverseConfigureTaskParams taskParams)
}
}

private void checkGeoPartitioningParameters(
public void checkGeoPartitioningParameters(
Customer customer, UniverseDefinitionTaskParams taskParams, OpType op) {

UUID defaultRegionUUID = PlacementInfoUtil.getDefaultRegion(taskParams);
Expand Down Expand Up @@ -1074,7 +1074,7 @@ private UUID updateCluster(
}

/** Merge node exporter related information from current universe details to the task params */
private void mergeNodeExporterInfo(Universe u, UniverseDefinitionTaskParams taskParams) {
public void mergeNodeExporterInfo(Universe u, UniverseDefinitionTaskParams taskParams) {
// Set the node exporter config based on the provider
UniverseDefinitionTaskParams universeDetails = u.getUniverseDetails();
boolean installNodeExporter = universeDetails.extraDependencies.installNodeExporter;
Expand Down Expand Up @@ -1127,7 +1127,7 @@ private UUID submitEditUniverse(
return taskUUID;
}

private void notHelm2LegacyOrBadRequest(Universe u) {
public void notHelm2LegacyOrBadRequest(Universe u) {
Map<String, String> universeConfig = u.getConfig();
if (!universeConfig.containsKey(Universe.HELM2_LEGACY)) {
throw new PlatformServiceException(
Expand All @@ -1140,7 +1140,7 @@ private void notHelm2LegacyOrBadRequest(Universe u) {
}
}

private UUID checkValidRootCA(UUID rootCA) {
public UUID checkValidRootCA(UUID rootCA) {
if (!CertificateInfo.isCertificateValid(rootCA)) {
String errMsg =
String.format(
Expand Down Expand Up @@ -1506,7 +1506,7 @@ public UUID createReadReplicaCluster(
return taskUUID;
}

static void validateConsistency(Cluster primaryCluster, Cluster cluster) {
public static void validateConsistency(Cluster primaryCluster, Cluster cluster) {
checkEquals(c -> c.userIntent.enableYSQL, primaryCluster, cluster, "Ysql setting");
checkEquals(c -> c.userIntent.enableYSQLAuth, primaryCluster, cluster, "Ysql auth setting");
checkEquals(c -> c.userIntent.enableYCQL, primaryCluster, cluster, "Ycql setting");
Expand Down Expand Up @@ -2069,7 +2069,7 @@ public UUID tlsConfigUpdate(
return upgradeUniverseHandler.rotateCerts(certsRotateParams, customer, universe);
}

private void checkHelmChartExists(String ybSoftwareVersion) {
public void checkHelmChartExists(String ybSoftwareVersion) {
try {
kubernetesManagerFactory.getManager().getHelmPackagePath(ybSoftwareVersion);
} catch (RuntimeException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
content:
application/json:
schema:
$ref: ../schemas/UniverseEditSpec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
title: Edit Cluster Spec
type: object
required:
- uuid
properties:
uuid:
description: The system generated cluster uuid to edit. This can be fetched from ClusterInfo.
type: string
format: uuid
example: 19ebde21-d537-47dc-8fab-3edc243c6f68
num_nodes:
description: Set the number of nodes (tservers) to provision in this cluster
type: integer
format: int32
example: 3
minimum: 1
instance_type:
description: Set instance type for tserver nodes of cluster
type: string
example: c5.xlarge
storage_spec:
$ref: ./ClusterStorageSpec.yaml
provider_spec:
$ref: ./ClusterProviderEditSpec.yaml
placement_spec:
$ref: ./ClusterPlacementSpec.yaml
instance_tags:
description: 'A map of strings representing a set of Tags and Values to apply on nodes in the aws/gcp/azu cloud. See https://docs.yugabyte.com/preview/yugabyte-platform/manage-deployments/instance-tags/.'
type: object
additionalProperties:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
title: Edit Cloud Provider settings for the cluster
type: object
properties:
region_list:
description: Edit the list of regions in the cloud provider to place data replicas
type: array
items:
type: string
format: uuid
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
title: Edit Universe Spec
type: object
required:
- clusters
- expected_universe_version
properties:
clusters:
type: array
items:
$ref: ./ClusterEditSpec.yaml
maxItems: 16
minItems: 1
expected_universe_version:
description: Expected universe version. Set to -1 to ignore version checking.
type: integer
format: int32

0 comments on commit ea27a38

Please sign in to comment.