title | sidebar_position |
---|---|
Blueprints |
1 |
A blueprint is a deployment instruction. It defines how to deploy a set of artifacts, for example helm charts.
A blueprint is parameterized, i.e. it has import and export parameters to allow a dataflow. Thus, a blueprint can be compared with a function that has import and export parameters and performs a deployment. Blueprints are used in Installations, which are particular Landscaper custom resources. To stay with the analogy between blueprints and functions, an Installation corresponds to a function call. Creating an Installation custom resource (with a certain annotation) starts the deployment process defined by its blueprint. The Installation provides the values for the import parameters of the blueprint that it "calls".
During the processing of an Installation and its blueprint, DeployItem custom resources are created. They describe the elementary deployment units of a blueprint, for example the deployment of a single helm chart. The DeployItems are created based on templates (GoTemplate or Spiff) defined in the blueprint. These templates can be filled with values of the import parameters. The DeployItems are handled by independent Landscaper kubernetes controllers (so-called deployers), which perform the real deployment tasks.
For complex deployment processes, it is possible to bundle several installations and their blueprints into a parent blueprint. Put the other way round, a (parent) blueprint can define subinstallations, each with a blueprint that describes a part of the whole deployment process. Blueprints of subinstallations can again have subinstallations, and so on. Imports can be passed from a parent to its subinstallations, and exports from subinstallations to the parent. Moreover, subinstallations of the same parent can exchange data, i.e. a subinstallation can import the exports of its siblings. The export-import graph determines the order, in which installations are processed. This graph must not have cycles.
A blueprint is a filesystem structure. It consists of a directory that must contain a yaml file with name blueprint.yaml
.
It can contain further files and subdirectories.
For example, the blueprint of the guided tour example
import-export/export-parameters
contains besides the blueprint.yaml
also a file deploy-execution.yaml
with a template to generate DeployItems, and a
file export-execution.yaml
with a template to generate export values:
├── blueprint.yaml
├── deploy-execution.yaml
└── export-execution.yaml
Other possible files in the blueprint directory are for example subinstallations as in the guided tour example subinstallations/export-import.
The blueprint.yaml
file describes:
- declaration of import parameters
- declaration of export parameters
- JSONSchema definitions
- generation rules for DeployItems
- generation rules for export values
- generation of subinstallations (again calling some Blueprint with particular input parameters)
The following snippet shows the structure of a blueprint.yaml
file.
Refer to apis/.schemes/core-v1alpha1-Blueprint.json
for the automatically generated jsonschema definition.
apiVersion: landscaper.gardener.cloud/v1alpha1
kind: Blueprint
# jsonSchemaVersion describes the default jsonschema definition
# for the import and export definitions.
jsonSchemaVersion: "https://json-schema.org/draft/2019-09/schema"
# localTypes defines shared jsonschema types that can be used in the
# import and export definitions.
localTypes:
mytype: # expects a jsonschema
type: object
# imports defines all imported values that are expected.
# Data can be either imported as data objects, targets or target maps.
imports:
# Import a data object by specifying the expected structure of data
# as jsonschema.
- name: my-import-key
required: true # required, defaults to true
type: data # this is a data import
schema: # expects a jsonschema
"$ref": "local://mytype" # references local type
# Import a target by specifying the targetType
- name: my-target-import-key
required: true # required, defaults to true
type: target # this is a target import
targetType: landscaper.gardener.cloud/kubernetes-cluster
# Import a targetmap
- name: my-targetmap-import-key
# required: true # defaults to true
type: targetMap # this is a targetmap import
targetType: landscaper.gardener.cloud/kubernetes-cluster
# exports defines all values that are produced by the blueprint
# and that are exported.
# Exported values can be consumed by other blueprints.
# Data can be either exported as data object or target.
exports:
# Export a data object by specifying the expected structure of data
# as jsonschema.
- name: my-export-key
type: data # this is a data export
schema: # expects a jsonschema
type: string
# Export a target by specifying the targetType
- name: my-target-export-key
type: target # this is a target export
targetType: landscaper.gardener.cloud/kubernetes-cluster
# deployExecutions are a templating mechanism to
# template the deployitems.
# For detailed documentation see #DeployExecutions
deployExecutions:
- name: execution-name
type: GoTemplate
file: <path to file> # path is relative to the blueprint's filesystem root
# exportExecutions are a templating mechanism to
# template the export.
# For detailed documentation see #ExportExecutions
exportExecutions:
- name: execution-name
type: Spiff
template: # inline template
# subinstallations is a list of installation templates.
# An installation template expose specific installation configuration are
# used to assemble multiple blueprints together.
subinstallations:
- file: /installations/dns/dns-installation.yaml
- apiVersion: landscaper.gardener.cloud/v1alpha1
kind: InstallationTemplate
name: ingress # must be unique
blueprint:
ref: cd://componentReferences/ingress/resources/blueprint #cd://resources/myblueprint
# filesystem:
# blueprint.yaml: abc...
# define imported dataobjects and target from other installations or the
# parents import.
# It's the same syntax as for default installations.
imports:
data:
- name: "parent-data" # data import name
dataRef: "data" # dataobject name - refers to import of parent
targets:
- name: "" # target import name
target: "" # target name
#importDataMappings: {}
exports:
targets:
- name: "" # target export name
target: "" # target name
#exportDataMappings: {}
The declaration of import and export parameters in explained in the sections Import Definitions and Export Definitions below.
The templating of DeployItems, export values, and subinstallations is explained in the Templating chapter.
Blueprints are intended to be reusable. Therefore, they must be stored in a central place, from where they can be accessed by several installations. The Landscaper makes use of the Open Component Model (OCM). A blueprint (except inline blueprints) must belong to an OCM component version: it must be a local resource of this component version, and be store together with it in an OCM repository. The guided tour explains in example components/helm-chart how to achieve this.
All resources which the blueprint uses (e.g. helm charts, json schemata, or blueprints of subinstallations) must also be resources of this component version, or of component versions which are referenced by it (transitively).
Blueprints describe formal imports. A formal import parameter has a name and a value type. It may describe a single simple value or a complex data structure. There are several types of imports, indicating different use cases:
-
data
This type of import is used to import arbitrary data according to its value type. The value type is described by a JSONSchema.
-
target
This type declares an import of a deployment target object. It is used in the rendered deployitems to specify the target environment for the deployment of the deployitem.
-
targetMap
This type can be used if, instead of a single target object, an arbitrary number of targets should be imported. All targets imported as part of a targetmap import must have the same
targetType
. For more information on how to work with TargetMap imports, see the guided tour.
The imports are described as a list of import declarations in the blueprint top-level field imports
. An import declaration has the following fields:
-
name
stringIdentifier for the import parameter. Can be used in the templating to access the actual import value provided by the installation.
-
type
typeThe type of the import as described above. For backward compatibility, the
type
field is currently optional for data and target imports, but it is strongly recommended to specify it for each import declaration. -
required
bool (default:true
)If set to false, the installation does not have to provide this import.
-
default
anyIf the import is not required and not provided by the installation, this default value will be used for it.
-
imports
list of import declarationsNested imports only exist if the owning import is satisfied. Cannot be specified for a required import. See here for further details.
-
schema
JSONSchemaMust be set for imports of type
data
(only). Describes the structure of the expected import value as JSONSchema. -
targetType
stringMust be set for imports of type
target
andtargetMap
(only). It declares the type of the expected Target object. If thetargetType
does not contain a/
, it will be prefixed withlandscaper.gardener.cloud/
.
Example
imports:
- name: myimport # some unique name
required: false # defaults to true if not set
type: data # type of the imported object
schema:
type: object
properties:
username:
type: string
password:
type: string
default:
value:
username: foo
password: bar
- name: mycluster
type: target
targetType: kubernetes-cluster # will be defaulted to 'landscaper.gardener.cloud/kubernetes-cluster'
Values provided by Installations for import parameters are validated before any invasive action is done for an installation. There are two possibilities to validate the input:
-
using the
schema
attribute to describe a validation using a JSON schema.This validation always refers to a single import parameter. The definition of simple value parameters should be avoided, if possible. Instead, always a group of semantically related attributes should be aggregated into a structural value import. This can be validated as a whole.
-
using the
importExecutions
it is possible to describe templated checks on the complete set of imports and to provide a list of validation errors.
Blueprints describe formal exports whose values can be exported by using Installations to DataObjects to be consumed by other Installations, again. The following types can be exported:
-
data
This type of export is used to export arbitrary data according to its value type. The value type is described by a JSONSchema.
-
target
This type declares an export of a deployment target object. It is used in the rendered deployitems to specify the target environment for the deployment of the deployitem.
The exports are described as a list of export declarations in the blueprint
top-level field exports
. An export declaration has the following fields:
-
name
stringIdentifier for the export parameter. Can be used in the templating to access the actual export value provided by the installation.
-
type
typeThe type of the export as described above. For backward compatibility, the
type
field is currently optional for data and target exports, but it is strongly recommended to specify it for each export declaration. -
schema
JSONSchemaMust be set for exports of type
data
(only). Describes the structure of the expected export value as JSONSchema. -
targetType
stringMust be set for exports of type
target
(only). It declares the type of the expected Target object. If thetargetType
does not contain a/
, it will be prefixed withlandscaper.gardener.cloud/
.
Example
exports:
- name: myexport
type: data
schema:
type: object
properties:
username:
type: string
password:
type: string
- name: myclusterexport
type: target
targetType: kubernetes-cluster # will be defaulted to 'landscaper.gardener.cloud/kubernetes-cluster'
JSONSchemas are used to describe the structure of data
imports and exports. The provided import schema is used to validate the actual import value before executing the blueprint.
It is recommended to provide a description and an example for the structure, so that users of the blueprint know what to provide (see the json docs).
For detailed information about the jsonschema and landscaper specifics see JSONSchema Docs
The task of a Blueprint is to provide deployitems and final output for the data flow among Installations based of their input values provided by the actual Installation is evaluated for.
This is described by rule sets consisting of templates carried together with the blueprint.
All template executions get a common standardized binding:
-
imports
the imports of the installation, as a mapping from import name to assigned values
-
cd
the component descriptor of the owning component
-
components
the component descriptors of all referenced components, either directly or transitively (includes the component descriptor of the owning component too)
-
blueprintDef
the blueprint definition, as given in the installation (not the blueprint.yaml itself)
-
componentDescriptorDef
the component descriptor definition, as given in the installation (not the component descriptor itself)
Additionally, there are context specific bindings and those depending on the chosen template processor.
Example
imports:
<import-name>: <import value>
cd: <component descriptor>
blueprintDef: <blueprint definition> # blueprint definition from the Installation
componentDescriptorDef: <component descriptor definition> # component descriptor definition from the installation
The rendering result must be a YAML map document.
The rendered elements are typically expected under a dedicated certain top-level
node (e.g. deployItems
for a deployitem execution).
There are several rendering contexts:
importExecutions
rendering of additional import values derived from the input values provided by the Installation and/or cross-import input validation.deployExecutions
rendering of deployitems produced by the blueprint for the actual installation.exportExecutions
rendering of values for the export parameters of the blueprint.subinstallationExecutions
rendering of installations to be instantiated in the context of the actual blueprint execution.
If there are several templates used for some rendering tasks it might be useful to share some attributes derived from the values for the declared imports. This can avoid replicating the calculation of those values to several templates.
To support this, Blueprints may declare import executions using the
top-level field importExecutions
. It may list any number of appropriate
template executions. This can be used to enrich the set
of import bindings for further template processing steps.
The template processing is fed with the standard binding.
A template execution should return a YAML document with two optional top-level nodes:
-
bindings
mapThis node is expected to contain a map with additional bindings which will be added to the regular import bindings. This way it is even possible to modify or replace original import values for the further processing steps, for example, to provide more complex defaults based on other import values.
The additional bindings are added incrementally to the
imports
binding, meaning the order of the executions is relevant and previously added bindings are available for the processing of the following executions. -
errors
string listAlternatively the template processing may provide a list of validation errors. Like schema validations, this could be used to validate imports before invasive actions are started. But for import executions the template has access to the complete set of imports provided by the Installation and can therefore perform cross-checks.
The first execution providing errors will abort the execution of further steps.
Other nodes in the document are ignored.
Example
imports:
- name: prefix
type: data
scheme:
type: string
- name: suffix
type: data
scheme:
type: string
importExecutions:
- name: check
type: Spiff
template:
errors:
- (( imports.prefix == imports.suffix ? "prefix and suffix must be different" :~~ ))
bindings:
basename: "tempfile"
- name: compose
type: Spiff
template:
bindings:
compound: (( imports.prefix imports.basename imports.suffix ))
If the blueprint is fed with
imports:
prefix: /tmp/
suffix: .tmp
the final import bindings would look like this:
imports:
prefix: /tmp/
suffix: .tmp
basename: tempfile
compound: /tmp/tempfile.tmp
The main task of a Blueprint is to provide DeployItems. Therefore, the blueprint
manifest uses a top-level field deployExecutions
listing any number of appropriate
template executions.
Example
deployExecutions:
- name: kubernetes
type: GoTemplate
file: data/gotemplate.tmpl
The template processing is fed with the standard binding and supports state handling.
A template execution must return a YAML document with at least the top-level
node deployItems
. It is expected to contain a list of deployitem specifications.
Other nodes in the document are ignored.
These specifications will then be mapped to final DeployItems by the Landscaper.
A deployitem specification has the following fields:
-
name
stringThe name of the item in the context of the blueprint. It is used to generate the name of the final deployitem together with its scope and for referring to exported values in ete export value rendering.
The name must be unique in the context of the blueprint over all executions.
-
dependsOn
string listThis list of item names can be used to enforce an ordering for the creation. The deletion is done in the opposite order.
-
type
stringThe type of the deployitem described. This type finally determines the expected structure for the configuration, and it determines the kind of Deployer responsible for the item. This is the contract between the deployers and the deployitem creator.
-
target
target import referenceThis reference denotes the target object describing the target environment the deployitem should be deployed to, e.g. a dedicated kubernetes cluster. It refers to a target import or targetMap import and has the following fields:
import
string
Name of the import importing the referenced target.
key
string (optional)
If the import refers to a targetmap import,
key
specifies the keys of the referenced targets.updateOnChangeOnly
bool
If set on true, a successfully processed deployitem is only deployed again, if something has changed in its spec.
name
string (deprecated)
Name of the in-cluster target object. It must be taken from the
.metadata.name
field of the imported target (e.g..imports.mytarget.metadata.name
). Deprecated: reference an imported target viaimport
instead. -
labels
string mapThis map is used to attach labels to the generated deployitem.
-
configuration
anyThe structure of this field depends on the type of the deployitem. It is intended to tell the deployer about the expected target state to achieve Depending on the deployer it is possible to request information about the deployment, for example, the address of a generated load balancer for a Kubernetes service object.
This structure is the formal contract towards the deployer. It should be versioned according to the Kubernetes mechanism with
apiVersion
andkind
. But this is not enforced by the Landscaper.While the requesting of imports is not formalized (please refer to the documentation of the dedicated deployer), the deployer can provide exports in formal way in the status of a deployitem. These values are then available in the binding for the export executions.
Example rendered document:
deployItems:
- name: myfirstitem # unique identifier of the step
target:
name: my-kubernetes-cluster
namespace: my-application
config:
apiVersion: mydeployer/v1
kind: ProviderConfiguration
...
All lists of deployitem specifications of all template executions are appended to one list as they are specified in the deployExecution.
Example:
Bindings:
imports:
replicas: 3
cluster:
apiVersion: landscaper.gardener.cloud/v1alpha1
kind: Target
metadata:
name: dev-cluster
namespace: default
spec:
type: landscaper.gardener.cloud/kubernetes-cluster
config:
kubeconfig: |
apiVersion: ...
my-cdlist: # import of a component descriptor list
meta:
schemaVersion: v2
components:
- meta:
schemaVersion: v2
component:
name: component-1
version: v1.0.1
... # same structure as for key "cd"
- meta:
schemaVersion: v2
component:
name: component-2
version: v1.0.1
...
cd:
meta:
schemaVersion: v2
component:
name: my-component
version: v1.0.0
componentReferences:
- name: abc
componentName: my-referenced-component
version: v1.0.0
resources:
- name: nginx-ingress-chart
version: 0.30.0
relation: external
access:
type: ociRegistry
imageReference: nginx:0.30.0
blueprintDef:
ref:
resourceName: gardener
# inline:
# filesystem: # vfs filesystem
# blueprint.yaml:
# apiVersion: landscaper.gardener.cloud/v1alpha1
# kind: Blueprint
# ...
componentDescriptorDef:
ref:
# repositoryContext:
# type: ociRegistry
# baseUrl: eu.gcr.io/myproj
componentName: github.com/gardener/gardener
version: v1.7.2
Deploy Execution:
deployExecutions:
- name: default
type: GoTemplate
template: |
deployItems:
- name: deploy
type: landscaper.gardener.cloud/helm
target:
import: cluster # will be replaced with a reference to the in-cluster target object the referenced import refers to
config:
apiVersion: helm.deployer.landscaper.gardener.cloud/v1alpha1
kind: ProviderConfiguration
chart:
{{ $resource := getResource .cd "name" "nginx-ingress-chart" }}
ref: {{ $resource.access.imageReference }} # resolves to nginx:0.30.0
values:
replicas: {{ .imports.replicas }} # will resolve to 3
{{ $component := getComponent .cd "name" "my-referenced-component" }} # get a component that is referenced
{{ $resource := getResource $component "name" "ubuntu" }}
usesImage: {{ $resource.access.imageReference }} # resolves to ubuntu:0.18.0
After a successful deployment of the generated DeployItems the Blueprint may use the provided export information of the deployitems to generate values for its export parameters.
Therefore, the blueprint manifest uses a top-level field exportExecutions
listing
any number of appropriate template executions.
Example
exportExecutions:
- name: kubernetes
type: GoTemplate
template: |
exports:
myexport:
username: ...
password: ...
The template processing is fed with the standard binding and supports state handling. Additional bindings are provided to access the exports of generated elements:
-
deployitems
mapThis map contains a map entry for all generated deployitems according to their configured name. The entry then contains the configured deployitem exports.
-
dataobjects
mapThis map contains the values of the nested data objects provided by nested installations.
-
targets
mapThis map contains the values of the nested targets provided by all nested installations.
-
values
map (deprecated)This node is a map with the entries shown above.
A template execution must return a YAML document with at least the top-level
node exports
. It is expected to contain a map of export parameters mapped to
concrete values. Other nodes in the document are ignored.
Example rendered document:
exports:
myexport: # name of the export parameter
username: ...
password: ...
The result of multiple template executions exports will be merged in the defined order, whereas the latter defined values overwrites previous templates. The final result must provide a value for all export parameters.
Example
exportExecutions:
- name: default
type: GoTemplate
template: |
exports:
url: https://{{ .deployitems.ingressPrefix }}.{{ .dataobjects.domain }} # resolves to https://my-pref.example.com
cluster:
type: {{ .targets.dev-cluster.spec.type }}
config: {{ .targets.dev-cluster.spec.config }}
The typical export is a data value. The structure for arbitrary data is completely free. The value is just taken as it is defined by the dedicated exports map entry.
A data export of a blueprint can be exported by a using installation. The result will be a DataObject of the name specified in the exporting installation in the parent scope of this installation.
It is possible to export targets, also. Hereby the target will be created in the scope of the installation the blueprint is instantiated for. To export a target the structure below the export name must match a target specification:
-
type
stringThe type of the target.
-
configuration
anyThe configuration of the target. The structure depends on the type of the target.
-
labels
string mapThis map is passed to the
labels
section of the generated target object. -
annotations
string mapThis map is passed to the
annotations
section of the generated target object.
The specification does not allow to provide a name for the target. It is created in the parent scope of the installation the blueprint is instantiated for, if this export is exported by the installation, also. In this case the name in this scope is provided by the export definition of the installation.
Blueprints may contain installation specifications which will result in installations when the blueprint is instantiated through an installation. They have a naming scope that is finally defined by the installation the blueprint is instantiated for. Naming scope means that subinstallations can only import data that is also imported by the parent or exported by other subinstallations with the same parent.
Subinstallation specifications offer the same configuration as real installations except that the used blueprints have to be defined in the component descriptor of the blueprint (either as resource or by a component reference). Inline blueprints are also possible, although not recommended for productive purposes.
Subinstallations can be defined in two flavors;
In any case the result is a list of installation specifications. Every specification is mapped to a dedicated subinstallation object in the context of the actual installation the blueprint is instantiated for.
All possible options to define a subinstallation can be used in parallel and are summed up. The name of all described subinstallations must be unique in the context of the blueprint. But they don't need to be unique in the landscaper namespace, because the live only in the context of installation the blueprint is instantiated for. If the blueprint is used for multiple installations (flat or nested, again), the generated installations and used data objects are living in separate naming scopes.
The installation specification (although technically called InstallationTemplate
it is not
templated by a template processor but used by the Landscaper to generate a final Installation object)
has the following format:
A specification will still be processed by the landscaper before putting it as regular installations into the landscaper data plane:
- it handles the scopes
- it maps the usage of directly used parent imports
- it maps the usage of the component descriptor of the top-level installation.
Example
apiVersion: landscaper.gardener.cloud/v1alpha1
kind: InstallationTemplate
name: my-subinstallation # must be unique
blueprint:
ref: cd://componentReferences/ingress/resources/blueprint #cd://resources/myblueprint
# filesystem:
# blueprint.yaml: abc...
# define imported dataobjects and target from other installations or the
# parents import.
# It's the same syntax as for default installations.
imports:
data:
- name: "" # data import name
dataRef: "" # dataobject name
targets:
- name: "" # target import name
target: "" # target name
- name: ""
targetMapRef: "" # references a targetmap import of the parent
#importMappings: {}
exports:
targets:
- name: "" # target export name
target: "" # target name
#exportMappings: {}
Static installations are configured as a list under the top-level field
subinstallations
in the blueprint manifest.
Every entry can either be a reference to file (using the field file
) provided
in the blueprint's filesystem or directly inlined. Only one of
both flavors can be used for one list entry.
Example
subinstallations:
- file: installations/installation.yaml
- apiVersion: landscaper.gardener.cloud/v1alpha1
kind: InstallationTemplate
name: mysubinst
imports:
targets:
- name: "also-my-foo-targets"
targetMapRef: "my-foo-targets"
Static installations are not templated and cannot refer to import values.
Similar to how deployitems can be defined, it is also possible to create
subinstallations based on the imports by using template executions.
Templated installations are configured as a list under the top-level field
subinstallationExecutions
in the blueprint manifest.
The template processing is fed with the standard binding and supports state handling.
A template execution must return a YAML document with at least the top-level
node subinstallations
. It is expected to contain a list of installation
specifications. Other nodes in the document are ignored.
_Example:
subinstallationExecutions:
- name: default
type: GoTemplate
template: |
subinstallations:
- apiVersion: landscaper.gardener.cloud/v1alpha1
kind: InstallationTemplate
name: my-subinstallation # must be unique
blueprint:
ref: cd://componentReferences/ingress/resources/blueprint
...