Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

AzureCreateVirtualMachineTask: introduce new task #346

Merged
merged 3 commits into from
May 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions prebuilt-tasks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<attlasian-fugue.version>2.6.1</attlasian-fugue.version>
<org.json.version>20230227</org.json.version>
<jgit-version>6.5.0.202303070854-r</jgit-version>
<azure-core-version>1.37.0</azure-core-version>
<azure-identity-version>1.8.1</azure-identity-version>
<azure-resourcemanager-version>2.24.0</azure-resourcemanager-version>
</properties>

<repositories>
Expand Down Expand Up @@ -134,6 +137,23 @@
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Start libraries for Azure access -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
<version>${azure-core-version}</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>${azure-identity-version}</version>
</dependency>
<dependency>
<groupId>com.azure.resourcemanager</groupId>
<artifactId>azure-resourcemanager</artifactId>
<version>${azure-resourcemanager-version}</version>
</dependency>
<!-- End libraries for Azure access -->
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.redhat.parodos.tasks.azure;

import java.util.List;

import com.azure.resourcemanager.compute.models.AvailabilitySet;
import com.azure.resourcemanager.compute.models.VirtualMachine;
import com.azure.resourcemanager.network.models.Network;
import com.azure.resourcemanager.network.models.NetworkInterface;
import com.azure.resourcemanager.network.models.PublicIpAddress;
import com.azure.resourcemanager.resources.models.ResourceGroup;
import com.redhat.parodos.workflow.exception.MissingParameterException;
import com.redhat.parodos.workflow.parameter.WorkParameter;
import com.redhat.parodos.workflow.parameter.WorkParameterType;
import com.redhat.parodos.workflow.task.enums.WorkFlowTaskOutput;
import com.redhat.parodos.workflow.task.infrastructure.BaseInfrastructureWorkFlowTask;
import com.redhat.parodos.workflows.work.DefaultWorkReport;
import com.redhat.parodos.workflows.work.WorkContext;
import com.redhat.parodos.workflows.work.WorkReport;
import com.redhat.parodos.workflows.work.WorkStatus;
import com.sun.jdi.InternalException;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AzureCreateVirtualMachineTask extends BaseInfrastructureWorkFlowTask {
masayag marked this conversation as resolved.
Show resolved Hide resolved

static final String VM_USER_NAME_KEY = "vm-user-name";
static final String VM_SSH_PUBLIC_KEY_KEY = "vm-ssh-public-key";
static final String AZURE_TENANT_ID_KEY = "azure-tenant-id";
static final String AZURE_SUBSCRIPTION_ID_KEY = "azure-subscription-id";
static final String AZURE_CLIENT_ID_KEY = "azure-client-id";
static final String AZURE_CLIENT_SECRET_KEY = "azure-client-secret";
static final String AZURE_RESOURCES_PREFIX_KEY = "azure-resources-prefix";

private AzureResourceClient azureResourceClient;

public AzureCreateVirtualMachineTask() {
this(new AzureResourceClientImpl());
}

AzureCreateVirtualMachineTask(AzureResourceClient azureResourceClient) {
this.azureResourceClient = azureResourceClient;
}

@Override
public @NonNull List<WorkParameter> getWorkFlowTaskParameters() {
return List.of(
WorkParameter.builder().key(VM_USER_NAME_KEY).description("The user name for the Virtual Machine login")
.type(WorkParameterType.TEXT).optional(false).build(),
WorkParameter.builder().key(VM_SSH_PUBLIC_KEY_KEY)
.description("The SSH public key for the Virtual Machine login").type(WorkParameterType.TEXT)
.optional(false).build(),
WorkParameter.builder().key(AZURE_TENANT_ID_KEY)
.description("The unique identifier of the Azure Active Directory instance")
.type(WorkParameterType.TEXT).optional(false).build(),
WorkParameter.builder().key(AZURE_SUBSCRIPTION_ID_KEY)
.description("The GUID that uniquely identifies your subscription to use Azure services")
.type(WorkParameterType.TEXT).optional(false).build(),
WorkParameter.builder().key(AZURE_CLIENT_ID_KEY).description(
"The unique Application (client) ID assigned to your app by Azure AD when the app was registered")
.type(WorkParameterType.TEXT).optional(false).build(),
WorkParameter.builder().key(AZURE_CLIENT_SECRET_KEY)
.description("The password of the service principal").type(WorkParameterType.TEXT)
.optional(false).build(),
WorkParameter.builder().key(AZURE_RESOURCES_PREFIX_KEY)
.description("A designated prefix for naming all Azure resources").type(WorkParameterType.TEXT)
.optional(false).build());
}

@Override
/**
* Execute task, creates Virtual Machine in Azure Results with Virtual Machine created
* with public IP. This VM can be accessed using ssh (with provided username and ssh
* key) the public IP address is added to the context returned when the task is
* completed. Key: public-ip-address
*/
public WorkReport execute(WorkContext context) {
try {
final String userName = getRequiredParameterValue(context, VM_USER_NAME_KEY);
final String sshKey = getRequiredParameterValue(context, VM_SSH_PUBLIC_KEY_KEY);

final String azureTenantId = getRequiredParameterValue(context, AZURE_TENANT_ID_KEY);
final String azureSubscriptionId = getRequiredParameterValue(context, AZURE_SUBSCRIPTION_ID_KEY);
final String azureClientId = getRequiredParameterValue(context, AZURE_CLIENT_ID_KEY);
final String azureClientSecret = getRequiredParameterValue(context, AZURE_CLIENT_SECRET_KEY);
final String resourcesPrefix = getRequiredParameterValue(context, AZURE_RESOURCES_PREFIX_KEY);

this.azureResourceClient.init(azureTenantId, azureClientId, azureClientSecret, azureSubscriptionId);

ResourceGroup resourceGroup = azureResourceClient.createResourceGroup(resourcesPrefix);

AvailabilitySet availabilitySet = azureResourceClient.createAvailabilitySet(resourcesPrefix);

PublicIpAddress publicIPAddress = azureResourceClient.createPublicIpAddress(resourcesPrefix);

Network network = azureResourceClient.createNetwork(resourcesPrefix);

NetworkInterface networkInterface = azureResourceClient.createNetworkInterface(resourcesPrefix, network,
publicIPAddress);

VirtualMachine virtualMachine = azureResourceClient.createVirtualMachine(resourcesPrefix, networkInterface,
availabilitySet, userName, sshKey);

PublicIpAddress publicIpAddress = virtualMachine.getPrimaryPublicIPAddress();
if (publicIpAddress == null) {
masayag marked this conversation as resolved.
Show resolved Hide resolved
log.error("VirtualMachine was created but without public IP");
throw new InternalException("The new created VirtualMachine missing public IP address");
}

String ipAddress = publicIpAddress.ipAddress();
log.info("VirtualMachine was created with public IP {}", ipAddress);
context.put("public-ip-address", ipAddress);
masayag marked this conversation as resolved.
Show resolved Hide resolved
}
catch (MissingParameterException e) {
log.error("Task {} failed: missing required parameter, error: {}", getName(), e.getMessage());
return new DefaultWorkReport(WorkStatus.FAILED, context, e);
}
catch (Exception e) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please be specific which exceptions we want to catch here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except to MissingParameterException there is no known declared Exception that should be catched
Therefore I catch Exception to cover any unexpected RuntimeException

log.error("Task {} failed, with error: {}", getName(), e.getMessage());
return new DefaultWorkReport(WorkStatus.FAILED, context, e);
}

return new DefaultWorkReport(WorkStatus.COMPLETED, context);
}

@Override
public @NonNull List<WorkFlowTaskOutput> getWorkFlowTaskOutputs() {
return List.of(WorkFlowTaskOutput.OTHER);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.redhat.parodos.tasks.azure;

import com.azure.resourcemanager.compute.models.AvailabilitySet;
import com.azure.resourcemanager.compute.models.VirtualMachine;
import com.azure.resourcemanager.network.models.Network;
import com.azure.resourcemanager.network.models.NetworkInterface;
import com.azure.resourcemanager.network.models.PublicIpAddress;
import com.azure.resourcemanager.resources.models.ResourceGroup;

interface AzureResourceClient {

void init(String azureTenantId, String azureClientId, String azureClientSecret, String azureSubscriptionId);

ResourceGroup createResourceGroup(String resourcesPrefix);

AvailabilitySet createAvailabilitySet(String resourcesPrefix);

PublicIpAddress createPublicIpAddress(String resourcesPrefix);

Network createNetwork(String resourcesPrefix);

NetworkInterface createNetworkInterface(String resourcesPrefix, Network network, PublicIpAddress publicIPAddress);

VirtualMachine createVirtualMachine(String resourcesPrefix, NetworkInterface networkInterface,
AvailabilitySet availabilitySet, String userName, String sshKey);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.redhat.parodos.tasks.azure;

import com.azure.core.credential.TokenCredential;
import com.azure.core.http.policy.HttpLogDetailLevel;
import com.azure.core.management.AzureEnvironment;
import com.azure.core.management.Region;
import com.azure.core.management.profile.AzureProfile;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.azure.resourcemanager.AzureResourceManager;
import com.azure.resourcemanager.compute.models.AvailabilitySet;
import com.azure.resourcemanager.compute.models.KnownLinuxVirtualMachineImage;
import com.azure.resourcemanager.compute.models.VirtualMachine;
import com.azure.resourcemanager.compute.models.VirtualMachineSizeTypes;
import com.azure.resourcemanager.network.models.Network;
import com.azure.resourcemanager.network.models.NetworkInterface;
import com.azure.resourcemanager.network.models.PublicIpAddress;
import com.azure.resourcemanager.resources.models.ResourceGroup;
import lombok.extern.slf4j.Slf4j;

@Slf4j
class AzureResourceClientImpl implements AzureResourceClient {

private AzureResourceManager azureResourceManager;

@Override
public void init(String azureTenantId, String azureClientId, String azureClientSecret, String azureSubscriptionId) {
TokenCredential credential = new ClientSecretCredentialBuilder().tenantId(azureTenantId).clientId(azureClientId)
.clientSecret(azureClientSecret).build();
AzureProfile profile = new AzureProfile(azureTenantId, azureSubscriptionId, AzureEnvironment.AZURE);
azureResourceManager = AzureResourceManager.configure().withLogLevel(HttpLogDetailLevel.BASIC)
.authenticate(credential, profile).withDefaultSubscription();
}

@Override
public ResourceGroup createResourceGroup(String resourcesPrefix) {
log.info("Creating resource group...");
return azureResourceManager.resourceGroups().define(resourcesPrefix + "ResourceGroup")
.withRegion(Region.US_EAST).create();
}

@Override
public AvailabilitySet createAvailabilitySet(String resourcesPrefix) {
log.info("Creating availability set...");
return azureResourceManager.availabilitySets().define(resourcesPrefix + "AvailabilitySet")
.withRegion(Region.US_EAST).withExistingResourceGroup(resourcesPrefix + "ResourceGroup").create();
}

@Override
public PublicIpAddress createPublicIpAddress(String resourcesPrefix) {
log.info("Creating public IP address...");
return azureResourceManager.publicIpAddresses().define(resourcesPrefix + "PublicIP").withRegion(Region.US_EAST)
.withExistingResourceGroup(resourcesPrefix + "ResourceGroup").withDynamicIP().create();
}

@Override
public Network createNetwork(String resourcesPrefix) {
log.info("Creating virtual network...");
return azureResourceManager.networks().define(resourcesPrefix + "VN").withRegion(Region.US_EAST)
.withExistingResourceGroup(resourcesPrefix + "ResourceGroup").withAddressSpace("10.0.0.0/16")
.withSubnet(resourcesPrefix + "Subnet", "10.0.0.0/24").create();
}

@Override
public NetworkInterface createNetworkInterface(String resourcesPrefix, Network network,
PublicIpAddress publicIPAddress) {
log.info("Creating network interface...");
return azureResourceManager.networkInterfaces().define(resourcesPrefix + "NIC").withRegion(Region.US_EAST)
.withExistingResourceGroup(resourcesPrefix + "ResourceGroup").withExistingPrimaryNetwork(network)
.withSubnet(resourcesPrefix + "Subnet").withPrimaryPrivateIPAddressDynamic()
.withExistingPrimaryPublicIPAddress(publicIPAddress).create();
}

@Override
public VirtualMachine createVirtualMachine(String resourcesPrefix, NetworkInterface networkInterface,
AvailabilitySet availabilitySet, String userName, String sshKey) {
log.info("Creating virtual machine...");
return azureResourceManager.virtualMachines().define(resourcesPrefix + "VM").withRegion(Region.US_EAST)
.withExistingResourceGroup(resourcesPrefix + "ResourceGroup")
.withExistingPrimaryNetworkInterface(networkInterface)
.withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_18_04_LTS).withRootUsername(userName)
.withSsh(sshKey).withComputerName(resourcesPrefix + "VM").withExistingAvailabilitySet(availabilitySet)
.withSize(VirtualMachineSizeTypes.STANDARD_D3_V2).create();
}

}
Loading