what
- Add
Hierarchical Inheritance
andMultilevel Inheritance
for Atmos Components - Update docs https://atmos.tools/core-concepts/components/inheritance
why
- Allow creating complex hierarchical stack configurations and make them DRY
- In Hierarchical Inheritance, every component can act as a base component for one or more child (derived) components, and each child component can inherit from one of more base components.
description
Multilevel Inheritance
Multilevel Inheritance is used when an Atmos component inherits from a base Atmos component, which in turn inherits from another base Atmos component.
In the diagram below, ComponentC
directly inherits from ComponentB
.
ComponentB
directly inherits from ComponentA
.
After this Multilevel Inheritance chain gets processed by Atmos, ComponentC
will inherit all the configurations (vars
, settings
, env
and other sections) from both ComponentB
and ComponentA
.
Note that ComponentB
overrides the values from ComponentA
. ComponentC
overrides the values from both ComponentB
and ComponentA
.
classDiagram
ComponentA --> ComponentB
ComponentB --> ComponentC
ComponentA : vars
ComponentA : settings
ComponentA : env
class ComponentB {
vars
settings
env
metadata:
inherits:
- ComponentA
}
class ComponentC {
vars
settings
env
metadata:
inherits:
- ComponentB
}
Hierarchical Inheritance
Hierarchical Inheritance is a combination of Multiple Inheritance and Multilevel Inheritance.
In Hierarchical Inheritance, every component can act as a base component for one or more child (derived) components, and each child component can inherit from one of more base components.
classDiagram
ComponentA --> ComponentB
ComponentA --> ComponentC
ComponentB --> ComponentD
ComponentB --> ComponentE
ComponentC --> ComponentF
ComponentC --> ComponentG
ComponentH --> ComponentE
ComponentI --> ComponentG
ComponentA : vars
ComponentA : settings
ComponentA : env
class ComponentB {
vars
settings
env
metadata:
inherits:
- ComponentA
}
class ComponentC {
vars
settings
env
metadata:
inherits:
- ComponentA
}
class ComponentD {
vars
settings
env
metadata:
inherits:
- ComponentB
}
class ComponentE {
vars
settings
env
metadata:
inherits:
- ComponentB
- ComponentH
}
class ComponentF {
vars
settings
env
metadata:
inherits:
- ComponentC
}
class ComponentG {
vars
settings
env
metadata:
inherits:
- ComponentI
- ComponentC
}
class ComponentH {
vars
settings
env
}
class ComponentI {
vars
settings
env
}
In the diagram above:
-
ComponentA
is the base component of the whole hierarchy -
ComponentB
andComponentC
inherit fromComponentA
-
ComponentD
inherits fromComponentB
directly, and fromComponentA
via Multilevel Inheritance -
ComponentE
is an example of using both Multiple Inheritance and Multilevel Inheritance.
It inherits fromComponentB
andComponentH
directly, and fromComponentA
via Multilevel Inheritance
For ComponentE
, the inherited components are processed and deep-merged in the order they are specified in the inherits
list:
-
ComponentB
overrides the configuration fromComponentA
-
ComponentH
overrides the configuration fromComponentB
andComponentA
(since it's defined afterComponentB
in theinherits
section) -
And finally,
ComponentE
overridesComponentH
,ComponentB
andComponentA
For ComponentG
:
-
ComponentI
is processed first (since it's the first item in theinherits
list) -
Then
ComponentA
is processed (since it's the base component forComponentC
which is the second item in theinherits
list) -
Then
ComponentC
is processed, and it overrides the configuration fromComponentA
andComponentI
-
And finally,
ComponentG
is processed, and it overridesComponentC
,ComponentA
andComponentI
Hierarchical Inheritance Example
Let's consider the following configuration for Atmos components base-component-1
, base-component-2
, derived-component-1
and derived-component-2
:
components:
terraform:
base-component-1:
metadata:
type: abstract
vars:
hierarchical_inheritance_test: "base-component-1"
base-component-2:
metadata:
type: abstract
vars:
hierarchical_inheritance_test: "base-component-2"
derived-component-1:
metadata:
component: "test/test-component"
inherits:
- base-component-1
vars: {}
derived-component-2:
metadata:
component: "test/test-component"
inherits:
- base-component-2
- derived-component-1
vars: {}
This configuration can be represented by the following diagram:
classDiagram
`base-component-1` --> `derived-component-1`
`derived-component-1` --> `derived-component-2`
`base-component-2` --> `derived-component-2`
class `base-component-1` {
settings
env
vars:
hierarchical_inheritance_test: base-component-1
}
class `base-component-2` {
settings
env
vars:
hierarchical_inheritance_test: base-component-2
}
class `derived-component-1` {
settings
env
vars
metadata:
inherits:
- base-component-1
}
class `derived-component-2` {
settings
env
vars
metadata:
inherits:
- base-component-2
- derived-component-1
}
In the configuration above, derived-component-1
inherits from base-component-1
.
derived-component-2
inherits from base-component-2
and derived-component-1
via Multiple Inheritance, and from base-component-1
via Multilevel Inheritance.
The derived-component-2
component is processed in the following order:
-
base-component-2
is processed first (since it's the first item in theinherits
list) -
Then
base-component-1
is processed (since it's the base component forderived-component-1
which is the second item in theinherits
list), and
it overrides the configuration frombase-component-2
-
Then
derived-component-1
is processed, and it overrides the configuration frombase-component-2
andbase-component-1
-
And finally,
derived-component-2
is processed, and it overridesderived-component-1
,base-component-1
andbase-component-2
When we run the following command to provision the derived-component-2
component:
atmos terraform plan derived-component-2 -s tenant1-ue2-test-1
Atmos will show the following output:
Variables for the component 'derived-component-2' in the stack 'tenant1-ue2-test-1':
environment: ue2
hierarchical_inheritance_test: base-component-1
namespace: cp
region: us-east-2
stage: test-1
tenant: tenant1
Command info:
Terraform binary: terraform
Terraform command: plan
Component: derived-component-2
Terraform component: test/test-component
Inheritance: derived-component-2 -> derived-component-1 -> base-component-1 -> base-component-2
Note that the hierarchical_inheritance_test
variable was inherited from base-component-1
because it overrode the configuration from base-component-2
.
If we change the order of the components in the inherits
list for derived-component-2
:
components:
terraform:
derived-component-2:
metadata:
component: "test/test-component"
inherits:
- derived-component-1
- base-component-2
vars: {}
base-component-2
will be processed after base-component-1
and derived-component-1
, and the hierarchical_inheritance_test
variable will be inherited from base-component-2
:
Variables for the component 'derived-component-2' in the stack 'tenant1-ue2-test-1':
environment: ue2
hierarchical_inheritance_test: base-component-2
namespace: cp
region: us-east-2
stage: test-1
tenant: tenant1
Command info:
Terraform binary: terraform
Terraform command: plan
Component: derived-component-2
Terraform component: test/test-component
Inheritance: derived-component-2 -> base-component-2 -> derived-component-1 -> base-component-1