Skip to content

jsaun/terra-landing-zone-service

 
 

Repository files navigation

Landing Zones

Quality Gate Status

Overview

A Landing Zone is a set of cloud resources that serve as the underlying infrastructure where workspaces or other Terra applications can be deployed. The resources in a Landing Zone define and implement constraints, provide cross-cutting features, or can be shared. These resources have a different lifecycle than resources in workspaces.

Implementing a Landing Zone

Landing Zone Definition Factories and Landing Zone Definitions.

Landing zones are implemented using the factory pattern; the factory creates Landing Zone Definitions (LZDs).

Landing Zone Definitions are where resources and their purpose are defined.

A Landing Zone Definition factory is an implementation of:

public interface LandingZoneDefinitionFactory {
    DefinitionHeader header();

    List<DefinitionVersion> availableVersions();

    LandingZoneDefinable create(DefinitionVersion version);
}

The library includes an abstract class that expects the Azure Resource Manager (ARM) clients: ArmClientsDefinitionFactory. Factories should extend this class.

In addition, factories:

  • Must be implemented in the factories package.
  • Must have a package scoped parameterless constructor.
package bio.terra.landingzone.library.landingzones.definition.factories;


public class FooLZFactory extends ArmClientsDefinitionFactory {

    FooLZFactory() {
    }

    @Override
    public DefinitionHeader header() {
        return new DefinitionHeader("Foo LZ", "Description of Foo LZ");
    }

    @Override
    public List<DefinitionVersion> availableVersions() {
        return List.of(DefinitionVersion.V1);
    }

    @Override
    public LandingZoneDefinable create(DefinitionVersion version) {
        if (version.equals(DefinitionVersion.V1)) {
            return new FooLZDefinitionV1(azureResourceManager, relayManager);
        }
        throw new RuntimeException("Invalid Version");
    }
}

An inner class in the factory class is a good convention for implementing a Landing Zone Definition.

public class FooLZFactory extends ArmClientsDefinitionFactory {
    ...

    class FooLZDefinitionV1 extends LandingZoneDefinition {

        protected FooLZDefinitionV1(
                AzureResourceManager azureResourceManager, RelayManager relayManager) {
            super(azureResourceManager, relayManager);
        }

        @Override
        public Deployable definition(DefinitionContext definitionContext) {
            var storage =
                    azureResourceManager
                            .storageAccounts()
                            .define(definitionContext.resourceNameGenerator().nextName(20))
                            .withRegion(Region.US_EAST2)
                            .withExistingResourceGroup(definitionContext.resourceGroup());

            var vNet =
                    azureResourceManager
                            .networks()
                            .define(definitionContext.resourceNameGenerator().nextName(20))
                            .withRegion(Region.US_EAST2)
                            .withExistingResourceGroup(definitionContext.resourceGroup())
                            .withAddressSpace("10.0.0.0/28")
                            .withSubnet("compute", "10.0.0.0/29")
                            .withSubnet("storage", "10.0.0.8/29");

            return definitionContext
                    .deployment()
                    .withResourceWithPurpose(storage, ResourcePurpose.SHARED_RESOURCE)
                    .withVNetWithPurpose(vNet, "compute", SubnetResourcePurpose.WORKSPACE_COMPUTE_SUBNET)
                    .withVNetWithPurpose(vNet, "storage", SubnetResourcePurpose.WORKSPACE_STORAGE_SUBNET);
        }
    }

Resources are defined using the standard Azure Java SDK but with the following constraints to consider:

  • The purpose of the resources must be indicated in the deployment.
  • Resources not included in the deployment won't be created.
  • The create() method in the resource definition must not be called.
  • The resource definition must have the required configuration for a creation before it can be added to the deployment.

Receiving Parameters

The DefinitionContext contains the property Map<String, String> parameters, which can be used to receive parameter for the definition of a landing zone.

A few points to consider when implementing a definition that requires parameters:

  • You must add the necessary validation for these parameters.
  • parameters can be null

Naming Resources and Idempotency

You can use the resource name generator in the deployment context to guarantee that names are consistent in retry attempts.

The resource name generator creates a name from a hash of the landing zone id and internal sequence number. As long as the landing zone id is globally unique, the resulting name will be the same across retry attempts with a very low probability of a naming collision.

The Azure Resource Manager APIs can be retried if a transient error occurs - the API is idempotent. However, The request must be the same as the failed one to avoid duplicating resources in the deployment. The deployment could create duplicate resources if the resource's name is auto-generated and changes in every request.

An instance of the resource name generator is included in the deployment context.

 var storage=azureResourceManager
        .storageAccounts()
        .define(definitionContext.resourceNameGenerator().nextName(20))
        .withRegion(Region.US_EAST2)
        .withExistingResourceGroup(definitionContext.resourceGroup());

Handling Prerequisites

The library deploys resources in a non-deterministic order. Therefore, it is not possible to assume any specific order. For cases when a resource must be created before other resources, you can create a prerequisite deployment inside your definition.

class FooLZDefinitionV1 extends LandingZoneDefinition {

    protected FooLZDefinitionV1(
            AzureResourceManager azureResourceManager, RelayManager relayManager) {
        super(azureResourceManager, relayManager);
    }

    @Override
    public Deployable definition(DefinitionContext definitionContext) {

        var vNet =
                azureResourceManager
                        .networks()
                        .define(definitionContext.resourceNameGenerator().nextName(20))
                        .withRegion(Region.US_EAST2)
                        .withExistingResourceGroup(definitionContext.resourceGroup())
                        .withAddressSpace("10.0.0.0/28")
                        .withSubnet("subnet1", "10.0.0.0/29")

        var prerequisites =
                deployment
                        .definePrerequisites()
                        .withVNetWithPurpose(vNet, "subnet1", SubnetResourcePurpose.WORKSPACE_STORAGE_SUBNET)
                        .deploy();

        var storage =
                azureResourceManager
                        .storageAccounts()
                        .define(definitionContext.resourceNameGenerator().nextName(20))
                        .withRegion(Region.US_EAST2)
                        .withExistingResourceGroup(definitionContext.resourceGroup());

        return definitionContext
                .deployment()
                .withResourceWithPurpose(storage, ResourcePurpose.SHARED_RESOURCE);
    }

Landing Zone Manager

The Landing Zone Manager is the high-level component that lists the available Landing Zone Definition factories, deploys Landing Zone Definitions and lists resources per purpose.

The Landing Zone Manager requires a TokenCredential, AzureProfile and resourceGroupName.

landingZoneManager=
        LandingZoneManager.createLandingZoneManager(
        credential,
        azureProfile,
        resourceGroupName);

Deploying a Landing Zone Definition

You can deploy Landing Zone Definition using the manager.

    List<DeployedResource> resources=
        landingZoneManager.deployLandingZone(
        landingZoneId,
        "FooLZFactory",
        DefinitionVersion.V1,
        parameters);

The manager has an asynchronous API for deployments. You can implement retry capabilities using standard reactive retry policies.

    Flux<DeployedResource> resources=
        landingZoneManager
        .deployLandingZoneAsync(landingZoneId,FooLZDefinitionV1.class,DefinitionVersion.V1,parameters)
        .retryWhen(Retry.max(1));

Reading Landing Zone Resources

You can list resources by purpose using the Landing Zone Manager:

List<DeployedResource> resources=landingZoneManager.reader().listResourcesByPurpose(landingZoneId, ResourcePurpose.SHARED_RESOURCE);

Virtual Networks can be listed by subnet purpose:

    List<DeployedVNet> vNets=
        landingZoneManager.reader().listVNetWithSubnetPurpose(landingZoneId, SubnetResourcePurpose.WORKSPACE_COMPUTE_SUBNET);

Getting the available Landing Zone Factories

You can get all available Landing Zone Factories:

List<FactoryInfo> factories=LandingZoneManager.listDefinitionFactories();

Landing Zone Service

The Landing Zone Service is a Spring service component that wraps the Landing Zone Manager, provides a job based/async API to deploy Landing Zones, and persists Landing Zone deployments state in a DB.

Landing Zone Definitions

The table below describes the current Landing Zone Definitions available in the library.

Factory Description Versions Shared Resources Parameters
CromwellBaseResourcesFactory Deploys required resources to run Cromwell on Azure. V1
  • AKS Cluster
  • Batch Account
  • Storage Account
  • PostgreSQL
  • Azure Relay Namespace
  • VNet with subnets for PostgreSQL, AKS Node pool, PostgreSQL databases and Compute resources
POSTGRES_DB_ADMIN: Username of the DB admin
Default value: db_admin

POSTGRES_DB_PASSWORD: DB admin password
Default value: UUID.randomUUID().toString()

POSTGRES_SERVER_SKU: PostgreSQL Server SKU
Default value: GP_Gen5_2

VNET_ADDRESS_SPACE: Virtual network address space
Default value: 10.1.0.0/27

AKS_SUBNET: AKS subnet address space
Default value: 10.1.0.0/29

BATCH_SUBNET: Batch subnet address space
Default value: 10.1.0.8/29

POSTGRESQL_SUBNET: PostgreSQL subnet address space
Default value: 10.1.0.16/29

COMPUTE_SUBNET: Compute resources subnet address space
Default value: 10.1.0.24/29

AKS_NODE_COUNT: Number of nodes in AKS Nodepool
Default value: 1

AKS_MACHINE_TYPE: Machine type used for AKS hosts
Default value: ContainerServiceVMSizeTypes.STANDARD_A2_V2

AKS_AUTOSCALING_ENABLED: Flag to enabled autoscaling for the AKS nodepool
Default value: false

AKS_AUTOSCALING_MIN: Minimum number of nodes in nodepool when autoscaling is enabled
Default value: 1

AKS_AUTOSCALING_MAX: Maximum number of nodes in nodepool when autoscaling is enabled
Default value: 3

Azure storage account overview: documentation

Azure storage CORS configuration: documentation

STORAGE_ACCOUNT_BLOB_CORS_ALLOWED_ORIGINS: The origin domains that are permitted to make a request against the storage service via CORS
Default value: *

STORAGE_ACCOUNT_BLOB_CORS_ALLOWED_METHODS: The methods (HTTP request verbs) that the origin domain may use for a CORS request
Default value: GET,HEAD,OPTIONS,PUT,PATCH,POST,MERGE,DELETE

STORAGE_ACCOUNT_BLOB_CORS_ALLOWED_HEADERS: The request headers that the origin domain may specify on the CORS request
Default value: authorization,content-type,x-app-id,Referer,x-ms-blob-type,x-ms-copy-source,content-length

STORAGE_ACCOUNT_BLOB_CORS_EXPOSED_HEADERS: The response headers that may be sent in the response to the CORS request and exposed by the browser to the request issuer
Default value: Empty string

STORAGE_ACCOUNT_BLOB_CORS_MAX_AGE: The maximum amount time that a browser should cache the preflight OPTIONS request (in seconds)
Default value: 0

Azure storage SKU types: documentation

STORAGE_ACCOUNT_SKU_TYPE: Type of storage account
Default value: Standard_LRS;
Accepted values: Standard_LRS, Standard_GRS, Standard_RAGRS, Standard_ZRS, Premium_LRS; Please see StorageAccountSkuType;

ManagedNetworkWithSharedResourcesFactory Deploys a virtual network, shared storage and Azure Relay namespace. V1
  • Storage Account
  • Azure Relay Namespace
  • VNet
NA

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 98.0%
  • Shell 2.0%