The puppet.yaml
file contains Puppet specific settings, overrides and other
customizations to build the Puppet module for the product.
A puppet.yaml
file should derive from Provider::Puppet::Config
object.
Please note that you may find Ruby code inlined in the
puppet.yaml
throughout this doc and in the products/*/puppet.yaml. The main reason for that are 2 fold:
- Puppet is in Ruby. The code listed will be placed into the final product generated, so it has to be in Ruby. If your provider is targeting something on another language, e.g. Go or Python, those scripts will likely be written in Go or Python respectively.
- We decided to inline the Ruby functions because they were usually small and managing many small files. That was an arbitrary decision, but localized to the Puppet provider. If you write another provider you are free to do in a different way if you wish.
You will still have to deal with a minor amount of Ruby, mostly [ERB (Embedded RuBy)][erb-home]. However it will be very minimal and restricted to helping you build the code in your target language. For example you can use ERB to iterate through objects, properties and other niceties provided by the compiler. For example:
<% object.properties.each do |prop| ... your code (or template) iterated with every property end -%>
Example:
--- !ruby/object:Provider::Puppet::Config
manifest: !ruby/object:Provider::Puppet::Manifest
version: '0.1.0'
source: 'https://github.com/GoogleCloudPlatform/puppet-google-compute'
...
...
--- !ruby/object:Provider::Puppet::Config
manifest: !ruby/object:Provider::Puppet::Manifest
version:
source:
homepage:
issues:
summary:
tags:
- ...
requires:
- !ruby/object:Provider::Config::Requirements
- ...
operating_systems:
- !ruby/object:Provider::Config::OperatingSystem
- ...
objects: !ruby/object:Api::Resource::HashArray
<ObjectName>:
create:
delete:
flush:
resource_to_request_patch:
resource_to_query:
provider_helpers:
visible:
unwrap_resource:
resource_to_request:
return_if_object:
include:
- <file>
- ...
functions:
- !ruby/object:Provider::Puppet::Function
name:
description:
arguments:
- !ruby/object:Provider::Puppet::Function::Argument
name:
type:
description:
- ...
examples:
- ...
notes:
- ...
examples: !ruby/object:Api::Resource::HashArray
<ObjectName>:
- <object_name>.pp
- delete_<object_name>.pp
- ...
files: !ruby/object:Provider::Config::Files
copy:
<target_file>: <source_file>
...
compile:
<target_file>: <source_file>
...
test_data: !ruby/object:Provider::Config::TestData
network: !ruby/object:Api::Resource::HashArray
<ObjectName>:
- <file>
- ...
style:
- !ruby/object:Provider::Config::StyleException
name:
pinpoints:
- function:
exceptions:
- ...
- module:
exceptions:
- ...
- test: matrix > ...
exceptions:
- ...
- ...
- provider/puppet/common~operating_systems.yaml
- provider/puppet/common~copy.yaml
- provider/puppet/common~compile~before.yaml
- provider/puppet/common~compile~after.yaml
When creating examples you can inlcude the credential block that defines, and explains how to use the credentials:
- templates/puppet/examples~credential.pp.erb
See Compute instance.pp example as reference.
TODO(nelsonjr): Get a list of features here and how they relate to the settings.
A hash array is the same as a hash with the exception that the key should match
an existing object. If the object is not defined in the api.yaml
the compiler
will fail with error.
This is to ensure deleted objects are not left behind and litters the
puppet.yaml
files.
Every Puppet module contains a manifest, which provides the basic information for the module. This information is parsed by Puppet (and Puppet Forge) to reason about the module (name, version, etc).
Example:
manifest: !ruby/object:Provider::Puppet::Manifest
version: '0.1.0'
...
The version of the module, in SemVer format. Example:
version: '0.1.0'
The URL of the source code for the module. Example:
source: 'https://github.com/GoogleCloudPlatform/puppet-google-compute'
The URL of the project page for the module. Example:
homepage: 'https://github.com/GoogleCloudPlatform/puppet-google-compute'
The URL where bugs, issues and feature requests are tracked. Example:
issues:
'https://github.com/GoogleCloudPlatform/puppet-google-compute/issues'
A short description of the module. Example:
summary: 'A Puppet module to manage Google Compute Engine resources'
A list of tags for the module. Used by Puppet Forge to build keyword search. Example:
tags:
- google
- cloud
- compute
- engine
- gce
A list of modules that this product module depends on. Example:
requires:
- !ruby/object:Provider::Config::Requirements
name: 'google-gauth'
versions: '< 0.2.0'
Note:
google-gauth
should always be listed in the required modules.
The list of operating systems (and versions) that this module fully supports. Example:
operating_systems:
- !ruby/object:Provider::Config::OperatingSystem
name: RedHat
versions:
- '6'
- '7'
Note that we have a file that can be included with the common operating systems we run Puppet modules on. Then the definition should be:
operating_systems:
<%= indent(include('provider/puppet/common~operating_systems.yaml'), 4) %>
Provides the root of customizations for a resource defined in the api.yaml
.
Example (Compute Disk
cannot be changed via API updates):
objects: !ruby/object:Api::Resource::HashArray
Disk:
flush: raise 'Disk cannot be edited'
Creates a custom create
function. Puppet uses create
when it detects the
object does not exist and user specified ensure => present
in the manifest.
Example: products/compute/puppet.yaml
Creates a custom delete
function. Puppet uses delete
when it detects the
object exists and user specified ensure => absent
in the manifest.
Example: products/compute/puppet.yaml
Creates a custom flush
function. Puppet uses flush
to update values for the
resources if they mismatch with the catalog.
Example: products/compute/puppet.yaml
Note that
flush
is called aftercreate
ordelete
. Usually you should not executeflush
whencreate
ordelete
is called. Those functions by default assign true to a boolean to@created
and@deleted
respectively. So a guard like this is in the defaultflush
function:
return if @created || @deleted || !@dirty
Code provided in this section will be added to resource_to_request
method.
This allows performing object specific changes to the request before it is send
to the API. This is useful when there are special changes to the object being
submitted aside from the properties defined in the api.yaml
file.
Example (Compute BackendService
requires that we provide the
original fingerprint
value to avoid concurrent changes since our last read):
objects: !ruby/object:Api::Resource::HashArray
BackendService:
resource_to_request_patch: |
unless @fetched.nil?
# Convert to pure JSON
request = JSON.parse(request.to_json)
request['fingerprint'] = @fetched['fingerprint']
end
A function that is used to filter objects returned from the API. When the API
does not provide a GET
operation to fetch the object (e.g. fetching a VM by
name and zone), but instead it provides a LIST
operation that returns all
objects in a group (e.g. all DNS records for a zone) we need to filter the
results to find the object user is determine to operate on.
Example: products/compute/puppet.yaml
Defines provider specific changes to the provider.
Emits or suppresses specific functions from being generated in the object. This is useful when the provider needs to have a special 'snowflake' version of the function because the API is not straightforward.
Functions that can be overriden:
- unwrap_resource
- resource_to_request
- return_if_object
Example:
objects: !ruby/object:Api::Resource::HashArray
ResourceRecordSet:
provider_helpers:
visible:
unwrap_resource: false
Appends the files listed into the provider generated file
Example (Compute includes a file with helper functions to be
used by the Disk
object):
objects: !ruby/object:Api::Resource::HashArray
Disk:
provider_helpers:
include:
- 'products/compute/helpers/provider_disk_type.rb'
Deprecated: Properties are automatically inferred and included into the providers and types automatically.
Specifies extra tests to be added to the rspec unit tests.
This will display code that should override auto-generated testing code. View the DNS tests for the most comprehensive view of altered tests.
Describes Puppet client functions provided by this module.
To add the same documentation to the function code use the provider function
emit_function_doc(name)
. For exampleemit_function_doc('gcompute_image_family')
.
TODO(nelsonjr): Make the functions:
actually generate the function and remove
the requirement above about files:compile:
Note that this definition does not include the actual function code (yet). It generates the documentation and README updates, but it is not involved in the function generation, so it is still required to have the function listed in the files:compile: section.
The name of the function
The description of the function
The arguments that the function requires. Example:
arguments:
- !ruby/object:Provider::Puppet::Function::Argument
name: image_family
type: Api::Type::String
description: 'the name of the family, e.g. ubuntu-1604-lts'
Examples to be included in the documentation
examples:
- gcompute_image_family('ubuntu-1604-lts', 'ubuntu-os-cloud')
- gcompute_image_family('my-web-server', 'my-project')
Additional notes to be added at the end of the documentation.
notes: |
Note: In the case of private images, your credentials will need to have
the proper permissions to access the image.
To get a list of supported families you can use the gcloud utility:
gcloud compute images list
Lists the file names of each example included for each object. All examples must be located in a product's files/ directory. Each file will be included in the examples folder with the prefix 'examples~'. The convention is to include a .pp file and a delete_.pp file
Example:
examples: !ruby/object:Api::Resource::HashArray
Instance:
- instance.rb # refers to files/examples~cookb
- delete_instance.rb
Lists files to be copied or compiled into the final product. We can add files on
a per-product basis at our whim. If the file is simply copied use the copy
section. If you need to execute code to build the file use the compile
section instead.
Example:
files: !ruby/object:Provider::Config::Files
copy:
lib/google/copy_file.rb: copy_file.rb
compile:
lib/google/object_store.rb: google/object_store.rb
lib/puppet/functions/gcompute_image_family.rb:
products/compute/files/function~image_family.rb.erb
Lists files that should be copied directly to the module without any compilation. List the location in the module as the key (relative to the module root) and the location in the code generator (relative to Magic Module root) as the value.
Lists files that should be compiled and then copied to the module. List the location in the module as the key (relative to the module root) and the location in the code generator (relative to Magic Module root) as the value.
Data files that will be used to generate canned responses from the API. During tests we trap all API calls and return canned data to avoid the need to hit GCP with a real request.
This is useful to both make the tests faster and simple, as well as avoid charges and operating setup to developers helping us improve the modules.
Example:
test_data:
network: !ruby/object:Api::Resource::HashArray
ResourceRecordSet:
- create~name
- create~title
May be of type Provider::Config::TestData::NONE with a reason. This means that all test data will be automatically generated.
Example:
test_data: !ruby/object:Provider::Config::TestData::NONE
reason: 'Test Data will be automatically generated'
A list of objects that have manually written network data files for unit tests.
A list of test data files to be included with the module. These files will be taken from files/ and are all prefixed with 'spec~'. They will be copied to spec/data/network//
Applies rubocop exemptions to generated filed. This is useful when the generated file violates a Rubocop rule and we want a one-off pass instead of disabling the rule.
For example, if there are too many properties in an object, the resource_to_request will have too many lines for rubocop taste.
In that case we can whitelist the resource_to_request to be exempt without the need to allow any other method to have the same leniency.
Example:
- !ruby/object:Provider::Config::StyleException
name: lib/google/compute/property/instance_disks.rb
pinpoints:
- function: resource_to_request
exceptions:
- Metrics/MethodLength
The filename where the exception is needed.
List of places in the file where exceptions are needed.
The name of the function where exceptions are needed. Prefix with class name of multiple versions of the function exist and only one needs exceptions
The name of the class where exceptions are needed. Prefix with module chain if multiple classes of the same name exist and only one needs exceptions.
A list of necessary Rubocop exceptions.
TODO(nelsonjr): Add a link to each example pointing to the other puppet.yaml
files that exist in the products.