When you compose applications, application components can share information with each other by exporting and consuming environment-wide service resources.
Exporting and consuming service resources
Service resources help components work together even if they are managed by Puppet on different servers.
An environment service resource works like an exported resource, providing data for other parts of the application to consume but also helps application components express inter-dependent relationships.
Specifically, service resources:
Transport data between nodes participating in an application.
Provide an abstraction so that you no longer need to hard-code dependencies in modules.
Provide service resource availability tests at the node level to understand when a component is “ready.” Service readiness can tell the Puppet agent to pause execution until the required service is available.
Live in a module’s
type
directory and uses the typical path<ENVIRONMENT DIRECTORY>/modules/<MODULE NAME>/lib/puppet/type/<SERVICE RESOURCE.rb>
.
Essentially, service resources serve as checkpoints that the Puppet master uses to determine when it is safe to trigger a Puppet run on a node that consumes a resource.
Service resource instances must be unique across an environment. Like other kinds of resource uniqueness, this means they must have a unique type and title. Also, just like exported resources, service resources can be located using resource collectors.
Service resources are stored, like other resources, in PuppetDB. They are subject to the lifecycle of any resource and the node whose catalog they are in. In addition, service resources adhere to the standard PuppetDB rules for purging nodes and resources.
Producing a service resource with export
When declaring an application component, the export
statement is used to produce an instance of the service resource as produced by the node managing that application component.
In the following example, the myssql::db
application component exports the Sql
service resource. (The values of the parameters for the exported resource are defined in the component.)
mysql::db { $name:
user => $wp_db_user,
password => $wp_db_password,
export => Sql[$name],
}
Syntax
'export' '=>' CapabilityType[Name]
The general form of the export
statement is structured like an attribute/value pair:
- The
export
keyword. - A
=>
(called an arrow, “fat comma,” or “hash rocket”). - The name of the capability type, followed by a pair of brackets (
[ ]
) that contain the name to be given the service resource.
The CapabilityType
must be the name of a service type for which there is a produces
declaration for the type of resource being instantiated.
The resulting service resource will have the name that is specified in the export
reference; the remaining attributes of the service resource will be filled by evaluating the corresponding produces
declaration.
The produced service resource must be unique, by type and title, within the current environment.
Consuming a service resource with consume
When instantiating a resource that is declared to consume a capability, the consume
statement can be used to fill attributes of the resource being instantiated.
In the following example, the values of the wordpress::instance::app are assigned when the component consumes the Sql
service resource.
wordpress::instance::app { $name:
install_dir => "/var/www/${name}",
consume => Sql[$name],
}
Syntax
'consume''=>' CapabilityType[Name]
The general form of the consume
statement is structured like an attribute/value pair:
- The
consume
keyword. - A
=>
(called an arrow, “fat comma,” or “hash rocket”). - The name of the capability type, followed by a pair of brackets (
[ ]
) that contain the name to be given the service resource.
The reference CapabilityType[Name]
must specify a service resource that is produced on a node, including the current node, in the current environment.
Using require between components
Requiring a resource declares a dependency between a component and a service resource.
If you need to depend on a service resource but don't need to consume its information, you can use the
, which behaves as it does elsewhere in Puppet.require
metaparameter
Mapping information from components to service resources
You use the produces
and consumes
statements to map information between components and service resources.
Mapping with the produces statement
The produces
statement allows you to define a mapping from a component providing a service to a service resource. Further, it allows you to define how a service resource is created when an application is configured, and it defines the complete content of the service resource.
When you produce a service resource, you have access to all the top-level variables and facts available on the source node, as well as the properties of the source component. As noted below, function calls are evaluated only during Puppet runs that produce the source node's catalog.
In this example, the mysql::db
defined type produces the Sql
resource capability.
Mysql::Db produces Sql {
user => $user,
password => $password,
host => pick($::mysql_host_override, $::fqdn),
#port => not used here, will default as described in the definition
database => $dbname,
type => 'mysql',
}
Let's examine some of the use-cases in this statement:
-
To capture information about the component, its properties can be accessed directly. In this example
user
,password
, anddatabase
of theSql
service resource is set directly from theMysql::Db
user
,password
, anddatabase
properties. -
To capture information about the node hosting the component, facts describe the state at the time of configuration. In this example the
host
of theSql
service resource is set to the fully qualified hostname ($::fqdn
) of the node running the database. -
To capture information about the node's environment, top-level variables, set by the node classifier, can be accessed. In this example the
$::mysql_host_override
could be set through the node classifier to define the DNS name to use for accessing databases on a specific node. -
Constant values can be used to supply additional information. In this example we specify the capability's
type
asmysql
to allow its consumers to choose the correct database connector. -
Function calls allow access to a significant part of Puppet's power, from simple
lowercase()
to complexhiera()
lookups. Note that function calls---like everything else here---are only evaluated during the Puppet run when the producing node's catalog is compiled. In this example thepick()
function is used to choose between the node classifier supplied value and the fact value, using the latter as the default.
produces
statements, as they depend on the evaluation order during catalog compilation, and non-top-level values may not actually be available for evaluation.Syntax
A produces
statement has the following syntax:
ResourceType 'produces' CapabilityType ( '{'
(AttributeName '=>' Expression ',')*
'}' )?
The general form of the produces
statement contains:
- The name of the resource type producing the service resource.
- The
produces
keyword. - The name of the service resource created.
- An opening curly brace (
{
). - Any number of attribute and value pairs, each of which consists of:
- An attribute name, which is a lowercase word with no quotes.
- A
=>
(called an arrow, “fat comma,” or “hash rocket”). - A value, which can have any data type.
- A trailing comma.
- A closing curly brace (
}
).
Behavior
- The
ResourceType
can be any normal resource type, including classes and defined types. - The
CapabilityType
must be a service resource type. - The statement does not result in the creation of a service resource; instead, it serves as the blueprint for creating the service resource using the
export
statement. - Note that for any pair of
(ResourceType, CapabilityType)
, we can have at most oneproduces
statement. If there is more than oneproduces
statement, Puppet will not know which one to use, and the compiler will flag this as an error. - The possible names for the
AttributeName
are the names of the attributes of theCapabilityType
. For any attribute with nameaname
which is not explicitly listed, we act as if the user has writtenaname => $aname
. - The
Expression
for eachAttributeName
is evaluated in a scope that contains top-level variables, including the facts for the node on which the underlyingResourceType
is being instantiated. In addition, the scope contains bindings for all the parameters/attributes from the instantiation of the underlyingResourceType
.
Mapping with the consumes statement
The consumes
statement allows you to define which components use a resource capability produced by some other component.
In the following example, the Wordpress::Instance::App
defined type consumes the Sql
resource capability. Here the attributes for the defined type are derived from the attributes of the service resource it's consuming (for example, the Wordpress::Instance::App
db_password
parameter setting comes from the $password
parameter setting defined in the Sql
service resource).
Wordpress::Instance::App consumes Sql {
# defined_type_attribute => $capability_attribute,
db_name => $name,
db_host => $host,
db_user => $user,
db_password => $password,
}
Now these two defined types (Mysql::Db
and Wordpress::Instance::App
) can exchange data about themselves through the Sql
service resource. Service resources make it possible to create interfaces between services (potentially across nodes) from existing code without modification. Further, since they provide a level of abstraction at the service level, you could swap out mysql::db
for postgresql::server::db
, and the consuming service wouldn't mind in the least.
Note: Properties specified in a component will override any values contained in a service resource.
Syntax
A consumes
statement has the following syntax:
ResourceType 'consumes' CapabilityType ( '{'
(AttributeName '=>' Expression ',')*
'}' )?
The general form of the consumes
statement contains:
- The name of the resource type consuming the service resource.
- The
consumes
keyword. - The name of the service resource consumed.
- An opening curly brace (
{
). - Any number of attribute and value pairs, each of which consists of:
- An attribute name, which is a lowercase word with no quotes.
- A
=>
(called an arrow, “fat comma,” or “hash rocket”). - A value, which can have any data type.
- A trailing comma.
- A closing curly brace (
}
).
Behavior
- Similar to
produces
, theResourceType
must be a normal resource type, and theCapabilityType
must be a service resource type. There can be at most oneconsumes
statement for every(ResourceType, CapabilityType)
pair. - The
consumes
statement alone does not change how an instance of theResourceType
is created. When an instance of the givenResourceType
is instantiated and uses theconsume
statement, the mappings established by theconsumes
statement are used to fill the attributes/parameters of the resource from the service resource. - The possible names for the
AttributeName
are all the names of attributes/parameters thatResourceType
accepts. For each attribute/parameter nameaname
of theResourceType
, theconsumes
statement will act as if the statement contained a mappinganame => $aname
as long as theCapabilityResource
has an attributeaname
and the attribute/parameter of the instance ofResourceType
is not set explicitly in the resource instantiation. Any attribute/parameter name ofResourceType
that does not coincide with the name of an attribute ofCapabilityType
will not be affected by theconsumes
statement. It is legal to not use some of the attributes of theCapabilityType
in the mapping at all. - The
Expression
for eachAttributeName
is evaluated in a scope that contains top-level variables as well as a variable for each attribute of theCapabilityResource
, bound to the value of that attribute in the consumedCapabilityResource
.
Implicit parameter mappings
If the attribute names of both defined types that need to exchange data are identical, the mapping between them within a service resource is automatic.
Let's say you have a defined type called mysql::db
(that produces Sql
) and a defined type called wordpress::app
(that consumes Sql
), and both share the attribute name, $param
.
You can simply express this as:
mysql::db produces Sql
wordpress::app consumes Sql