Skip to content

Commit cc9e979

Browse files
committed
[PLAT-12874] Import Universe to operator - individual resources
Summary: Implemented the OperatorImportResource task, along with each resource's specific import functionality. The resources implemented: 1. Secrets 2. Storage Config 3. Backup Schedule 4. Release 5. Universe Provider CRD is not yet merged, and backups have already been implemented. Backups are integrated to the import task though Test Plan: unit tests Reviewers: anijhawan, vkumar, anabaria, #yba-api-review Reviewed By: anijhawan, #yba-api-review Subscribers: yugaware Differential Revision: https://phorge.dev.yugabyte.com/D46338
1 parent 25f4d62 commit cc9e979

30 files changed

+2124
-359
lines changed

managed/build.sbt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ libraryDependencies ++= Seq(
242242
"io.fabric8" % "kubernetes-client" % "6.8.0",
243243
"io.fabric8" % "kubernetes-client-api" % "6.8.0",
244244
"io.fabric8" % "kubernetes-model" % "6.8.0",
245+
"io.fabric8" % "kubernetes-server-mock" % "6.8.0",
245246
"org.modelmapper" % "modelmapper" % "2.4.4",
246247
"com.datadoghq" % "datadog-api-client" % "2.25.0" classifier "shaded-jar",
247248
"javax.xml.bind" % "jaxb-api" % "2.3.1",
@@ -468,7 +469,7 @@ generateCrdObjects / fileInputs += baseDirectory.value.toGlob /
468469
generateCrdObjects := {
469470
if (generateCrdObjects.inputFileChanges.hasChanges) {
470471
ybLog("Generating crd classes...")
471-
val generatedSourcesDirectory = baseDirectory.value / "target/scala-2.13/"
472+
val generatedSourcesDirectory = baseDirectory.value / "target/operatorCRD/"
472473
val command = s"mvn generate-sources -DoutputDirectory=$generatedSourcesDirectory"
473474
val status = Process(command, baseDirectory.value / "src/main/java/com/yugabyte/yw/common/operator/").!
474475
status
@@ -477,6 +478,7 @@ generateCrdObjects := {
477478
0
478479
}
479480
}
481+
Compile / unmanagedSourceDirectories += baseDirectory.value / "target/operatorCRD/"
480482

481483
downloadThirdPartyDeps := {
482484
ybLog("Downloading third-party dependencies...")

managed/src/main/java/MainModule.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@
8484
import com.yugabyte.yw.common.operator.OperatorStatusUpdaterFactory;
8585
import com.yugabyte.yw.common.operator.YBInformerFactory;
8686
import com.yugabyte.yw.common.operator.YBReconcilerFactory;
87+
import com.yugabyte.yw.common.operator.utils.KubernetesClientFactory;
8788
import com.yugabyte.yw.common.operator.utils.OperatorUtils;
89+
import com.yugabyte.yw.common.operator.utils.UniverseImporter;
8890
import com.yugabyte.yw.common.rbac.PermissionUtil;
8991
import com.yugabyte.yw.common.rbac.RoleBindingUtil;
9092
import com.yugabyte.yw.common.rbac.RoleUtil;
@@ -94,7 +96,6 @@
9496
import com.yugabyte.yw.common.ybflyway.YBFlywayInit;
9597
import com.yugabyte.yw.controllers.MetricGrafanaController;
9698
import com.yugabyte.yw.controllers.PlatformHttpActionAdapter;
97-
import com.yugabyte.yw.controllers.handlers.OperatorResourceMigrateHandler;
9899
import com.yugabyte.yw.metrics.MetricQueryHelper;
99100
import com.yugabyte.yw.models.CertificateInfo;
100101
import com.yugabyte.yw.models.HealthCheck;
@@ -303,7 +304,8 @@ public void configure() {
303304
bind(ReleasesUtils.class).asEagerSingleton();
304305
bind(ReleaseContainerFactory.class).asEagerSingleton();
305306
bind(SoftwareUpgradeHelper.class).asEagerSingleton();
306-
bind(OperatorResourceMigrateHandler.class).asEagerSingleton();
307+
bind(KubernetesClientFactory.class).asEagerSingleton();
308+
bind(UniverseImporter.class).asEagerSingleton();
307309

308310
// Destroy current session on SSO logout.
309311
final LogoutController logoutController = new LogoutController();

managed/src/main/java/api/v2/controllers/UniverseApiControllerImp.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import api.v2.models.UniverseEditGFlags;
1919
import api.v2.models.UniverseEditKubernetesOverrides;
2020
import api.v2.models.UniverseEditSpec;
21+
import api.v2.models.UniverseOperatorImportReq;
2122
import api.v2.models.UniverseQueryLogsExport;
2223
import api.v2.models.UniverseResourceDetails;
2324
import api.v2.models.UniverseRestart;
@@ -220,4 +221,16 @@ public YBATask configureMetricsExport(
220221
Request request, UUID cUUID, UUID uniUUID, ConfigureMetricsExportSpec req) throws Exception {
221222
return universeUpgradeHandler.configureMetricsExport(request, cUUID, uniUUID, req);
222223
}
224+
225+
@Override
226+
public YBATask operatorImportUniverse(
227+
Request request, UUID cUUID, UUID uniUUID, UniverseOperatorImportReq req) throws Exception {
228+
return universeHandler.operatorImportUniverse(request, cUUID, uniUUID, req);
229+
}
230+
231+
@Override
232+
public void operatorImportUniversePrecheck(
233+
Request request, UUID cUUID, UUID uniUUID, UniverseOperatorImportReq req) throws Exception {
234+
universeHandler.precheckOperatorImportUniverse(request, cUUID, uniUUID, req);
235+
}
223236
}

managed/src/main/java/api/v2/handlers/UniverseManagementHandler.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import api.v2.models.UniverseCreateSpec;
2020
import api.v2.models.UniverseDeleteSpec;
2121
import api.v2.models.UniverseEditSpec;
22+
import api.v2.models.UniverseOperatorImportReq;
2223
import api.v2.models.UniverseSpec;
2324
import api.v2.models.YBATask;
2425
import api.v2.utils.ApiControllerUtils;
@@ -709,4 +710,76 @@ public api.v2.models.UniverseResourceDetails getUniverseResources(
709710
}
710711
return v2Response;
711712
}
713+
714+
public void precheckOperatorImportUniverse(
715+
Request request, UUID cUUID, UUID uniUUID, UniverseOperatorImportReq req) {
716+
Customer customer = Customer.getOrBadRequest(cUUID);
717+
Universe universe = Universe.getOrBadRequest(uniUUID, customer);
718+
719+
// validate the universe is kubernetes
720+
if (!Util.isKubernetesBasedUniverse(universe)) {
721+
log.error(
722+
"Universe {} is not a Kubernetes universe, cannot migrate to operator",
723+
universe.getName());
724+
throw new PlatformServiceException(BAD_REQUEST, "Universe is not a Kubernetes universe");
725+
}
726+
if (!confGetter.getGlobalConf(GlobalConfKeys.KubernetesOperatorEnabled)) {
727+
log.error("Operator is not enabled, cannot migrate universe {}", universe.getName());
728+
throw new PlatformServiceException(
729+
BAD_REQUEST,
730+
"Operator is not enabled. Please enable the runtime config"
731+
+ " 'yb.kubernetes.operator.enabled' and restart YBA");
732+
}
733+
734+
// Check if the namespace is the same as the operator namespace if it is set
735+
boolean migrateNamespaceSet = req.getNamespace() != null && !req.getNamespace().isEmpty();
736+
String operatorNamespace = confGetter.getGlobalConf(GlobalConfKeys.KubernetesOperatorNamespace);
737+
boolean operatorNamespaceSet = operatorNamespace != null && !operatorNamespace.isEmpty();
738+
if (migrateNamespaceSet
739+
&& operatorNamespaceSet
740+
&& !req.getNamespace().equals(operatorNamespace)) {
741+
log.error(
742+
"Namespace {} is not the same as the operator namespace {}",
743+
req.getNamespace(),
744+
operatorNamespace);
745+
throw new PlatformServiceException(
746+
BAD_REQUEST,
747+
"Namespace is not the same as the operator namespace. Please set the namespace to the"
748+
+ " same as the operator namespace");
749+
}
750+
751+
// Read Replicas clusters are not supported
752+
if (universe.getUniverseDetails().clusters.size() > 1) {
753+
log.error(
754+
"Universe {} has read-only clusters, cannot migrate to operator", universe.getName());
755+
throw new PlatformServiceException(BAD_REQUEST, "Universe has read-only clusters");
756+
}
757+
758+
// XCluster is not supported by operator
759+
if (!XClusterConfig.getByUniverseUuid(universe.getUniverseUUID()).isEmpty()) {
760+
log.error("Universe {} has xClusterInfo set, cannot migrate to operator", universe.getName());
761+
throw new PlatformServiceException(
762+
BAD_REQUEST, "Cannot migrate universes in an xcluster setup.");
763+
}
764+
765+
// AZ Level overrides are not supported by operator
766+
Map<String, String> azOverrides =
767+
universe.getUniverseDetails().getPrimaryCluster().userIntent.azOverrides;
768+
if (azOverrides != null && azOverrides.size() > 0) {
769+
log.error(
770+
"Universe {} has AZ level overrides set, cannot migrate to operator", universe.getName());
771+
throw new PlatformServiceException(
772+
BAD_REQUEST, "Cannot migrate universes with AZ level overrides.");
773+
}
774+
}
775+
776+
public YBATask operatorImportUniverse(
777+
Request request, UUID cUUID, UUID uniUUID, UniverseOperatorImportReq req) {
778+
Customer customer = Customer.getOrBadRequest(cUUID);
779+
Universe universe = Universe.getOrBadRequest(uniUUID, customer);
780+
// TODO: Implement the migration logic
781+
UUID taskUuid = UUID.randomUUID();
782+
YBATask ybaTask = new YBATask().taskUuid(taskUuid).resourceUuid(universe.getUniverseUUID());
783+
return ybaTask;
784+
}
712785
}

managed/src/main/java/com/yugabyte/yw/commissioner/tasks/OperatorImportUniverse.java

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.yugabyte.yw.commissioner.tasks;
22

3+
import static com.yugabyte.yw.commissioner.tasks.subtasks.OperatorImportResource.*;
34
import static play.mvc.Http.Status.BAD_REQUEST;
45

56
import com.google.inject.Inject;
@@ -11,8 +12,8 @@
1112
import com.yugabyte.yw.commissioner.tasks.subtasks.OperatorImportResource;
1213
import com.yugabyte.yw.common.PlatformServiceException;
1314
import com.yugabyte.yw.common.Util;
15+
import com.yugabyte.yw.common.operator.StorageConfigReconciler;
1416
import com.yugabyte.yw.forms.BackupRequestParams;
15-
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams.UserIntent;
1617
import com.yugabyte.yw.forms.UniverseTaskParams;
1718
import com.yugabyte.yw.models.Backup;
1819
import com.yugabyte.yw.models.Customer;
@@ -47,7 +48,8 @@ public OperatorImportUniverse(BaseTaskDependencies baseTaskDependencies) {
4748
}
4849

4950
public static class Params extends UniverseTaskParams {
50-
UUID universeUUID;
51+
// UniverseUUID is also required, but already defined in the base class.
52+
String namespace;
5153
}
5254

5355
@Override
@@ -71,9 +73,10 @@ private static interface ImportResourceTaskCreator {
7173

7274
@Override
7375
public void run() {
74-
Universe universe =
75-
lockAndFreezeUniverseForUpdate(
76-
taskParams().universeUUID, taskParams().expectedUniverseVersion, null);
76+
log.info(
77+
"Running OperatorImportUniverse task with universe UUID: {}",
78+
taskParams().getUniverseUUID());
79+
Universe universe = lockAndFreezeUniverseForUpdate(taskParams().getUniverseUUID(), -1, null);
7780

7881
for (ImportResourceTaskCreator creator : importResourceTaskCreators) {
7982
log.trace("creating subtaskGroup with creator {}", creator);
@@ -94,13 +97,17 @@ public void run() {
9497
}
9598

9699
private void createImportSecretSubtask(
97-
String secretName, String secretValue, SubTaskGroup group) {
100+
String secretName, String secretKey, String secretValue, SubTaskGroup group) {
98101
OperatorImportResource task = createTask(OperatorImportResource.class);
99102
OperatorImportResource.Params params = new OperatorImportResource.Params();
100103
params.secretName = secretName;
104+
params.secretKey = secretKey;
101105
params.secretValue = secretValue;
102106
params.resourceType = OperatorImportResource.Params.ResourceType.SECRET;
107+
params.namespace = taskParams().namespace;
108+
103109
initializeTask(group, task, params);
110+
log.trace("initialized task for secret");
104111
}
105112

106113
private void createImportStorageConfigSubtask(
@@ -118,24 +125,41 @@ private void createImportStorageConfigSubtask(
118125
String.format(
119126
"no storage config %s found or is not a storage config", storageConfigUUID));
120127
}
128+
129+
OperatorImportResource task = createTask(OperatorImportResource.class);
130+
OperatorImportResource.Params params = new OperatorImportResource.Params();
131+
params.storageConfigUUID = storageConfigUUID;
132+
params.resourceType = OperatorImportResource.Params.ResourceType.STORAGE_CONFIG;
133+
params.namespace = taskParams().namespace;
134+
121135
// Create Secret task(s)
122136
switch (storageConfig.getName()) {
123137
case Util.S3:
124138
CustomerConfigStorageS3Data s3Data =
125139
(CustomerConfigStorageS3Data) storageConfig.getDataObject();
126140
createImportSecretSubtask(
127-
"awsSecretAccessKeySecret", s3Data.awsSecretAccessKey, secretGroup);
141+
"awsSecretAccessKeySecret",
142+
StorageConfigReconciler.AWS_SECRET_ACCESS_KEY_SECRET_KEY,
143+
s3Data.awsSecretAccessKey,
144+
secretGroup);
128145
break;
129146
case Util.GCS:
130147
CustomerConfigStorageGCSData gcsData =
131148
(CustomerConfigStorageGCSData) storageConfig.getDataObject();
132149
createImportSecretSubtask(
133-
"gcsCredentialsJsonSecret", gcsData.gcsCredentialsJson, secretGroup);
150+
"gcsCredentialsJsonSecret",
151+
StorageConfigReconciler.GCS_CREDENTIALS_JSON_SECRET_KEY,
152+
gcsData.gcsCredentialsJson,
153+
secretGroup);
134154
break;
135155
case Util.AZ:
136156
CustomerConfigStorageAzureData azData =
137157
(CustomerConfigStorageAzureData) storageConfig.getDataObject();
138-
createImportSecretSubtask("azureStorageSasTokenSecret", azData.azureSasToken, secretGroup);
158+
createImportSecretSubtask(
159+
"azureStorageSasTokenSecret",
160+
StorageConfigReconciler.AZURE_STORAGE_SAS_TOKEN_SECRET_KEY,
161+
azData.azureSasToken,
162+
secretGroup);
139163
break;
140164
case Util.NFS:
141165
// No secrets for NFS
@@ -144,11 +168,8 @@ private void createImportStorageConfigSubtask(
144168
throw new RuntimeException("Unknown storage config type: " + storageConfig.getName());
145169
}
146170

147-
OperatorImportResource task = createTask(OperatorImportResource.class);
148-
OperatorImportResource.Params params = new OperatorImportResource.Params();
149-
params.storageConfigUUID = storageConfigUUID;
150-
params.resourceType = OperatorImportResource.Params.ResourceType.STORAGE_CONFIG;
151171
initializeTask(storageCfgGroup, task, params);
172+
log.trace("initialized task for storage config");
152173
}
153174

154175
private List<SubTaskGroup> createImportReleaseSubtasks(Universe universe) {
@@ -159,7 +180,9 @@ private List<SubTaskGroup> createImportReleaseSubtasks(Universe universe) {
159180
params.releaseVersion =
160181
universe.getUniverseDetails().getPrimaryCluster().userIntent.ybSoftwareVersion;
161182
params.resourceType = OperatorImportResource.Params.ResourceType.RELEASE;
183+
params.namespace = taskParams().namespace;
162184
initializeTask(group, task, params);
185+
log.trace("initialized task for release");
163186
return List.of(group);
164187
}
165188

@@ -174,35 +197,27 @@ private List<SubTaskGroup> createImportProviderSubtasks(Universe universe) {
174197
params.providerUUID =
175198
UUID.fromString(universe.getUniverseDetails().getPrimaryCluster().userIntent.provider);
176199
params.resourceType = OperatorImportResource.Params.ResourceType.PROVIDER;
200+
params.namespace = taskParams().namespace;
177201
initializeTask(group, task, params);
202+
log.trace("initialized task for provider");
178203
return List.of(group);
179204
}
180205

181206
private List<SubTaskGroup> createImportUniverseSubtasks(Universe universe) {
182207
List<SubTaskGroup> groups = new ArrayList<>();
183-
String secretName;
184-
UserIntent userIntent = universe.getUniverseDetails().getPrimaryCluster().userIntent;
185-
SubTaskGroup secretGroup =
186-
createSubTaskGroup(UniverseImportSecretTaskName, SubTaskGroupType.OperatorImportResource);
187-
if (userIntent.isYSQLAuthEnabled()) {
188-
secretName = universe.getName() + "-ysqlpassword";
189-
createImportSecretSubtask(secretName, userIntent.ysqlPassword, secretGroup);
190-
}
191-
if (userIntent.enableYCQLAuth) {
192-
secretName = universe.getName() + "-ycqlpassword";
193-
createImportSecretSubtask(secretName, userIntent.ycqlPassword, secretGroup);
194-
}
195-
if (secretGroup.getSubTaskCount() > 0) {
196-
groups.add(secretGroup);
197-
}
208+
209+
// Create universe params first so we can reference them when creating secrets
210+
OperatorImportResource.Params universeParams = new OperatorImportResource.Params();
211+
universeParams.universeUUID = universe.getUniverseUUID();
212+
universeParams.resourceType = OperatorImportResource.Params.ResourceType.UNIVERSE;
213+
universeParams.namespace = taskParams().namespace;
214+
198215
SubTaskGroup group =
199216
createSubTaskGroup("ImportUniverse", SubTaskGroupType.OperatorImportResource);
200217
groups.add(group);
201218
OperatorImportResource task = createTask(OperatorImportResource.class);
202-
OperatorImportResource.Params params = new OperatorImportResource.Params();
203-
params.universeUUID = universe.getUniverseUUID();
204-
params.resourceType = OperatorImportResource.Params.ResourceType.UNIVERSE;
205-
initializeTask(group, task, params);
219+
initializeTask(group, task, universeParams);
220+
log.trace("initialized task for universe");
206221
return groups;
207222
}
208223

@@ -222,14 +237,20 @@ private List<SubTaskGroup> createImportBackupSchedulesSubtasks(Universe universe
222237
// Create tasks for migrating storage configs
223238
BackupRequestParams backupParams =
224239
Json.mapper().convertValue(schedule.getTaskParams(), BackupRequestParams.class);
225-
createImportStorageConfigSubtask(backupParams.scheduleUUID, storageCfgGroup, secretGroup);
240+
createImportStorageConfigSubtask(
241+
backupParams.storageConfigUUID, storageCfgGroup, secretGroup);
226242

227243
// Now migrate the actual schedule.
228244
OperatorImportResource task = createTask(OperatorImportResource.class);
229245
OperatorImportResource.Params params = new OperatorImportResource.Params();
230246
params.backupScheduleUUID = schedule.getScheduleUUID();
231247
params.resourceType = OperatorImportResource.Params.ResourceType.BACKUP_SCHEDULE;
248+
params.namespace = taskParams().namespace;
249+
250+
// Add any secrets from storage configs to this backup schedule's secret map
251+
// The storage config subtask will have populated the secret map
232252
initializeTask(group, task, params);
253+
log.trace("initialized task for backup schedule");
233254
});
234255
return List.of(secretGroup, storageCfgGroup, group);
235256
}
@@ -254,7 +275,12 @@ private List<SubTaskGroup> createImportBackupsSubtasks(Universe universe) {
254275
OperatorImportResource.Params params = new OperatorImportResource.Params();
255276
params.backupUUID = backup.getBackupUUID();
256277
params.resourceType = OperatorImportResource.Params.ResourceType.BACKUP;
278+
params.namespace = taskParams().namespace;
279+
params.customerUUID = customer.getUuid();
280+
// Add any secrets from storage configs to this backup's secret map
281+
// The storage config subtask will have populated the secret map
257282
initializeTask(group, task, params);
283+
log.trace("initialized task for backup");
258284
});
259285
return List.of(group);
260286
}

0 commit comments

Comments
 (0)