Skip to main content

Abstract Component

Atmos Design Pattern

The Abstract Component Design Pattern describes the mechanism of deriving Atmos components from one or more abstract base components (blueprints), allowing reusing the base components' configurations and prohibiting the abstract base component from being provisioned.

Atmos supports two types of components:

real
is a "concrete" component instance that can be provisioned
abstract
a component configuration, which cannot be instantiated directly. The concept is borrowed from "abstract base classes" of Object-Oriented Programming

The type of component is expressed in the metadata.type parameter of a given component configuration.

note

For more details, refer to:

Use-cases

Use the Abstract Component pattern when:

  • You need to have reusable base components that serve as blueprints for the derived Atmos components

  • You need to prevent the abstract base components from being provisioned

  • You need to keep the configuration of all components DRY

Benefits

The Abstract Component pattern provides the following benefits:

  • Allows creating very DRY and reusable configurations that are built upon existing abstract base components (blueprints)

  • Prevents the abstract base components from being provisioned

  • The metadata.type: abstract attribute serves as a guard against accidentally deploying the components that are not meant to be deployed

Example

The following example shows the Atmos stack and component configurations to provision the vpc component into a multi-account, multi-region environment. In the catalog/vpc folder, we have the defaults.yaml manifest that configures the base abstract component vpc/defaults to be inherited by all the derived VPC components in all stacks. By being abstract, the base component vpc/defaults is prohibited from being provisioned.

   │   # Centralized stacks configuration (stack manifests)
   ├── stacks
   │   ├── catalog # component-specific defaults
   │   │   └── vpc
   │   │       └── defaults.yaml
   │   ├── mixins
   │   │   ├── tenant # tenant-specific defaults
   │   │   │   └── plat.yaml
   │   │   ├── region # region-specific defaults
   │   │   │   ├── us-east-2.yaml
   │   │   │   └── us-west-2.yaml
   │   │   └── stage # stage-specific defaults
   │   │       ├── dev.yaml
   │   │       ├── staging.yaml
   │   │       └── prod.yaml
   │   └── orgs # Organizations
   │       └── acme
   │           ├── _defaults.yaml
   │           └── plat # 'plat' represents the "Platform" OU (a.k.a tenant)
   │               ├── _defaults.yaml
   │               ├── dev
   │               │   ├── _defaults.yaml
   │               │   ├── us-east-2.yaml
   │               │   └── us-west-2.yaml
   │               ├── staging
   │               │   ├── _defaults.yaml
   │               │   ├── us-east-2.yaml
   │               │   └── us-west-2.yaml
   │               └── prod
   │                   ├── _defaults.yaml
   │                   ├── us-east-2.yaml
   │                   └── us-west-2.yaml
   │   # Centralized components configuration
   └── components
       └── terraform # Terraform components (a.k.a Terraform "root" modules)
           └── vpc

Add the following minimal configuration to atmos.yaml CLI config file :

atmos.yaml
components:
terraform:
base_path: "components/terraform"

stacks:
base_path: "stacks"
name_pattern: "{tenant}-{environment}-{stage}"
included_paths:
# Tell Atmos to search for the top-level stack manifests in the `orgs` folder and its sub-folders
- "orgs/**/*"
excluded_paths:
# Tell Atmos that the `defaults` folder and all sub-folders don't contain top-level stack manifests
- "defaults/**/*"

schemas:
jsonschema:
base_path: "stacks/schemas/jsonschema"
opa:
base_path: "stacks/schemas/opa"
atmos:
manifest: "stacks/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json"

Add the following configuration for the abstract base component vpc/defaults to the stacks/catalog/vpc/defaults.yaml manifest:

stacks/catalog/vpc/defaults.yaml
components:
terraform:
vpc/defaults:
metadata:
# Abstract components can't be provisioned, they just serve as base components (blueprints) for real components
# `metadata.type` can be `abstract` or `real`
# `real` is the default and can be omitted
type: abstract
settings:
# All validation steps must succeed to allow the component to be provisioned
validation:
validate-vpc-component-with-jsonschema:
schema_type: jsonschema
schema_path: "vpc/validate-vpc-component.json"
description: Validate 'vpc' component variables using JSON Schema
check-vpc-component-config-with-opa-policy:
schema_type: opa
schema_path: "vpc/validate-vpc-component.rego"
module_paths:
- "catalog/constants"
description: Check 'vpc' component configuration using OPA policy
vars:
enabled: true
name: "common"
max_subnet_count: 3
map_public_ip_on_launch: true
assign_generated_ipv6_cidr_block: false
nat_gateway_enabled: true
nat_instance_enabled: false
vpc_flow_logs_enabled: true
vpc_flow_logs_traffic_type: "ALL"
vpc_flow_logs_log_destination_type: "s3"
nat_eip_aws_shield_protection_enabled: false
subnet_type_tag_key: "acme/subnet/type"
ipv4_primary_cidr_block: 10.0.0.0/18

Configure the vpc Atmos component in the stacks/orgs/acme/plat/prod/us-east-2.yaml top-level stack. The vpc component inherits from the vpc/defaults abstract base component:

stacks/orgs/acme/plat/prod/us-east-2.yaml
import:
import:
- orgs/acme/plat/prod/_defaults
- mixins/region/us-east-2
# Import the `vpc/defaults` component from the `catalog/vpc/defaults.yaml` manifest
- catalog/vpc/defaults

components:
terraform:
# Atmos component `vpc`
vpc:
metadata:
# `metadata.type` can be `abstract` or `real`
# `real` is the default and can be omitted
# Real components can be provisioned
type: real
# Point to the Terraform component in `components/terraform/vpc`
component: vpc
# Inherit from the `vpc/defaults` Atmos base component
# This is Single Inheritance: the Atmos component inherits from one base Atmos component
inherits:
- vpc/defaults
# Define/override variables specific to this `vpc` component
vars:
name: my-vpc
vpc_flow_logs_enabled: false
ipv4_primary_cidr_block: 10.9.0.0/18

To provision the vpc component into the plat-ue2-prod top-level stack, execute the following command:

atmos terraform apply vpc -s plat-ue2-prod

If you try to execute the following commands to provision the vpc/defaults abstract base component:

atmos terraform plan vpc/defaults -s plat-ue2-prod
atmos terraform apply vpc/defaults -s plat-ue2-prod

the following error will be thrown:

abstract component 'vpc/defaults' cannot be provisioned since it's explicitly prohibited from
being deployed by 'metadata.type: abstract' attribute

References