This is an automated email from the ASF dual-hosted git repository.
lahirujayathilake pushed a commit to branch airavata-aws
in repository https://gitbox.apache.org/repos/asf/airavata.git
The following commit(s) were added to refs/heads/airavata-aws by this push:
new 0a09a41e26 Implement AWS task factory, EC2 instance creation workflow,
and migration script
0a09a41e26 is described below
commit 0a09a41e2614d3500baa9c327ea85ee8dccc1eb2
Author: lahiruj <[email protected]>
AuthorDate: Thu Jun 26 19:16:47 2025 -0400
Implement AWS task factory, EC2 instance creation workflow, and migration
script
---
modules/airavata-helix/helix-spectator/pom.xml | 5 +
.../airavata/helix/impl/task/AWSTaskFactory.java | 69 ++++++++
.../airavata/helix/impl/task/SlurmTaskFactory.java | 18 +--
.../airavata/helix/impl/task/TaskFactory.java | 1 +
.../impl/task/aws/AWSProcessContextManager.java | 116 ++++++++++++++
.../helix/impl/task/aws/CreateEC2InstanceTask.java | 174 +++++++++++++++++++++
.../init/06-cloud-execution-support-migration.sql | 18 ++-
.../AWSGroupComputeResourcePrefEntity.java | 73 +++++++++
.../appcatalog/GrpComputePrefRepository.java | 15 ++
.../src/main/resources/META-INF/persistence.xml | 1 +
.../group_resource_profile_model.thrift | 11 ++
11 files changed, 490 insertions(+), 11 deletions(-)
diff --git a/modules/airavata-helix/helix-spectator/pom.xml
b/modules/airavata-helix/helix-spectator/pom.xml
index bc4a092282..d6e4b060aa 100644
--- a/modules/airavata-helix/helix-spectator/pom.xml
+++ b/modules/airavata-helix/helix-spectator/pom.xml
@@ -85,6 +85,11 @@ under the License.
<artifactId>job-monitor-api</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>software.amazon.awssdk</groupId>
+ <artifactId>ec2</artifactId>
+ <version>2.31.70</version>
+ </dependency>
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
diff --git
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/AWSTaskFactory.java
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/AWSTaskFactory.java
new file mode 100644
index 0000000000..a121d50e5a
--- /dev/null
+++
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/AWSTaskFactory.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.airavata.helix.impl.task;
+
+import org.apache.airavata.helix.impl.task.aws.CreateEC2InstanceTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AWSTaskFactory implements HelixTaskFactory {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(AWSTaskFactory.class);
+
+ @Override
+ public AiravataTask createEnvSetupTask(String processId) {
+ LOGGER.info("Creating AWS CreateEc2InstanceTask for process {}...",
processId);
+ return new CreateEC2InstanceTask();
+ }
+
+ @Override
+ public AiravataTask createInputDataStagingTask(String processId) {
+ return null;
+ }
+
+ @Override
+ public AiravataTask createJobSubmissionTask(String processId) {
+ return null;
+ }
+
+ @Override
+ public AiravataTask createOutputDataStagingTask(String processId) {
+ return null;
+ }
+
+ @Override
+ public AiravataTask createArchiveTask(String processId) {
+ return null;
+ }
+
+ @Override
+ public AiravataTask createJobVerificationTask(String processId) {
+ return null;
+ }
+
+ @Override
+ public AiravataTask createCompletingTask(String processId) {
+ return null;
+ }
+
+ @Override
+ public AiravataTask createParsingTriggeringTask(String processId) {
+ return null;
+ }
+}
diff --git
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/SlurmTaskFactory.java
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/SlurmTaskFactory.java
index b13a4a276c..8726b197a3 100644
---
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/SlurmTaskFactory.java
+++
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/SlurmTaskFactory.java
@@ -31,53 +31,53 @@ import org.slf4j.LoggerFactory;
public class SlurmTaskFactory implements HelixTaskFactory {
- private static final Logger logger =
LoggerFactory.getLogger(SlurmTaskFactory.class);
+ private static final Logger LOGGER =
LoggerFactory.getLogger(SlurmTaskFactory.class);
@Override
public AiravataTask createEnvSetupTask(String processId) {
- logger.info("Creating Slurm EnvSetupTask for process {}...",
processId);
+ LOGGER.info("Creating Slurm EnvSetupTask for process {}...",
processId);
return new EnvSetupTask();
}
@Override
public AiravataTask createInputDataStagingTask(String processId) {
- logger.info("Creating Slurm InputDataStagingTask for process {}...",
processId);
+ LOGGER.info("Creating Slurm InputDataStagingTask for process {}...",
processId);
return new InputDataStagingTask();
}
@Override
public AiravataTask createJobSubmissionTask(String processId) {
- logger.info("Creating Slurm DefaultJobSubmissionTask for process
{}...", processId);
+ LOGGER.info("Creating Slurm DefaultJobSubmissionTask for process
{}...", processId);
return new DefaultJobSubmissionTask();
}
@Override
public AiravataTask createOutputDataStagingTask(String processId) {
- logger.info("Creating Slurm OutputDataStagingTask for process {}...",
processId);
+ LOGGER.info("Creating Slurm OutputDataStagingTask for process {}...",
processId);
return new OutputDataStagingTask();
}
@Override
public AiravataTask createArchiveTask(String processId) {
- logger.info("Creating Slurm ArchiveTask for process {}...", processId);
+ LOGGER.info("Creating Slurm ArchiveTask for process {}...", processId);
return new ArchiveTask();
}
@Override
public AiravataTask createJobVerificationTask(String processId) {
- logger.info("Creating Slurm JobVerificationTask for process {}...",
processId);
+ LOGGER.info("Creating Slurm JobVerificationTask for process {}...",
processId);
return new JobVerificationTask();
}
@Override
public AiravataTask createCompletingTask(String processId) {
- logger.info("Creating Slurm CompletingTask for process {}...",
processId);
+ LOGGER.info("Creating Slurm CompletingTask for process {}...",
processId);
return new CompletingTask();
}
@Override
public AiravataTask createParsingTriggeringTask(String processId) {
- logger.info("Creating Slurm ParsingTriggeringTask for process {}...",
processId);
+ LOGGER.info("Creating Slurm ParsingTriggeringTask for process {}...",
processId);
return new ParsingTriggeringTask();
}
}
diff --git
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/TaskFactory.java
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/TaskFactory.java
index 1cd0ca6549..c581d79431 100644
---
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/TaskFactory.java
+++
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/TaskFactory.java
@@ -29,6 +29,7 @@ public class TaskFactory {
static {
FACTORIES.put(ResourceType.SLURM, new SlurmTaskFactory());
+ FACTORIES.put(ResourceType.AWS, new AWSTaskFactory());
}
public static HelixTaskFactory getFactory(ResourceType type) {
diff --git
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/aws/AWSProcessContextManager.java
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/aws/AWSProcessContextManager.java
new file mode 100644
index 0000000000..66c0bf3578
--- /dev/null
+++
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/aws/AWSProcessContextManager.java
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.airavata.helix.impl.task.aws;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.airavata.agents.api.AgentException;
+import org.apache.airavata.agents.api.AgentUtils;
+import org.apache.airavata.helix.impl.task.TaskContext;
+import org.apache.airavata.model.process.ProcessModel;
+import org.apache.airavata.registry.api.RegistryService;
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Manage AWS context
+ */
+public class AWSProcessContextManager {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(AWSProcessContextManager.class);
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private static final String AWS_INSTANCE_ID_KEY = "AWS_INSTANCE_ID";
+ private static final String AWS_SECURITY_GROUP_ID_KEY =
"AWS_SECURITY_GROUP_ID";
+ private static final String AWS_KEY_PAIR_NAME_KEY = "AWS_KEY_PAIR_NAME";
+ private static final String AWS_SSH_CREDENTIAL_TOKEN =
"AWS_SSH_CREDENTIAL_TOKEN";
+
+
+ private final RegistryService.Client registryClient;
+ private final TaskContext taskContext;
+ private final String processId;
+
+ public AWSProcessContextManager(TaskContext taskContext) {
+ try {
+ this.registryClient = AgentUtils.getRegistryServiceClient();
+ this.taskContext = taskContext;
+ this.processId = taskContext.getProcessId();
+ LOGGER.info("Initialized AWSProcessContextManager for process {}",
processId);
+
+ } catch (AgentException e) {
+ LOGGER.error("Failed to initialize AWSProcessContextManager", e);
+ throw new RuntimeException("Failed to initialize
AWSProcessContextManager", e);
+ }
+ }
+
+ public String getInstanceId() throws IOException {
+ return getContextMap().get(AWS_INSTANCE_ID_KEY);
+ }
+
+ public void saveInstanceId(String instanceId) throws TException,
IOException {
+ updateContext(AWS_INSTANCE_ID_KEY, instanceId);
+ }
+
+ public String getSecurityGroupId() throws IOException {
+ return getContextMap().get(AWS_SECURITY_GROUP_ID_KEY);
+ }
+
+ public void saveSecurityGroupId(String securityGroupId) throws TException,
IOException {
+ updateContext(AWS_SECURITY_GROUP_ID_KEY, securityGroupId);
+ }
+
+ public String getKeyPairName() throws IOException {
+ return getContextMap().get(AWS_KEY_PAIR_NAME_KEY);
+ }
+
+ public void saveKeyPairName(String keyPairName) throws TException,
IOException {
+ updateContext(AWS_KEY_PAIR_NAME_KEY, keyPairName);
+ }
+
+ public String getSSHCredentialToken() throws IOException {
+ return getContextMap().get(AWS_SSH_CREDENTIAL_TOKEN);
+ }
+
+ public void saveSSHCredentialToken(String credentialToken) throws
TException, IOException {
+ updateContext(AWS_SSH_CREDENTIAL_TOKEN, credentialToken);
+ }
+
+ private Map<String, String> getContextMap() throws IOException {
+ String jsonContext = taskContext.getProcessModel().getProcessDetail();
+ if (jsonContext == null || jsonContext.isEmpty()) {
+ return new HashMap<>();
+ }
+ return MAPPER.readValue(jsonContext, new TypeReference<>() {
+ });
+ }
+
+ private void updateContext(String key, String value) throws TException,
IOException {
+ Map<String, String> contextMap = getContextMap();
+ contextMap.put(key, value);
+ ProcessModel processModel = taskContext.getProcessModel();
+ processModel.setProcessDetail(MAPPER.writeValueAsString(contextMap));
+ registryClient.updateProcess(processModel, processId);
+ LOGGER.info("Updated process detail for process {} with key '{}'",
processId, key);
+ }
+}
diff --git
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/aws/CreateEC2InstanceTask.java
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/aws/CreateEC2InstanceTask.java
new file mode 100644
index 0000000000..303e7051e5
--- /dev/null
+++
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/aws/CreateEC2InstanceTask.java
@@ -0,0 +1,174 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.airavata.helix.impl.task.aws;
+
+import org.apache.airavata.agents.api.AgentUtils;
+import org.apache.airavata.helix.impl.task.AiravataTask;
+import org.apache.airavata.helix.impl.task.TaskContext;
+import org.apache.airavata.helix.task.api.TaskHelper;
+import
org.apache.airavata.model.appcatalog.groupresourceprofile.AwsComputeResourcePreference;
+import org.apache.airavata.model.credential.store.PasswordCredential;
+import org.apache.airavata.model.credential.store.SSHCredential;
+import org.apache.helix.task.TaskResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.ec2.Ec2Client;
+import software.amazon.awssdk.services.ec2.model.CreateKeyPairResponse;
+import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupResponse;
+import software.amazon.awssdk.services.ec2.model.InstanceType;
+import software.amazon.awssdk.services.ec2.model.RunInstancesRequest;
+import software.amazon.awssdk.services.ec2.model.RunInstancesResponse;
+
+import java.util.UUID;
+
+/**
+ * Create all required AWS resources (SecurityGroup, KeyPair) and launches an
EC2 instance
+ */
+public class CreateEC2InstanceTask extends AiravataTask {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(CreateEC2InstanceTask.class);
+
+ @Override
+ public TaskResult onRun(TaskHelper helper, TaskContext taskContext) {
+ LOGGER.info("Starting Create EC2 Instance Task for process {}",
getProcessId());
+
+ AWSProcessContextManager awsContext = new
AWSProcessContextManager(taskContext);
+ Ec2Client ec2Client = null;
+
+ try {
+ AwsComputeResourcePreference awsPrefs =
taskContext.getGroupComputeResourcePreference().getSpecificPreferences().getAws();
+ String credentialToken =
taskContext.getGroupComputeResourcePreference().getResourceSpecificCredentialStoreToken();
+
+ ec2Client = buildEc2Client(credentialToken, getGatewayId(),
awsPrefs.getRegion());
+ LOGGER.info("Successfully built EC2 client for region {}",
awsPrefs.getRegion());
+
+ String securityGroupId = createSecurityGroup(ec2Client);
+ awsContext.saveSecurityGroupId(securityGroupId);
+ LOGGER.info("Created and saved security group: {}",
securityGroupId);
+
+ String keyPairName = "airavata-key-" + getProcessId();
+ CreateKeyPairResponse kpRes = ec2Client.createKeyPair(req ->
req.keyName(keyPairName));
+ awsContext.saveKeyPairName(keyPairName);
+
+ String sshCredentialToken = saveSSHCredential(kpRes.keyMaterial());
+ awsContext.saveSSHCredentialToken(sshCredentialToken);
+ LOGGER.info("Created key pair {} and saved credential with token
{}", keyPairName, sshCredentialToken);
+
+ RunInstancesRequest runRequest = RunInstancesRequest.builder()
+ .imageId(awsPrefs.getPreferredAmiId())
+
.instanceType(InstanceType.fromValue(awsPrefs.getPreferredInstanceType()))
+ .keyName(keyPairName)
+ .securityGroupIds(securityGroupId)
+ .minCount(1)
+ .maxCount(1)
+ .build();
+ RunInstancesResponse runResponse =
ec2Client.runInstances(runRequest);
+
+ if (runResponse.instances() == null ||
runResponse.instances().isEmpty()) {
+ LOGGER.error("No instances were launched by AWS even after
successful SDK call");
+ throw new Exception("No instances were launched by AWS even
after successful SDK call");
+ }
+
+ String instanceId = runResponse.instances().get(0).instanceId();
+ awsContext.saveInstanceId(instanceId);
+ LOGGER.info("Successfully launched EC2 instance {}", instanceId);
+
+ return new TaskResult(TaskResult.Status.COMPLETED, "Launched EC2
instance " + instanceId);
+
+ } catch (Exception e) {
+ // TODO catch for AMI issues, etc
+ LOGGER.error("Error creating EC2 instance for process {}",
getProcessId(), e);
+ onCancel(taskContext);
+ return new TaskResult(TaskResult.Status.FATAL_FAILED,
e.getMessage());
+
+ } finally {
+ if (ec2Client != null) {
+ ec2Client.close();
+ }
+ }
+ }
+
+ @Override
+ public void onCancel(TaskContext taskContext) {
+ LOGGER.warn("Cleaning up resources for failed CreateEC2InstanceTask,
process: {}", getProcessId());
+ try {
+ AWSProcessContextManager awsContext = new
AWSProcessContextManager(taskContext);
+ AwsComputeResourcePreference awsPrefs =
taskContext.getGroupComputeResourcePreference().getSpecificPreferences().getAws();
+ String credentialToken =
taskContext.getGroupComputeResourcePreference().getResourceSpecificCredentialStoreToken();
+
+ try (Ec2Client ec2Client = buildEc2Client(credentialToken,
getGatewayId(), awsPrefs.getRegion())) {
+ String sgId = awsContext.getSecurityGroupId();
+ String keyName = awsContext.getKeyPairName();
+
+ if (sgId != null) {
+ LOGGER.warn("Deleting security group: {}", sgId);
+ ec2Client.deleteSecurityGroup(req -> req.groupId(sgId));
+ }
+ if (keyName != null) {
+ LOGGER.warn("Deleting key pair: {}", keyName);
+ ec2Client.deleteKeyPair(req -> req.keyName(keyName));
+ }
+
+
AgentUtils.getCredentialClient().deleteSSHCredential(awsContext.getSSHCredentialToken(),
getGatewayId());
+ }
+ } catch (Exception e) {
+ LOGGER.error("Failed to cleanup resources during onCancel. Manual
intervention may be required.", e);
+ }
+ }
+
+ private Ec2Client buildEc2Client(String token, String gatewayId, String
region) throws Exception {
+ PasswordCredential pwdCred =
AgentUtils.getCredentialClient().getPasswordCredential(token, gatewayId);
+ AwsBasicCredentials awsCreds =
AwsBasicCredentials.create(pwdCred.getLoginUserName(), pwdCred.getPassword());
// TODO support using AWS Credential
+ return Ec2Client.builder()
+ .region(Region.of(region))
+
.credentialsProvider(StaticCredentialsProvider.create(awsCreds))
+ .build();
+ }
+
+ // Save the generated aws private key in Airavata Credential Store
+ private String saveSSHCredential(String privateKey) throws Exception {
+ SSHCredential credential = new SSHCredential();
+ credential.setGatewayId(getGatewayId());
+ credential.setToken(UUID.randomUUID().toString());
+ credential.setPrivateKey(privateKey);
+ credential.setUsername(getProcessModel().getUserName());
+
+ String savedToken =
AgentUtils.getCredentialClient().addSSHCredential(credential);
+ LOGGER.info("Successfully saved temporary SSH credential with token
{}", savedToken);
+
+ return savedToken;
+ }
+
+ private String createSecurityGroup(Ec2Client ec2) throws Exception {
+ String vpcId = ec2.describeVpcs(req -> req.filters(f ->
f.name("is-default").values("true"))).vpcs().get(0).vpcId();
+ CreateSecurityGroupResponse sgRes = ec2.createSecurityGroup(req -> req
+ .groupName("airavata-sg-" + getProcessId())
+ .description("Airavata temporary security group for " +
getProcessId())
+ .vpcId(vpcId));
+
+ ec2.authorizeSecurityGroupIngress(req -> req
+ .groupId(sgRes.groupId())
+ .ipPermissions(p ->
p.ipProtocol("tcp").fromPort(22).toPort(22).ipRanges(r ->
r.cidrIp("0.0.0.0/0")))); // TODO restrict the IP
+
+ return sgRes.groupId();
+ }
+}
diff --git
a/modules/ide-integration/src/main/containers/database_scripts/init/06-cloud-execution-support-migration.sql
b/modules/ide-integration/src/main/containers/database_scripts/init/06-cloud-execution-support-migration.sql
index 4da86e8141..bc2e7c1cab 100644
---
a/modules/ide-integration/src/main/containers/database_scripts/init/06-cloud-execution-support-migration.sql
+++
b/modules/ide-integration/src/main/containers/database_scripts/init/06-cloud-execution-support-migration.sql
@@ -57,7 +57,6 @@ SELECT
`SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO`
FROM `GROUP_COMPUTE_RESOURCE_PREFERENCE`;
-
-- Drop the Slurm-specific columns from the base table
ALTER TABLE `GROUP_COMPUTE_RESOURCE_PREFERENCE`
DROP COLUMN `PREFERED_BATCH_QUEUE`,
@@ -69,4 +68,19 @@ DROP COLUMN `PREFERED_BATCH_QUEUE`,
DROP COLUMN `RESERVATION_START_TIME`,
DROP COLUMN `RESERVATION_END_TIME`,
DROP COLUMN `SSH_ACCOUNT_PROVISIONER`,
- DROP COLUMN `SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO`;
\ No newline at end of file
+ DROP COLUMN `SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO`;
+
+-- Create the AWS-specific group compute preference table
+CREATE TABLE `AWS_GROUP_COMPUTE_RESOURCE_PREFERENCE`
+(
+ `RESOURCE_ID` VARCHAR(255) NOT NULL,
+ `GROUP_RESOURCE_PROFILE_ID` VARCHAR(255) NOT NULL,
+ `AWS_REGION` VARCHAR(255) NOT NULL,
+ `PREFERRED_AMI_ID` VARCHAR(255) NOT NULL,
+ `PREFERRED_INSTANCE_TYPE` VARCHAR(255) NOT NULL,
+
+ PRIMARY KEY (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`),
+ CONSTRAINT `FK_AWS_PREF_TO_BASE` FOREIGN KEY (`RESOURCE_ID`,
`GROUP_RESOURCE_PROFILE_ID`)
+ REFERENCES `GROUP_COMPUTE_RESOURCE_PREFERENCE` (`RESOURCE_ID`,
`GROUP_RESOURCE_PROFILE_ID`)
+ ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
\ No newline at end of file
diff --git
a/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/entities/appcatalog/AWSGroupComputeResourcePrefEntity.java
b/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/entities/appcatalog/AWSGroupComputeResourcePrefEntity.java
new file mode 100644
index 0000000000..b7675f7518
--- /dev/null
+++
b/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/entities/appcatalog/AWSGroupComputeResourcePrefEntity.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.airavata.registry.core.entities.appcatalog;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.PrimaryKeyJoinColumn;
+import jakarta.persistence.PrimaryKeyJoinColumns;
+import jakarta.persistence.Table;
+
+/**
+ * The persistent class for the aws_group_compute_resource_preference database
table.
+ */
+@Entity
+@Table(name = "AWS_GROUP_COMPUTE_RESOURCE_PREFERENCE")
+@PrimaryKeyJoinColumns({
+ @PrimaryKeyJoinColumn(name = "RESOURCE_ID", referencedColumnName =
"RESOURCE_ID"),
+ @PrimaryKeyJoinColumn(name = "GROUP_RESOURCE_PROFILE_ID",
referencedColumnName = "GROUP_RESOURCE_PROFILE_ID")
+})
+public class AWSGroupComputeResourcePrefEntity extends
GroupComputeResourcePrefEntity {
+
+ @Column(name = "AWS_REGION", nullable = false)
+ private String region;
+
+ @Column(name = "PREFERRED_AMI_ID", nullable = false)
+ private String preferredAmiId;
+
+ @Column(name = "PREFERRED_INSTANCE_TYPE", nullable = false)
+ private String preferredInstanceType;
+
+ public AWSGroupComputeResourcePrefEntity() {
+ }
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ public String getPreferredAmiId() {
+ return preferredAmiId;
+ }
+
+ public void setPreferredAmiId(String preferredAmiId) {
+ this.preferredAmiId = preferredAmiId;
+ }
+
+ public String getPreferredInstanceType() {
+ return preferredInstanceType;
+ }
+
+ public void setPreferredInstanceType(String preferredInstanceType) {
+ this.preferredInstanceType = preferredInstanceType;
+ }
+}
diff --git
a/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/repositories/appcatalog/GrpComputePrefRepository.java
b/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/repositories/appcatalog/GrpComputePrefRepository.java
index 41ec472ed2..7a6846106c 100644
---
a/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/repositories/appcatalog/GrpComputePrefRepository.java
+++
b/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/repositories/appcatalog/GrpComputePrefRepository.java
@@ -18,10 +18,12 @@
*/
package org.apache.airavata.registry.core.repositories.appcatalog;
+import
org.apache.airavata.model.appcatalog.groupresourceprofile.AwsComputeResourcePreference;
import
org.apache.airavata.model.appcatalog.groupresourceprofile.EnvironmentSpecificPreferences;
import
org.apache.airavata.model.appcatalog.groupresourceprofile.GroupComputeResourcePreference;
import org.apache.airavata.model.appcatalog.groupresourceprofile.ResourceType;
import
org.apache.airavata.model.appcatalog.groupresourceprofile.SlurmComputeResourcePreference;
+import
org.apache.airavata.registry.core.entities.appcatalog.AWSGroupComputeResourcePrefEntity;
import
org.apache.airavata.registry.core.entities.appcatalog.GroupComputeResourcePrefEntity;
import
org.apache.airavata.registry.core.entities.appcatalog.GroupComputeResourcePrefPK;
import
org.apache.airavata.registry.core.entities.appcatalog.SlurmGroupComputeResourcePrefEntity;
@@ -65,6 +67,19 @@ public class GrpComputePrefRepository
EnvironmentSpecificPreferences esp = new
EnvironmentSpecificPreferences();
esp.setSlurm(scrp);
pref.setSpecificPreferences(esp);
+
+ } else if (ent instanceof AWSGroupComputeResourcePrefEntity aws) {
+ pref.setResourceType(ResourceType.AWS);
+
+ AwsComputeResourcePreference awsPref = new
AwsComputeResourcePreference();
+ awsPref.setRegion(aws.getRegion());
+ awsPref.setPreferredAmiId(aws.getPreferredAmiId());
+ awsPref.setPreferredInstanceType(aws.getPreferredInstanceType());
+
+ EnvironmentSpecificPreferences esp = new
EnvironmentSpecificPreferences();
+ esp.setAws(awsPref);
+ pref.setSpecificPreferences(esp);
+ return pref;
}
return pref;
diff --git
a/modules/registry/registry-core/src/main/resources/META-INF/persistence.xml
b/modules/registry/registry-core/src/main/resources/META-INF/persistence.xml
index bb5b7af886..53903e91f8 100644
--- a/modules/registry/registry-core/src/main/resources/META-INF/persistence.xml
+++ b/modules/registry/registry-core/src/main/resources/META-INF/persistence.xml
@@ -67,6 +67,7 @@
<class>org.apache.airavata.registry.core.entities.appcatalog.ComputeResourcePolicyEntity</class>
<class>org.apache.airavata.registry.core.entities.appcatalog.GroupComputeResourcePrefEntity</class>
<class>org.apache.airavata.registry.core.entities.appcatalog.SlurmGroupComputeResourcePrefEntity</class>
+
<class>org.apache.airavata.registry.core.entities.appcatalog.AWSGroupComputeResourcePrefEntity</class>
<class>org.apache.airavata.registry.core.entities.appcatalog.GroupSSHAccountProvisionerConfig</class>
<class>org.apache.airavata.registry.core.entities.appcatalog.GroupResourceProfileEntity</class>
<class>org.apache.airavata.registry.core.entities.appcatalog.ModuleLoadCmdEntity</class>
diff --git
a/thrift-interface-descriptions/data-models/group_resource_profile_model.thrift
b/thrift-interface-descriptions/data-models/group_resource_profile_model.thrift
index 1f4f04a77e..357d3fbe61 100644
---
a/thrift-interface-descriptions/data-models/group_resource_profile_model.thrift
+++
b/thrift-interface-descriptions/data-models/group_resource_profile_model.thrift
@@ -44,6 +44,7 @@ struct ComputeResourceReservation {
enum ResourceType {
SLURM = 0,
+ AWS = 1,
}
struct SlurmComputeResourcePreference {
@@ -58,8 +59,18 @@ struct SlurmComputeResourcePreference {
9: optional list<ComputeResourceReservation> reservations
}
+struct AwsComputeResourcePreference {
+ 1: optional string preferredAmiId,
+ 2: optional string preferredInstanceType,
+ 3: optional string region,
+ 4: optional string securityGroupId,
+ 5: optional string keyPairName,
+ 6: optional i64 maxStartupTime,
+}
+
union EnvironmentSpecificPreferences {
1: SlurmComputeResourcePreference slurm,
+ 2: AwsComputeResourcePreference aws,
}
struct GroupComputeResourcePreference {