puppet-resource_api 1.8.9 → 1.8.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d66c960b7ed4b6ed6123bdf2c8cc158320354f32fb15499b09b92a8a3942d36d
4
- data.tar.gz: 1ba82297b72a4df466c2084feaeb02ed6e883b19d3ee6c913581b5edecd2ca69
3
+ metadata.gz: 23c38c650148fd1697a642774a4b470e208f1f45c3e47dc75c1d93795be7bb38
4
+ data.tar.gz: 884337cc89bd510169e9822982fbed68866dff0d9f30c027b3ebfca600670ce1
5
5
  SHA512:
6
- metadata.gz: b22b37590ff1ac73fdf22d5d0012cd8ac6b46a0e2541ba4b3096a6ede49c12bdaa37e895746194cc1257744dec6403f6428610f5e4b29742fe624f3c505ff211
7
- data.tar.gz: 2f44e9690a905edd1eafaad645f62c82bfe035c7f4f9ace31620b47d56d2eb963cf117edb4652f8b3c0feecea98e8f7cf13ae90993c1d7b291d8343b32741924
6
+ metadata.gz: 721c4d232ba09cef37c387bfec47d557de2873f858bbf7a3e72a9b7f66ef993e0298eb954cdd1864ad4d5091b9aa2fe83480f51a5324255861cd7fa2df80a6c2
7
+ data.tar.gz: af6b72118cc710bac4eec4b4fbae849f5620f1fc29b949afb0dd2baca5ebc2237b55ccf76c696ab6a5be8e534f7d3fbaead41829e256926a07791fc6a064c8e3
data/docs/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Resource API hands-on lab
2
+
3
+ The Resource API hands-on lab walks you through creating a native integration with Puppet. After completely this lab, you will have a fully functioning module to manage Philips HUE lights.
4
+
5
+ >Note: These labs are intended for both new and experienced developers. If you have any feedback or suggestions for improvement, post it in the [issues section](https://github.com/puppetlabs/puppet-resource_api/issues).
6
+
7
+ To start with, we'll go through [installing Puppet Development Kit](./hands-on-lab/01-installing-prereqs.md)(PDK).
@@ -0,0 +1,16 @@
1
+ # Install Puppet Development Kit (PDK) and other tools
2
+
3
+ To start, install Puppet Development Kit (PDK), which provides all the necessary tools and libraries to build and test modules. We also recommend an emulator for the target device, a code editor with good Ruby and Puppet support, and git — a version control system to keep track of your progress.
4
+
5
+ 1. [Download PDK](https://puppet.com/download-puppet-development-kit) on your platform of choice.
6
+
7
+ 2. If you do not have a Philips HUE hub and bulbs available, you can download the [Hue-Emulator](https://github.com/SteveyO/Hue-Emulator/raw/master/HueEmulator-v0.8.jar). You need to have Java installed to run this.
8
+
9
+ 3. To edit code, we recommend the cross-platform editor [VSCode](https://code.visualstudio.com/download), with the [Ruby](https://marketplace.visualstudio.com/items?itemName=rebornix.Ruby) and [Puppet](https://marketplace.visualstudio.com/items?itemName=jpogran.puppet-vscode) extensions. There are lots of other extensions that can help you with your development workflow.
10
+
11
+ 4. Git is a version control system that helps you keep track of changes and collaborate with others. As we go through hands-on lab, we will show you some integrations with cloud services. If you have never used git before, ignore this and all related steps.
12
+
13
+
14
+ ## Next up
15
+
16
+ After installing the relevant tools, you'll [light up a few](./02-connecting-to-the-lightbulbs.md).
@@ -0,0 +1,26 @@
1
+ # Connecting to the light bulbs
2
+
3
+ There are no technical restrictions on the kinds of remote devices or APIs you can connect to with transports. For this lab, we will connect to a Philips HUE hub and make some colourful wireless light bulbs light up. If you (understandably) do not have physical devices available, you can use the Hue Emulator.
4
+
5
+ ## Hue Emulator
6
+
7
+ Use `java -jar` with the emulator's filename to run it, for example:
8
+
9
+ ```
10
+ david@davids:~$ java -jar ~/Downloads/HueEmulator-v0.8.jar
11
+ ```
12
+
13
+ It does not produce any output on the command line, but a window pops up with a hub and a few predefined lights:
14
+
15
+ ![](./02-connecting-to-the-lightbulbs-emulator.png)
16
+
17
+ All you need now is to input a port (the default 8000 is usually fine) and click "Start" to activate the built-in server.
18
+
19
+ ## Connecting to your hub
20
+
21
+ To connect to an actual hub, you need be able to access the bub on your network and get an API key. See the [Philips Developer docs](http://www.developers.meethue.com/documentation/getting-started) (registration required).
22
+
23
+
24
+ ## Next up
25
+
26
+ Now that you have some lights up, you'll [create a module](./03-creating-a-new-module.md).
@@ -0,0 +1,47 @@
1
+ # Create a module
2
+
3
+ Depending on your preferences, you can use the VSCode/PDK integration or run PDK from the command line in a terminal of your choice.
4
+
5
+ ## Create a module with VSCode
6
+
7
+ Spin up the Command Palette (⇧⌘P on the Mac or Ctrl+Shift+P on Windows and Linux) and search for the `Puppet: PDK New Module` task:
8
+
9
+ ![](./03-creating-a-new-module_vscode.png)
10
+
11
+ Click Enter (↩) to execute this and follow the on-screen prompts.
12
+
13
+ The module will open in a new VSCode window.
14
+
15
+ ## Create a module from the command line
16
+
17
+ In your regular workspace (for example your home directory), run the following:
18
+
19
+ ```
20
+ pdk new module hue_workshop --skip-interview
21
+ ```
22
+
23
+ This command creates a new module `hue_workshop` in your directory of the same name, using all defaults. The output will look like:
24
+
25
+ ```
26
+ david@davids:~/tmp$ pdk new module hue_workshop --skip-interview
27
+ pdk (INFO): Creating new module: hue_workshop
28
+ pdk (INFO): Module 'hue_workshop' generated at path '/home/david/tmp/hue_workshop', from template 'file:///opt/puppetlabs/pdk/share/cache/pdk-templates.git'.
29
+ pdk (INFO): In your module directory, add classes with the 'pdk new class' command.
30
+ david@davids:~/tmp$ ls hue_workshop/
31
+ appveyor.yml data files Gemfile.lock manifests Rakefile spec templates
32
+ CHANGELOG.md examples Gemfile hiera.yaml metadata.json README.md tasks
33
+ david@davids:~/tmp$
34
+ ```
35
+
36
+ To read more about the different options when creating new modules, see [PDK docs](https://puppet.com/docs/pdk/1.x/pdk_creating_modules.html).
37
+
38
+ Open the new directory in your code editor:
39
+
40
+ ```
41
+ code -a hue_workshop
42
+ ```
43
+
44
+
45
+ ## Next up
46
+
47
+ Now that you have created a module, you'll [add a transport](./04-adding-a-new-transport.md).
@@ -0,0 +1,123 @@
1
+ # Add a new transport
2
+
3
+ Starting with PDK 1.12.0 there is the `pdk new transport` command, that you can use to create the base files for your new transport:
4
+
5
+ Next, we'll active a few future defaults. In the `hue_workshop` directory, create a file called `.sync.yml` and paste the following:
6
+
7
+ ```
8
+ # .sync.yml
9
+ ---
10
+ Gemfile:
11
+ optional:
12
+ ':development':
13
+ - gem: 'puppet-resource_api'
14
+ - gem: 'faraday'
15
+ - gem: 'rspec-json_expectations'
16
+ spec/spec_helper.rb:
17
+ mock_with: ':rspec'
18
+ ```
19
+
20
+ Run `pdk update` in the module's directory to deploy the changes in the module:
21
+
22
+ ```
23
+ david@davids:~/tmp/hue_workshop$ pdk update --force
24
+ pdk (INFO): Updating david-hue_workshop using the default template, from 1.10.0 to 1.10.0
25
+
26
+ ----------Files to be modified----------
27
+ Gemfile
28
+ spec/spec_helper.rb
29
+
30
+ ----------------------------------------
31
+
32
+ You can find a report of differences in update_report.txt.
33
+
34
+ Do you want to continue and make these changes to your module? Yes
35
+
36
+ ------------Update completed------------
37
+
38
+ 2 files modified.
39
+
40
+ david@davids:~/tmp/hue_workshop$
41
+ ```
42
+
43
+ Then, create the actual transport:
44
+
45
+ ```
46
+ david@davids:~/tmp/hue$ pdk new transport hue
47
+ pdk (INFO): Creating '/home/david/tmp/hue/lib/puppet/transport/hue.rb' from template.
48
+ pdk (INFO): Creating '/home/david/tmp/hue/lib/puppet/transport/schema/hue.rb' from template.
49
+ pdk (INFO): Creating '/home/david/tmp/hue/lib/puppet/util/network_device/hue/device.rb' from template.
50
+ pdk (INFO): Creating '/home/david/tmp/hue/spec/unit/puppet/transport/hue_spec.rb' from template.
51
+ pdk (INFO): Creating '/home/david/tmp/hue/spec/unit/puppet/transport/schema/hue_spec.rb' from template.
52
+ david@davids:~/tmp/hue$
53
+ ```
54
+
55
+ ## Checkpoint
56
+
57
+ To validate your new module and transport, run `pdk validate --parallel` and `pdk test unit`:
58
+
59
+ ```
60
+ david@davids:~/tmp/hue$ pdk validate --parallel
61
+ pdk (INFO): Running all available validators...
62
+ pdk (INFO): Using Ruby 2.5.5
63
+ pdk (INFO): Using Puppet 6.4.2
64
+ ┌ [✔] Validating module using 5 threads ┌
65
+ ├──[✔] Checking metadata syntax (metadat├──son tasks/*.json).
66
+ ├──[✔] Checking task names (tasks/**/*).├──
67
+ └──[✔] Checking YAML syntax (["**/*.yaml├──"*.yaml", "**/*.yml", "*.yml"]).
68
+ └──[/] Checking module metadata style (metadata.json).
69
+ └──[✔] Checking module metadata style (metadata.json).
70
+ info: puppet-syntax: ./: Target does not contain any files to validate (**/*.pp).
71
+ info: task-metadata-lint: ./: Target does not contain any files to validate (tasks/*.json).
72
+ info: puppet-lint: ./: Target does not contain any files to validate (**/*.pp).
73
+ david@davids:~/tmp/hue$ pdk test unit
74
+ pdk (INFO): Using Ruby 2.5.5
75
+ pdk (INFO): Using Puppet 6.4.2
76
+ [✔] Preparing to run the unit tests.
77
+ [✔] Running unit tests in parallel.
78
+ Run options: exclude {:bolt=>true}
79
+ Evaluated 6 tests in 2.405066937 seconds: 0 failures, 0 pending.
80
+ david@davids:~/tmp/hue$
81
+ ```
82
+
83
+ If you're working with a version control system, now would be a good time to make your first commit and store the boilerplate code, and then you can revisit the changes you made later. For example:
84
+
85
+ ```
86
+ david@davids:~/tmp/hue$ git init
87
+ Initialized empty Git repository in ~/tmp/hue/.git/
88
+ david@davids:~/tmp/hue$ git add -A
89
+ david@davids:~/tmp/hue$ git commit -m 'initial commit'
90
+ [master (root-commit) 67951dd] initial commit
91
+ 26 files changed, 887 insertions(+)
92
+ create mode 100644 .fixtures.yml
93
+ create mode 100644 .gitattributes
94
+ create mode 100644 .gitignore
95
+ create mode 100644 .gitlab-ci.yml
96
+ create mode 100644 .pdkignore
97
+ create mode 100644 .puppet-lint.rc
98
+ create mode 100644 .rspec
99
+ create mode 100644 .rubocop.yml
100
+ create mode 100644 .sync.yml
101
+ create mode 100644 .travis.yml
102
+ create mode 100644 .yardopts
103
+ create mode 100644 CHANGELOG.md
104
+ create mode 100644 Gemfile
105
+ create mode 100644 README.md
106
+ create mode 100644 Rakefile
107
+ create mode 100644 appveyor.yml
108
+ create mode 100644 data/common.yaml
109
+ create mode 100644 hiera.yaml
110
+ create mode 100644 lib/puppet/transport/hue.rb
111
+ create mode 100644 lib/puppet/transport/schema/hue.rb
112
+ create mode 100644 lib/puppet/util/network_device/hue/device.rb
113
+ create mode 100644 metadata.json
114
+ create mode 100644 spec/default_facts.yml
115
+ create mode 100644 spec/spec_helper.rb
116
+ create mode 100644 spec/unit/puppet/transport/hue_spec.rb
117
+ create mode 100644 spec/unit/puppet/transport/schema/hue_spec.rb
118
+ david@davids:~/tmp/hue$
119
+ ```
120
+
121
+ ## Next up
122
+
123
+ Now that you have everything ready, you'll [implement the transport](./05-implementing-the-transport.md).
@@ -0,0 +1,19 @@
1
+ ## Implementing the transport - Exercise
2
+
3
+ Implement the `request_debug` option that you can toggle on to create additional debug output on each request. If you get stuck, review the hints below, or [the finished file](TODO).
4
+
5
+ ## Hints
6
+
7
+ * You can create a toggle option with the `Boolean` (`true` or `false`) data type. Add it to the `connection_info` in the transport schema.
8
+
9
+ * Make it an `Optional[Boolean]` so that users who do not require request debugging do not have to specify the value.
10
+
11
+ * To remember the value you passed, store `connection_info[:request_debug]` in a `@request_debug` variable.
12
+
13
+ * In the `hue_get` and `hue_put` methods, add `context.debug(message)` calls showing the method's arguments.
14
+
15
+ * Make the debugging optional based on your input by appending `if @request_debug` to each logging statement.
16
+
17
+ # Next Up
18
+
19
+ Now that the transport can talk to the remote target, it's time to [implement a provider](./06-implementing-the-provider.md).
@@ -0,0 +1,126 @@
1
+ # Implementing the transport
2
+
3
+ A transport consists of a *schema* describing the required data and credentials to connect to the HUE hub, and the *implementation* containing all the code to facilitate communication with the devices.
4
+
5
+ ## Schema
6
+
7
+ The transport schema defines attributes in a reusable way, allowing you to understand the requirements of the transport. All schemas are located in `lib/puppet/transport/schema` in a Ruby file named after the transport. In this case `hue.rb`.
8
+
9
+ To connect to the HUE hub you need an IP address, a port, and an API key.
10
+
11
+ Replace the `connection_info` in `lib/puppet/transport/schema/hue.rb` with the following code:
12
+
13
+ ```ruby
14
+ connection_info: {
15
+ host: {
16
+ type: 'String',
17
+ desc: 'The FQDN or IP address of the hue light system to connect to.',
18
+ },
19
+ port: {
20
+ type: 'Optional[Integer]',
21
+ desc: 'The port to use when connecting, defaults to 80.',
22
+ },
23
+ key: {
24
+ type: 'String',
25
+ desc: 'The access key that allows access to the hue light system.',
26
+ sensitive: true,
27
+ },
28
+ },
29
+ ```
30
+
31
+ > Note: The Resource API transports use [Puppet Data Types](https://puppet.com/docs/puppet/5.3/lang_data_type.html#core-data-types) to define the allowable values for an attribute. Abstract types like `Optional[]` can be useful to make using your transport easier. Take note of the `sensitive: true` annotation on the `key`; it instructs all services processing this attribute with special care, for example to avoid logging the key.
32
+
33
+
34
+ ## Implementation
35
+
36
+ The implementation of a transport provides connectivity and utility functions for both Puppet and the providers managing the remote target. The HUE API is a simple REST interface, so you can store the credentials until you need make a connection. The default template at `lib/puppet/transport/hue.rb` already does this. Have a look at the `initialize` function to see how this is done.
37
+
38
+ For the HUE's REST API, we want to create a `Faraday` object to capture the target host and key so that the transport can facilitate requests. Replace the `initialize` method in `lib/puppet/transport/hue.rb` with the following code:
39
+
40
+ <!-- TODO: do we really need this? -- probably not ```
41
+ # @summary
42
+ # Expose the `Faraday` object connected to the hub
43
+ attr_reader :connection
44
+
45
+ ```-->
46
+ ```
47
+ # @summary
48
+ # Initializes and returns a faraday connection to the given host
49
+ def initialize(_context, connection_info)
50
+ # provide a default port
51
+ port = connection_info[:port].nil? ? 80 : connection_info[:port]
52
+ Puppet.debug "Connecting to #{connection_info[:host]}:#{port} with dev key"
53
+ @connection = Faraday.new(url: "http://#{connection_info[:host]}:#{port}/api/#{connection_info[:key].unwrap}", ssl: { verify: false })
54
+ end
55
+ ```
56
+
57
+ > Note the `unwrap` call on building the URL, to access the sensitive value.
58
+
59
+ ### Facts
60
+
61
+ The transport is also responsible for collecting any facts from the remote target, similar to how facter works for regular systems. For now we'll only return a hardcoded `operatingsystem` value to mark HUE Hubs:
62
+
63
+ Replace the example `facts` method in `lib/puppet/transport/hue.rb` with the following code:
64
+
65
+ ```
66
+ # @summary
67
+ # Returns set facts regarding the HUE Hub
68
+ def facts(_context)
69
+ { 'operatingsystem' => 'philips_hue' }
70
+ end
71
+ ```
72
+
73
+ ### Connection verification and closing
74
+
75
+ To enable better feedback when something goes wrong, a transport can implement a `verify` method to run extra checks on the credentials passed in.
76
+
77
+ To save resources both on the target and the node running the transport, the `close` method will be called when the transport is not needed anymore. The transport can close connections and release memory and other resources at this point.
78
+
79
+ For this tutorial, replace the example methods with the following code:
80
+
81
+ ```
82
+ # @summary
83
+ # Test that transport can talk to the remote target
84
+ def verify(_context)
85
+ end
86
+
87
+ # @summary
88
+ # Close connection, free up resources
89
+ def close(_context)
90
+ @connection = nil
91
+ end
92
+ ```
93
+
94
+ ### Making requests
95
+
96
+ Besides exposing some standardises functionality to Puppet, the transport is also a good place to put utility functions that can be reused across your providers. While it may seem overkill for this small example, it is no extra effort, and will establish a healthy pattern.
97
+
98
+ Insert the following code after the `close` method:
99
+
100
+ ```
101
+ # @summary
102
+ # Make a get request to the HUE Hub API
103
+ def hue_get(context, url, args = nil)
104
+ url = URI.escape(url) if url
105
+ result = @connection.get(url, args)
106
+ JSON.parse(result.body)
107
+ rescue JSON::ParserError => e
108
+ raise Puppet::ResourceError, "Unable to parse JSON response from HUE API: #{e}"
109
+ end
110
+
111
+ # @summary
112
+ # Sends an update command to the given url/connection
113
+ def hue_put(context, url, message)
114
+ message = message.to_json
115
+ @connection.put(url, message)
116
+ end
117
+ ```
118
+
119
+ ## Exercise
120
+
121
+ Implement a `request_debug` option that you can toggle to create additional debug output on each request. If you get stuck, have a look at [some hints](./05-implementing-the-transport-hints.md), or [the finished file](TODO).
122
+
123
+
124
+ # Next Up
125
+
126
+ Now that the transport can talk to the remote target, it's time to [implement a provider](./06-implementing-the-provider.md).
@@ -0,0 +1,227 @@
1
+ # Implementing the provider
2
+
3
+ To expose resources from the HUE Hub to Puppet, a type and provider define and implement the desired interactions. The *type*, like the transport schema, defines the shape of the data using Puppet data types. The implementation in the *provider* takes care of the communication and data transformation.
4
+
5
+ For this hands on lab, we'll now go through implementing a simple `hue_light` type and provider to manage the state of the light bulbs connected to the HUE Hub.
6
+
7
+ ## Generating the Boilerplate
8
+
9
+ In your module directory, run `pdk new provider hue_light`. This creates another set of files with a bare-bones type and provider, as well as unit tests.
10
+
11
+ ```
12
+ david@davids:~/tmp/hue_workshop$ pdk new provider hue_light
13
+ pdk (INFO): Creating '/home/david/tmp/hue_workshop/lib/puppet/provider/hue_light/hue_light.rb' from template.
14
+ pdk (INFO): Creating '/home/david/tmp/hue_workshop/lib/puppet/type/hue_light.rb' from template.
15
+ pdk (INFO): Creating '/home/david/tmp/hue_workshop/spec/unit/puppet/provider/hue_light/hue_light_spec.rb' from template.
16
+ pdk (INFO): Creating '/home/david/tmp/hue_workshop/spec/unit/puppet/type/hue_light_spec.rb' from template.
17
+ david@davids:~/tmp/hue_workshop$
18
+ ```
19
+
20
+ ## Defining the type
21
+
22
+ The type defines the attributes and allowed values, as well as a couple of other bits of information that concerns the processing of this provider.
23
+
24
+ For remote resources like this, adding the `'remote_resource'` feature is necessary to alert Puppet of its specific needs. Add the string to the existing `features` array:
25
+
26
+ ```
27
+ features: ['remote_resource'],
28
+ attributes: {
29
+ ```
30
+
31
+ Browsing through the Hub API (TODO: insert link), we can identify a few basic properties we want to manage, for example:
32
+
33
+ * Whether the lamp is on or off
34
+ * The colour of the light (hue and saturation)
35
+ * The brightness of the light
36
+
37
+ To define the necessary attributes, insert the following snippet into the `attributes` hash, after the `name`:
38
+
39
+ ```
40
+ ensure: {
41
+ type: 'Enum[present, absent]',
42
+ desc: 'Whether this resource should be present or absent on the target system.',
43
+ default: 'present',
44
+ },
45
+ on: {
46
+ type: 'Optional[Boolean]',
47
+ desc: 'Switches the light on or off',
48
+ },
49
+ hue: {
50
+ type: 'Optional[Integer]',
51
+ desc: 'The hue the light color.',
52
+ },
53
+ sat: {
54
+ type: 'Optional[Integer]',
55
+ desc: 'The saturation of the light colour',
56
+ },
57
+ bri: {
58
+ type: 'Optional[Integer[1,254]]',
59
+ desc: <<DESC,
60
+ This is the brightness of a light from its minimum brightness 1 to its maximum brightness 254
61
+ DESC
62
+ },
63
+ ```
64
+
65
+ ## Implementing the Provider
66
+
67
+ Every provider needs a `get` method, that returns a list of currently existing resources and their attributes from the remote target. For the HUE Hub, this is requires a call to the `lights` endpoint and some data transformation to the format Puppet expects.
68
+
69
+ ### Reading the state of the lights
70
+
71
+ Replace the example `get` function in `lib/puppet/provider/hue_light/hue_light.rb` with the following code:
72
+
73
+ ```
74
+ # @summary
75
+ # Returns a list of lights and their attributes as a list of hashes.
76
+ def get(context)
77
+ lights = context.transport.hue_get(context, 'lights')
78
+
79
+ return [] if lights.nil?
80
+
81
+ lights.collect { |name, content|
82
+ {
83
+ name: name,
84
+ ensure: 'present',
85
+ on: content['state']['on'],
86
+ hue: content['state']['hue'],
87
+ sat: content['state']['sat'],
88
+ bri: content['state']['bri'],
89
+ }
90
+ }
91
+ end
92
+ ```
93
+
94
+ This method returns all connected lights from the HUE Hub and allows Puppet to process them. To try this out, you need to setup a test configuration and use `puppet device` to drive your testing.
95
+
96
+ ### Obtaining an API key from the Philips Hue Bridge API
97
+ We will need to create an authorized user on the Bridge API, which in turn will provide us with a token we can use for subsequent requests:
98
+ - Press the 'Link' button on the Bridge device (**NOTE:** Linking expires after 10 seconds of inactivity by default)
99
+ - Perform the following POST request to the API using curl:
100
+ ```
101
+ curl -X POST -d '{"devicetype":"puppetlabs#hue_light_mgmt"}' 'http://192.168.43.195:8000/api'
102
+ ```
103
+ If that has been successful, we should get a `200` response with the user's API token:
104
+ ```
105
+ [
106
+ { "success":
107
+ {
108
+ "username": "onmdTvd198bMrC6QYyVE9iasfYSeyAbAj3XyQzfL"
109
+ }
110
+ }
111
+ ]
112
+ ```
113
+
114
+ # hub1.conf
115
+ ```
116
+ host: 192.168.43.195
117
+ key: onmdTvd198bMrC6QYyVE9iasfYSeyAbAj3XyQzfL
118
+ ```
119
+
120
+ ```
121
+ # device.conf
122
+ [hub1]
123
+ type hue
124
+ url file:///home/david/git/hue_workshop/spec/fixtures/hub1.conf
125
+
126
+ [hub2]
127
+ type hue
128
+ url file:///home/david/git/hue_workshop/spec/fixtures/hub2.conf
129
+ ```
130
+
131
+ ```
132
+ david@davids:~/tmp/hue_workshop$ pdk bundle install
133
+ pdk (INFO): Using Ruby 2.4.5
134
+ pdk (INFO): Using Puppet 5.5.12
135
+ [...]
136
+ Bundle complete! 10 Gemfile dependencies, 90 gems now installed.
137
+ Use `bundle info [gemname]` to see where a bundled gem is installed.
138
+
139
+ david@davids:~/tmp/hue_workshop$ pdk bundle exec puppet device --libdir lib --deviceconfig device.conf --target hub1 --resource hue_light
140
+ pdk (INFO): Using Ruby 2.4.5
141
+ pdk (INFO): Using Puppet 5.5.12
142
+ hue_light { '1':
143
+ on => true,
144
+ bri => 37,
145
+ hue => 13393,
146
+ sat => 204,
147
+ effect => 'none',
148
+ alert => 'select',
149
+ }
150
+ hue_light { '2':
151
+ on => true,
152
+ bri => 37,
153
+ hue => 13401,
154
+ sat => 204,
155
+ effect => 'none',
156
+ alert => 'select',
157
+ }
158
+ hue_light { '3':
159
+ on => true,
160
+ bri => 254,
161
+ hue => 65136,
162
+ sat => 254,
163
+ effect => 'none',
164
+ alert => 'none',
165
+ }
166
+
167
+ david@davids:~/tmp/hue_workshop$
168
+ ```
169
+
170
+ ### Changing the state of the lights
171
+
172
+ The final step here is to implement enforcing the desired state of the lights. The default template from the PDK offers `create`, `update`, and `delete` methods to implement the various operations.
173
+
174
+ For the HUE Hub API, we can remove the `create` and `delete` method. Since the attribute names and data definitions line up with the HUE Hub API, the `update` method is very short.
175
+
176
+ Replace the `create`, `update`, and `delete` methods with the following code:
177
+
178
+ ```
179
+ def update(context, name, should)
180
+ context.device.hue_put("lights/#{name}/state", should)
181
+ end
182
+ ```
183
+
184
+ Now you can also change the state of the lights using a manifest:
185
+
186
+ ```
187
+ # traffic_lights.pp
188
+ Hue_light { on => true, bri => 10, sat => 254 }
189
+ hue_light {
190
+ '1':
191
+ hue => 23536;
192
+ '2':
193
+ hue => 10000;
194
+ '3':
195
+ hue => 65136;
196
+ }
197
+ ```
198
+
199
+ ```
200
+ david@davids:~/git/hue_workshop$ pdk bundle exec puppet device --libdir lib --deviceconfig device.conf --target hub1 --apply examples/traffic_lights.pp
201
+ pdk (INFO): Using Ruby 2.4.5
202
+ pdk (INFO): Using Puppet 5.5.12
203
+ Notice: Compiled catalog for hub1 in environment production in 0.06 seconds
204
+ Notice: /Stage[main]/Main/Hue_light[1]/hue: hue changed 13393 to 23536 (corrective)
205
+ Notice: /Stage[main]/Main/Hue_light[1]/bri: bri changed 70 to 10 (corrective)
206
+ Notice: /Stage[main]/Main/Hue_light[1]/sat: sat changed 204 to 255 (corrective)
207
+ Notice: /Stage[main]/Main/Hue_light[2]/hue: hue changed 13401 to 10000 (corrective)
208
+ Notice: /Stage[main]/Main/Hue_light[2]/bri: bri changed 70 to 10 (corrective)
209
+ Notice: /Stage[main]/Main/Hue_light[2]/sat: sat changed 204 to 255 (corrective)
210
+ Notice: /Stage[main]/Main/Hue_light[3]/bri: bri changed 254 to 10 (corrective)
211
+ Notice: /Stage[main]/Main/Hue_light[3]/sat: sat changed 254 to 255 (corrective)
212
+ Notice: Applied catalog in 0.18 seconds
213
+
214
+ david@davids:~/git/hue_workshop$
215
+ ```
216
+
217
+ ## Exercise
218
+
219
+ To round out the API support, add an `effect` attribute that defaults to `none`, but can be set to `colorloop`, and an `alert` attribute that defaults to `none` and can be set to `select`.
220
+
221
+ Note that this exercise requires exploring new data types and Resource API options.
222
+
223
+ > TODO: add exercise hints
224
+
225
+ # Next Up
226
+
227
+ Now that we can manage state, it's time to [implement a task](./07-implementing-a-task.md) to do some fun transient things with the lights.
@@ -0,0 +1,181 @@
1
+ # Implementing a Task
2
+
3
+ > TODO: this is NOT fine, yet
4
+
5
+ * add bolt gem
6
+ ```
7
+ Gemfile:
8
+ optional:
9
+ ':development':
10
+ - gem: 'puppet-resource_api'
11
+ - gem: 'faraday'
12
+ # add this
13
+ - gem: 'bolt'
14
+ ```
15
+
16
+
17
+ ```
18
+ david@davids:~/tmp/hue_workshop$ pdk update --force
19
+ pdk (INFO): Updating david-hue_workshop using the default template, from 1.10.0 to 1.10.0
20
+
21
+ ----------Files to be modified----------
22
+ Gemfile
23
+
24
+ ----------------------------------------
25
+
26
+ You can find a report of differences in update_report.txt.
27
+
28
+
29
+ ------------Update completed------------
30
+
31
+ 1 files modified.
32
+
33
+ david@davids:~/tmp/hue_workshop$ pdk bundle install
34
+ pdk (INFO): Using Ruby 2.5.3
35
+ pdk (INFO): Using Puppet 6.4.2
36
+ [...]
37
+ Bundle complete! 11 Gemfile dependencies, 122 gems now installed.
38
+ Use `bundle info [gemname]` to see where a bundled gem is installed.
39
+
40
+ david@davids:~/tmp/hue_workshop$
41
+ ```
42
+
43
+ * add ruby_task_helper module
44
+ <!--
45
+ ```# .fixtures.yml
46
+ ---
47
+ fixtures:
48
+ forge_modules:
49
+ ruby_task_helper: "puppetlabs/ruby_task_helper"
50
+ ```
51
+
52
+ ```
53
+ david@davids:~/tmp/hue_workshop$ pdk bundle exec rake spec_prep
54
+ pdk (INFO): Using Ruby 2.5.3
55
+ pdk (INFO): Using Puppet 6.4.2
56
+ Notice: Preparing to install into /home/david/tmp/hue_workshop/spec/fixtures/modules ...
57
+ Notice: Downloading from https://forgeapi.puppet.com ...
58
+ Notice: Installing -- do not interrupt ...
59
+ /home/david/tmp/hue_workshop/spec/fixtures/modules
60
+ └── puppetlabs-ruby_task_helper (v0.3.0)
61
+ I, [2019-06-04T13:12:04.615368 #32070] INFO -- : Creating symlink from spec/fixtures/modules/hue_workshop to /home/david/tmp/hue_workshop
62
+ david@davids:~/tmp/hue_workshop$
63
+ ``` -->
64
+
65
+ Using the development version:
66
+
67
+ ```
68
+ fixtures:
69
+ # forge_modules:
70
+ # ruby_task_helper: "puppetlabs/ruby_task_helper"
71
+ repositories:
72
+ ruby_task_helper:
73
+ repo: "git://github.com/da-ar/puppetlabs-ruby_task_helper"
74
+ ref: "38745f8e7c2521c50bbf1b8e03318006cdac7a02"
75
+ ```
76
+
77
+ ```
78
+ david@davids:~/tmp/hue_workshop$ pdk bundle exec rake spec_prep
79
+ pdk (INFO): Using Ruby 2.5.3
80
+ pdk (INFO): Using Puppet 6.4.2
81
+ HEAD is now at 38745f8 (FM-7955) Update to use Transport helper code
82
+ Cloning into 'spec/fixtures/modules/ruby_task_helper'...
83
+ I, [2019-06-04T13:43:58.577944 #9390] INFO -- : Creating symlink from spec/fixtures/modules/hue_workshop to /home/david/tmp/hue_workshop
84
+ david@davids:~/tmp/hue_workshop$
85
+ ```
86
+
87
+ * `pdk new task` based on https://github.com/puppetlabs/puppetlabs-panos/blob/master/tasks/apikey.rb
88
+
89
+ ```
90
+ david@davids:~/tmp/hue_workshop$ pdk new task alarm
91
+ pdk (INFO): Creating '/home/david/tmp/hue_workshop/tasks/alarm.sh' from template.
92
+ pdk (INFO): Creating '/home/david/tmp/hue_workshop/tasks/alarm.json' from template.
93
+ david@davids:~/tmp/hue_workshop$ mv /home/david/tmp/hue_workshop/tasks/alarm.sh /home/david/tmp/hue_workshop/tasks/alarm.rb
94
+ david@davids:~/tmp/hue_workshop$
95
+ ```
96
+
97
+ * `tasks/alarm.json`
98
+ ```json
99
+ {
100
+ "puppet_task_version": 1,
101
+ "supports_noop": false,
102
+ "remote": true,
103
+ "description": "A short description of this task",
104
+ "parameters": {
105
+ "name": {
106
+ "type": "String",
107
+ "description": "The lamp to alarm"
108
+ }
109
+ },
110
+ "files": [
111
+ "ruby_task_helper/files/task_helper.rb",
112
+ "hue_workshop/lib/puppet/transport/hue.rb",
113
+ "hue_workshop/lib/puppet/transport/schema/hue.rb"
114
+ ]
115
+ }
116
+ ```
117
+
118
+ * `tasks/alarm.rb`
119
+
120
+ ```ruby
121
+ #!/opt/puppetlabs/puppet/bin/ruby
122
+
123
+ require 'puppet'
124
+ require_relative "../../ruby_task_helper/files/task_helper.rb"
125
+
126
+ class AlarmTask < TaskHelper
127
+ def task(params = {}, remote = nil)
128
+ name = params[:name]
129
+ 5.times do |i|
130
+ remote.transport.hue_put("lights/#{name}/state",
131
+ name: name,
132
+ on: false,
133
+ )
134
+ sleep 1.0
135
+ remote.transport.hue_put("lights/#{name}/state",
136
+ name: name,
137
+ on: true,
138
+ hue: 10000*i,
139
+ sat: 255
140
+ )
141
+ sleep 1.0
142
+ end
143
+ {}
144
+ end
145
+ end
146
+
147
+ if __FILE__ == $0
148
+ AlarmTask.run
149
+ end
150
+ ```
151
+
152
+ * execute `pdk bundle exec bolt ...`
153
+
154
+ ```yaml
155
+ # inventory.yaml
156
+ ---
157
+ nodes:
158
+ - name: "192.168.43.195"
159
+ alias: hub1
160
+ config:
161
+ transport: remote
162
+ remote:
163
+ remote-transport: hue
164
+ key: "onmdTvd198bMrC6QYyVE9iasfYSeyAbAj3XyQzfL"
165
+ ```
166
+
167
+ ```
168
+ david@davids:~/tmp/hue_workshop$ pdk bundle exec bolt task run hue_workshop::alarm --modulepath spec/fixtures/modules/ --target hub1 --inventoryfile inventory.yaml
169
+ pdk (INFO): Using Ruby 2.5.3
170
+ pdk (INFO): Using Puppet 6.4.2
171
+ Started on 192.168.43.195...
172
+ Finished on 192.168.43.195:
173
+ {
174
+ }
175
+ Successful on 1 node: 192.168.43.195
176
+ Ran on 1 node in 11.32 seconds
177
+
178
+ david@davids:~/tmp/hue_workshop$
179
+ ```
180
+
181
+ * profit!
@@ -331,7 +331,10 @@ module Puppet::ResourceApi
331
331
  else
332
332
  my_provider.set(context, rsapi_title => { is: @rsapi_current_state, should: target_state }) unless noop?
333
333
  end
334
- raise 'Execution encountered an error' if context.failed?
334
+ if context.failed?
335
+ context.reset_failed
336
+ raise 'Execution encountered an error'
337
+ end
335
338
 
336
339
  # remember that we have successfully reached our desired state
337
340
  @rsapi_current_state = target_state
@@ -33,6 +33,10 @@ class Puppet::ResourceApi::BaseContext
33
33
  @failed
34
34
  end
35
35
 
36
+ def reset_failed
37
+ @failed = false
38
+ end
39
+
36
40
  def feature_support?(feature)
37
41
  Puppet.deprecation_warning('context.feature_support? is deprecated. Please use context.type.feature? instead.')
38
42
  type.feature?(feature)
@@ -1,5 +1,5 @@
1
1
  module Puppet
2
2
  module ResourceApi
3
- VERSION = '1.8.9'.freeze
3
+ VERSION = '1.8.10'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet-resource_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.9
4
+ version: 1.8.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Schmitt
@@ -52,6 +52,17 @@ files:
52
52
  - codecov.yml
53
53
  - contrib/README.md
54
54
  - contrib/pre-commit
55
+ - docs/README.md
56
+ - docs/hands-on-lab/01-installing-prereqs.md
57
+ - docs/hands-on-lab/02-connecting-to-the-lightbulbs-emulator.png
58
+ - docs/hands-on-lab/02-connecting-to-the-lightbulbs.md
59
+ - docs/hands-on-lab/03-creating-a-new-module.md
60
+ - docs/hands-on-lab/03-creating-a-new-module_vscode.png
61
+ - docs/hands-on-lab/04-adding-a-new-transport.md
62
+ - docs/hands-on-lab/05-implementing-the-transport-hints.md
63
+ - docs/hands-on-lab/05-implementing-the-transport.md
64
+ - docs/hands-on-lab/06-implementing-the-provider.md
65
+ - docs/hands-on-lab/07-implementing-a-task.md
55
66
  - lib/puppet/resource_api.rb
56
67
  - lib/puppet/resource_api/base_context.rb
57
68
  - lib/puppet/resource_api/data_type_handling.rb