Within a blueprint or catalog item, entities can be configured. The rules for setting this configuration, including when composing and extending existing entities, is described in this section.
Within a YAML file, entity configuration should be supplied within a brooklyn.config
map. It is also possible to supply configuration at the top-level of the entity. However, that approach is discouraged as it can sometimes be ambiguous (e.g. if the config key is called “name” or “type”), and also it does not work in all contexts such as for an enricher's configuration.
A simple example is shown below:
{% highlight yaml %} services:
If no config value is supplied, the default for that config key will be used. For example, http.port
would default to 8080 if not explicitly supplied.
Some config keys also have a short-form (e.g. httpPort
instead of http.port
would also work in the YAML example above). However, that approach is discouraged as it does not work in all contexts such as for inheriting configuration from a parent entity.
When defining an entity in the catalog, it can include configuration values like any other blueprint (i.e. inside the brooklyn.config
block).
It can also explicitly declare config keys, using the brooklyn.parameters
block. The example below illustrates the principle:
{% highlight yaml %} {% read example_yaml/entity-config-catalog.yaml %} {% endhighlight yaml %}
Once added to the catalog, it can be used with the simple blueprint below (substituting the location of your choice). Because no configuration has been overridden, this will use the default value for custom.message
, and will use the given values for launch.command
and checkRunning.command
:
{% highlight yaml %} {% read example_yaml/entity-config-app.yaml %} {% endhighlight yaml %}
For details of how to write and add catalog items, see Catalog, and for a complete reference on the syntax of brooklyn.parameters
see that section of the YAML Reference.
The config keys in the brooklyn.parameters
can also have a list of constraints defined, for what values are valid. If more than one constraint is defined, then they must all be satisfied. The constraints can be any of:
required
: deployment will fail if no value is supplied for this config keyregex: <pattern>
: the value must match the regular expression <pattern>
glob: <pattern>
: the value must match the bash-style wildcard glob <pattern>
urlExists: <url>
: the server must be able to resolve and access the URL <url>
forbiddenIf: <key>
: setting a value is disallowed if the config key <key>
has a value setforbiddenUnless: <key>
: setting a value is disallowed if the config key <key>
does not have a value setrequiredIf: <key>
: a value is required if the config key <key>
has a value setrequiredUnless: <key>
: a value is required if the config key <key>
does not have a value setPredicate
, declared using the DSL $brooklyn:object
.This is illustrated in the example below:
{% highlight yaml %} {% read example_yaml/entity-constraint-catalog.yaml %} {% endhighlight yaml %}
An example usage of this toy example, once added to the catalog, is shown below:
{% highlight yaml %} {% read example_yaml/entity-constraint-app.yaml %} {% endhighlight yaml %}
Often configuration objects will be “primitives” -- string
, integer
, boolean
, char
. But they can be more sophisticated:
list
and map
are supported, and can be declared with generics, e.g. type: map<string,boolean>
value-wrapper
that can be a value or can be a Brooklyn DSL expressionport
which allows specifying a range or set of values, eg 8080+
or 80,443,[8000-8999]
duration
which recognises textual input, eg 2 minutes
bean
using bean-with-type
(see Catalog)Configuration can be inherited from a super-type, and from a parent entity in the runtime management hierarchy. This applies to entities and locations. In a future release, this will be extended to also apply to policies and enrichers.
When a blueprint author defines a config key, they can explicitly specify the rules for inheritance (both for super/sub-types, and for the runtime management hierarchy). This gives great flexibilty, but should be used with care so as not to surprise users of the blueprint.
The default behaviour is outlined below, along with examples and details of how to explilcitly define the desired behaviour.
There are several places that a configuration value can come from. If different values are specified in multiple places, then the order of precedence is as listed below:
When using an entity from the catalog, its configuration values can be overridden. For example, consider the entity-config-example
added to the catalog in the section Configuration in a Catalog Item. We can override these values. If not overridden, then the existing values from the super-type will be used:
{% highlight yaml %} {% read example_yaml/entity-config-override-app.yaml %} {% endhighlight yaml %}
In this example, the custom.message
overrides the default defined on the config key. The launch.command
overrides the original command. The other config (e.g. checkRunning.command
) is inherited unchanged.
It will write out: Sub-type launch command: Goodbye
.
Configuration passed to an entity is inherited by all child entities, unless explicitly overridden.
In the example below, the wars.root
config key is inherited by all TomcatServer entities created under the cluster, so they will use that war:
{% highlight yaml %} services:
In the above example, it would be better to have specified the wars.root
configuration in the TomcatServer
entity spec, rather than at the top level. This would make it clearer for the reader what is actually being configured.
The technique of inherited config can simplify some blueprints, but care should be taken. For more complex (composite) blueprints, this can be difficult to use safely; it relies on knowledge of the internals of the child components. For example, the inherited config may impact multiple sub-components, rather than just the specific entity to be changed. This is particularly true when using complex items from the catalog, and when using common config values (e.g. install.version
).
An alternative approach is to declare the expected configuration options at the top level of the catalog item, and then (within the catalog item) explicitly inject those values into the correct sub-components. Users of this catalog item would set only those exposed config options, rather than trying to inject config directly into the nested entities.
When writing blueprints that rely on inheritance from the runtime management hierarchy, it is important to understand how config keys that use DSL will be evaluated. In particular, when evaluating a DSL expression, it will be done in the context of the entity declaring the config value (rather than on the entity using the config value).
For example, consider the config value $brooklyn:attributeWhenReady("host.name")
declared on entity X, and inherited by child entity Y. If entity Y uses this config value, it will get the “host.name” attribute of entity X.
Below is another (contrived!) example of this DSL evaluation. When evaluating refExampleConfig
, it retrievies the value of exampleConfig
which is the DSL expression, and evaluates this in the context of the parent entity that declares it. Therefore $brooklyn:config("ownConfig")
returns the parent's ownConfig
value, and the final result for refExampleConfig
is set to “parentValue”:
{% highlight yaml %} services:
type: org.apache.brooklyn.entity.stock.BasicApplication brooklyn.config: ownConfig: parentValue exampleConfig: $brooklyn:config(“ownConfig”)
brooklyn.children:
However, the web-console also shows other misleading (incorrect!) config values for the child entity. It shows the inherited config value of exampleConfig
as “childValue” (because the REST API did not evaluate the DSL in the correct context, when retrieving the value! See https://issues.apache.org/jira/browse/BROOKLYN-455.
For some configuration values, the most logical behaviour is to merge the configuration value with that in the super-type. This depends on the type and meaning of the config key, and is thus an option when defining the config key.
Currently it is only supported for merging config keys of type Map.
Some common config keys will default to merging the values from the super-type. These config keys
include those below. The value is merged with that of its super-type (but will not be merged with the value on a parent entity):
shell.env
: a map of environment variables to pass to the runtime shellfiles.preinstall
: a mapping of files, to be copied before install, to destination name relative to installDirtemplates.preinstall
: a mapping of templates, to be filled in and copied before pre-install, to destination name relative to installDirfiles.install
: a mapping of files, to be copied before install, to destination name relative to installDirtemplates.install
: a mapping of templates, to be filled in and copied before install, to destination name relative to installDirfiles.runtime
: a mapping of files, to be copied before customisation, to destination name relative to runDirtemplates.runtime
: a mapping of templates, to be filled in and copied before customisation, to destination name relative to runDirprovisioning.properties
: custom properties to be passed in when provisioning a new machineA simple example of merging shell.env
is shown below (building on the entity-config-example
in the section Configuration in a Catalog Item). The environment variables will include the MESSAGE
set in the super-type and the MESSAGE2
set here:
{% highlight yaml %} location: aws-ec2:us-east-1 services:
To explicitly remove a value from the super-type's map (rather than adding to it), a blank entry can be defined.
An entity (which extends SoftwareProcess
) can define a map of provisioning.properties
. If the entity then provisions a location, it passes this map of properties to the location for obtaining the machine. These properties will override and augment the configuration on the location itself.
When deploying to a jclouds location, one can specify templateOptions
(of type map). Rather than overriding, these will be merged with any templateOptions defined on the location.
In the example below, the VM will be provisioned with minimum 2GB RAM and minimum 2 cores. It will also use the merged template options value of {placementGroup: myPlacementGroup, securityGroupIds: sg-000c3a6a}
:
{% highlight yaml %} location: aws-ec2:us-east-1: minRam: 2G templateOptions: placementGroup: myPlacementGroup services:
The merging of templateOptions
is shallow (i.e. maps within the templateOptions
are not merged). In the example below, the userMetadata
value within templateOptions
will be overridden by the entity's value, rather than the maps being merged; the value used when provisioning will be {key2: val2}
:
{% highlight yaml %} location: aws-ec2:us-east-1: templateOptions: userMetadata: key1: val1 services:
For some configuration values, the most logical behaviour is for an entity to “consume” the config key's value, and thus not pass it down to children in the runtime type hierarchy. This is called “not re-inherited”.
Some common config keys that will not re-inherited include:
install.command
(and the pre.install.command
and post.install.command
)customize.command
(and the pre.customize.command
and post.customize.command
)launch.command
(and the ``pre.launch.commandand
post.launch.command`)checkRunning.command
stop.command
VanillaWindowsProcess
PowerShell.files.preinstall
, templates.preinstall
, etc)An example is shown below. Here, the “logstash-child” is a sub-type of VanillaSoftwareProcess
, and is co-located on the same VM as Tomcat. We don‘t want the Tomcat’s configuration, such as install.command
, to be inherited by the logstash child. If it was inherited, the logstash-child entity might re-execute the Tomcat's install command! Instead, the install.command
config is “consumed” by the Tomcat instance and is not re-inherited:
{% highlight yaml %} services:
“Not re-inherited” differs from “never inherited”. The example below illustrates the difference, though this use is discouraged (it is mostly for backwards compatibility). The post.install.command
is not consumed by the BasicApplication
, so will be inherited by the Tomcat8Server
which will consume it. The config value will therefore not be inherited by the logstash-child
.
{% highlight yaml %} services:
For some configuration values, the most logical behaviour is for the value to never be inherited in the runtime management hierarchy.
Some common config keys that will never inherited include:
defaultDisplayName
: this is the name to use for the entity, if an explicit name is not supplied. This is particularly useful when adding an entity in a catalog item (so if the user does not give a name, it will get a sensible default). It would not be intuitive for all the children of that entity to also get that default name.
id
: the id of an entity (as supplied in the YAML, to allow references to that entity) is not inherited. It is the id of that specific entity, so must not be shared by all its children.
The javadoc in the code is useful for anyone who wants to go deep! See org.apache.brooklyn.config.BasicConfigInheritance
and org.apache.brooklyn.config.ConfigInheritances
in the repo https://github.com/apache/brooklyn-server.
When defining a new config key, the exact semantics for inheritance can be defined. There are separate options to control config inheritance from the super-type, and config inheritance from the parent in the runtime management hierarchy.
The possible modes are:
NEVER_INHERITED
: indicates that a key's value should never be inherited (even if defined on an entity that does not know the key). Most usages will prefer NOT_REINHERITED
.
NOT_REINHERITED
: indicates that a config key value (if used) should not be passed down to children / sub-types. Unlike NEVER_INHERITED
, these values can be passed down if they are not used by the entity (i.e. if the entity does not expect it). However, when used by a child, it will not be passed down any further. If the inheritor also defines a value the parent's value is ignored irrespective (as in OVERWRITE
; see NOT_REINHERITED_ELSE_DEEP_MERGE
if merging is desired).
NOT_REINHERITED_ELSE_DEEP_MERGE
: as NOT_REINHERITED
but in cases where a value is inherited because a parent did not recognize it, if the inheritor also defines a value the two values should be merged.
OVERWRITE
: indicates that if a key has a value at both an ancestor and a descendant, the descendant and his descendants will prefer the value at the descendant.
DEEP_MERGE
: indicates that if a key has a value at both an ancestor and a descendant, the descendant and his descendants should attempt to merge the values. If the values are not mergable, behaviour is undefined (and often the descendant's value will simply overwrite).
The YAML support for explicitly defining the inheritance mode is still work-in-progress. The options documented below will be enhanced in a future version of Brooklyn, to better support the modes described above.
In a YAML blueprint, within the brooklyn.parameters
section for declaring new config keys, one can set the mode for inheritance.type
and inheritance.runtime
(i.e. for inheritance from the super-type, and inheritance in the runtime management hierarchy). The possible values are:
deep_merge
: the inherited and the given value should be merged; maps within the map will also be mergedalways
: the inherited value should be used, unless explicitly overridden by the entitynone
: the value should not be inherited; if there is no explicit value on the entity then the default value will be usedBelow is a (contrived!) example of inheriting the example.map
config key. When using this entity in a blueprint, the entity‘s config will be merged with that defined in the super-type, and the parent entity’s value will never be inherited:
{% highlight yaml %} brooklyn.catalog: items:
The blueprints below demonstrate the various permutations for setting configuration for the config example.map
. This can be inspected by looking at the entity‘s config. The config we see for app1 is the inherited {MESSAGE: "Hello"}
; in app2 we define additional configuration, which will be merged to give {MESSAGE: "Hello", MESSAGE_IN_CHILD: "InChild"}
; in app3, the config from the parent is not inherited because there is an explicit inheritance.runtime of “none”, so it just has the value {MESSAGE: "Hello"}
; in app4 again the parent’s config is ignored, with the super-type and entity's config being merged to give {MESSAGE: "Hello", MESSAGE_IN_CHILD: "InChild"}
.
{% highlight yaml %} location: aws-ec2:us-east-1 services:
type: org.apache.brooklyn.entity.stock.BasicApplication name: app1 brooklyn.children:
type: org.apache.brooklyn.entity.stock.BasicApplication name: app2 brooklyn.children:
type: org.apache.brooklyn.entity.stock.BasicApplication name: app3 brooklyn.config: example.map: MESSAGE_IN_PARENT: InParent brooklyn.children:
type: org.apache.brooklyn.entity.stock.BasicApplication name: app4 brooklyn.config: example.map: MESSAGE_IN_PARENT: InParent brooklyn.children:
A limitations of inheritance.runtime
is when inheriting values from parent and grandparent entities: a value specified on the parent will override (rather than be merged with) the value on the grandparent.
A current limitation is that sub-type inheritance is not supported for configuration of policies and enrichers. The current behaviour is that config is not inherited. The concept of inheritance from the runtime management hierarchy does not apply for policies and enrichers (they do not have “parents”; they are attached to an entity).