Description
Describe the Change You Would Like
For example: validate, insync, munge, Boolean properties, array matching, for a start - document how they underlying need is (better) served by Resource API
Originally raised in https://twitter.com/leothrix/status/920940960435011589
A more detailed starting point from an internal document:
Intro
The type defines how puppet sees a resource and which attributes accept which values.
The provider implements the interaction between puppet and the actual resources.
Due to historical reasons, puppet types are defined using a ruby DSL, expose puppet's inner workings as API, were never adapted to allow use of modern puppet features, and have bits of code sprinkled throughout which are only supposed to run on the agent.
The API between the type definition and its providers has never been formalised, and is up to each individual type to figure out. Accordingly, coding for each type needs to understand its particular quirks. Especially vexing is the general pattern of passing the desired state to the provider in a different call than enforcing that state, which makes reasoning about the behavior.
The provider is sprinkled across many little blocks, making tracing what happens hard.
The Resource API, on the other hand, has a pure-data schema definition, and a straightforward get/set or get/create/update/delete API.
Type Definition
Legacy | Resource API | |
---|---|---|
Interface Definition | code ("DSL") | data |
Input validation | code-only | puppet 4 data types; code possible |
Boolean attributes | see The Boolean Slide below | type: 'Boolean' |
Array attributes | "By default, if a property is assigned multiple values in an array, it is considered in sync if any of those values matches the current value." [docs] | type: 'Array[String]' |
Array attributes, really | "[...] should only be in sync if all values match the current value [use] :array_matching => :all." [ibid.] (but passes through single-element arrays as just that element) | type: 'Array[String]' |
Optional Attributes | don't use isrequired |
type: 'Optional[...]' |
List acceptable values | use newvalue or newvalues , with slightly different capabilities |
type: 'Variant[Boolean, Enum[present, absent]]' |
keep value from being logged | good luck | type: 'Sensitive[...]' |
environment isolation today | run puppet generate types |
run puppet generate types |
environment isolation tomorrow | run puppet generate types |
Load data directly into environment |
read-only attributes | PUP-5624: Properties (in resource types) should allow a read-only designation | behaviour: :read_only |
init only attributes | lol wat? | behaviour: :init_only |
designate namevar | use isnamevar |
behaviour: :namevar |
distinguish parameters and properties | use newparam vs newproperty |
behaviour: :parameter (defaults to property) |
title_patterns | a 5-deep nested data structure with a lambda in it | a list of descriptive hashes |
credentials for remote access | URL | define a schema for arbitrary credentials, using the same structure as type attributes |
Implementation
Legacy | Resource API | |
---|---|---|
API | types can implement all functionality without a provider. provider api can either use @property_hash , mkresourcemethods , CRUD pattern, or custom coded interaction |
return list of resources as data, receive desired state as data |
Memory usage | one Type and one Provider instance per resource; one Parameter instance per attribute per resource; additional memory for @property_hash |
Two Hashes per resource |
List existing resources | Implement the instances and prefetch methods |
return the list of resources as a Hash from get |
Batch processing | "What is wrong with taking two hours for a 5 minute operation?" | built-in (but not used by puppet because "it's hard to implement, and we gave up six years ago trying") |
Setting values on a specific resource | Create a new type instance, set values, flush | create an instance of the provider, call set with desired state |
Logging | Use the Puppet::Util::Logging module, which is included in a variety of places hogging your namespace. e.g. Puppet.info() or just info() in a provider, but not three levels down in your type definition |
call the logging methods on context |
implement simple CRUD | Implement create , flush , destroy methods, data gets injected through @property_hash , which you may or may not have to implement yourself. Just read one of the books. |
Inherit from SimpleProvider , implement create , update , delete according to the example. Get the existing and desired state passed in as argument. |
Debugging | Good luck finding where data gets passed in or stored. Depends on per-type type/provider API choices. | Each method on the provider gets data passed in or is expected to return data. Each readily available for inspection. If any data is stored, the developer has put it there themselves, and is accessible through standard debugging techniques |
transform user input | use the munge function in the type definition; attribute specific; needs to be jruby 1.7 compatible because it's parsed (but not executed) on the server; only works on individual array elements |
Implement canonicalize , transform the complete resource |
API evolution | Still supporting puppet 2.7 semantics, because any change breaks something somewhere | each type can specify feature flags to unlock optional parts of the API, or opt-out of legacy behaviour. |
unit testing | There is no way to properly separate a "unit". Testing requires a full puppet and a VM. Individual tests can take minutes. | 100% test coverage without touching the system. All tests run in seconds. |
remote access | use the global Device instance setup by the framework |
use the Transport instance passed through to your provider |
The Boolean Slide
… or how to define a Boolean attribute in core puppet
-
Set
:boolean => true
in the parameter definition -
or, set
:parent => Puppet::Parameter::Boolean
in the parameter definition -
or, use the
voxpupuli/puppet-boolean
module -
Remember to use
:true
and:false
(strings), instead oftrue
andfalse
(bools) -
Depending on implementation also allows
yes
/no
,0
/1
, and/ort
/f
-
PUP-8462: Including
:boolean => true
in a type parameter definition should automatically populate the values a user is allowed to specify for that parameter -
PUP-8442: type parameters do not honor when default is set to
false
-
PUP-2368: using booleans result in unmanaged property
-
#17519: Puppet type and provider handling of boolean values
-
#17206: parameter defaults of
false
and{}
does not work