puppet-resource_api 1.8.9 → 1.8.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.dependency_decisions.yml +9 -9
- data/.github/dependabot.yml +12 -0
- data/.rubocop.yml +1 -1
- data/.travis.yml +12 -48
- data/CHANGELOG.md +138 -24
- data/Gemfile +39 -17
- data/HISTORY.md +0 -5
- data/README.md +6 -6
- data/appveyor.yml +3 -3
- data/contrib/pre-commit +2 -0
- data/docs/README.md +7 -0
- data/docs/hands-on-lab/01-installing-prereqs.md +16 -0
- data/docs/hands-on-lab/02-connecting-to-the-lightbulbs-emulator.png +0 -0
- data/docs/hands-on-lab/02-connecting-to-the-lightbulbs.md +26 -0
- data/docs/hands-on-lab/03-creating-a-new-module.md +47 -0
- data/docs/hands-on-lab/03-creating-a-new-module_vscode.png +0 -0
- data/docs/hands-on-lab/04-adding-a-new-transport.md +123 -0
- data/docs/hands-on-lab/05-implementing-the-transport-hints.md +19 -0
- data/docs/hands-on-lab/05-implementing-the-transport.md +126 -0
- data/docs/hands-on-lab/06-implementing-the-provider.md +227 -0
- data/docs/hands-on-lab/07-implementing-a-task.md +181 -0
- data/lib/puppet/resource_api.rb +95 -55
- data/lib/puppet/resource_api/base_context.rb +6 -0
- data/lib/puppet/resource_api/data_type_handling.rb +2 -0
- data/lib/puppet/resource_api/glue.rb +9 -2
- data/lib/puppet/resource_api/io_context.rb +2 -0
- data/lib/puppet/resource_api/parameter.rb +4 -2
- data/lib/puppet/resource_api/property.rb +65 -5
- data/lib/puppet/resource_api/puppet_context.rb +2 -0
- data/lib/puppet/resource_api/read_only_parameter.rb +2 -0
- data/lib/puppet/resource_api/simple_provider.rb +2 -0
- data/lib/puppet/resource_api/transport.rb +2 -0
- data/lib/puppet/resource_api/transport/wrapper.rb +2 -0
- data/lib/puppet/resource_api/type_definition.rb +63 -3
- data/lib/puppet/resource_api/value_creator.rb +2 -0
- data/lib/puppet/resource_api/version.rb +3 -1
- data/lib/puppet/util/network_device/simple/device.rb +2 -0
- metadata +15 -4
data/HISTORY.md
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
# Changelog
|
2
|
-
|
3
|
-
All significant changes to this repo will be summarized in this file.
|
4
|
-
|
5
|
-
|
6
1
|
## [v1.8.1](https://github.com/puppetlabs/puppet-resource_api/tree/v1.8.1) (2019-03-13)
|
7
2
|
[Full Changelog](https://github.com/puppetlabs/puppet-resource_api/compare/v1.8.0...v1.8.1)
|
8
3
|
|
data/README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
# Puppet::ResourceApi [](https://travis-ci.org/puppetlabs/puppet-resource_api) [](https://ci.appveyor.com/project/puppetlabs/puppet-resource-api/branch/main) [](https://codecov.io/gh/puppetlabs/puppet-resource_api)
|
2
2
|
|
3
|
-
This is an implementation of the [Resource API specification](https://github.com/puppetlabs/puppet-specifications/blob/
|
3
|
+
This is an implementation of the [Resource API specification](https://github.com/puppetlabs/puppet-specifications/blob/main/language/resource-api/README.md).
|
4
4
|
|
5
5
|
Find a working example of a new-style providers in the [Palo Alto Firewall module](https://github.com/puppetlabs/puppetlabs-panos/):
|
6
|
-
* [Type](https://github.com/puppetlabs/puppetlabs-panos/blob/
|
7
|
-
* [Base provider](https://github.com/puppetlabs/puppetlabs-panos/blob/
|
8
|
-
* [Actual provider with validation and xml processing](https://github.com/puppetlabs/puppetlabs-panos/blob/
|
9
|
-
* [New unit tests](https://github.com/puppetlabs/puppetlabs-panos/blob/
|
6
|
+
* [Type](https://github.com/puppetlabs/puppetlabs-panos/blob/main/lib/puppet/type/panos_address.rb)
|
7
|
+
* [Base provider](https://github.com/puppetlabs/puppetlabs-panos/blob/main/lib/puppet/provider/panos_provider.rb)
|
8
|
+
* [Actual provider with validation and xml processing](https://github.com/puppetlabs/puppetlabs-panos/blob/main/lib/puppet/provider/panos_address/panos_address.rb)
|
9
|
+
* [New unit tests](https://github.com/puppetlabs/puppetlabs-panos/blob/main/spec/unit/puppet/provider/panos_provider_spec.rb) for 100% coverage.
|
10
10
|
|
11
11
|
## [Find the full Resource API documentation here](https://puppet.com/docs/puppet/latest/custom_resources.html)
|
12
12
|
|
data/appveyor.yml
CHANGED
@@ -6,9 +6,9 @@ branches:
|
|
6
6
|
|
7
7
|
environment:
|
8
8
|
matrix:
|
9
|
-
- RUBY_VERSION:
|
10
|
-
- PUPPET_GEM_VERSION: '~>
|
11
|
-
RUBY_VERSION:
|
9
|
+
- RUBY_VERSION: 25-x64
|
10
|
+
- PUPPET_GEM_VERSION: '~> 5.0'
|
11
|
+
RUBY_VERSION: 24-x64
|
12
12
|
|
13
13
|
install:
|
14
14
|
- set PATH=C:\Ruby%RUBY_VERSION%\bin;C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev1\mingw64\bin;%PATH%
|
data/contrib/pre-commit
CHANGED
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 completing 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 [VS Code](https://code.visualstudio.com/download), with the [Ruby](https://marketplace.visualstudio.com/items?itemName=rebornix.Ruby) and [Puppet](https://marketplace.visualstudio.com/items?itemName=puppet.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).
|
Binary file
|
@@ -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
|
+

|
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
|
+

|
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).
|
Binary file
|
@@ -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
|
+
[main (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.
|