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.
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.
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 benull
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());
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);
}
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);
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));
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);
You can get all available Landing Zone Factories:
List<FactoryInfo> factories=LandingZoneManager.listDefinitionFactories();
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.
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 |
|
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 |
|
NA |