Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 58 additions & 40 deletions language/resource-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,11 @@ class Puppet::Provider::Nx9k_vlan::Nexus
end
```

Declaring this feature restricts the resource from being run "locally". It executes all external interactions through the `context.transport` instance. The way the instance is set up is runtime specific. In Puppet, it is configured through the [`device.conf`](https://puppet.com/docs/puppet/5.3/config_file_device.html) file, and only available when running under [`puppet device`](https://puppet.com/docs/puppet/5.3/man/device.html).
Declaring this feature restricts the resource from being run "locally". It executes all external interactions through the `context.transport` instance. The way the instance is set up is runtime specific. In Puppet, it is configured through the [`device.conf`](https://puppet.com/docs/puppet/5.3/config_file_device.html) file, and only available when running under [`puppet device`](https://puppet.com/docs/puppet/5.3/man/device.html). We recommend using the [device_manager module](https://forge.puppet.com/puppetlabs/device_manager) to deploy device configuration and credentials. In Bolt, credentials are passed in through the [`inventory.yaml`](https://puppet.com/docs/bolt/latest/inventory_file.html#remote-targets).

To support Puppet versions prior to 6, see the [Legacy Support](#legacy-support) section below.
To use remote resources, you need a transport. See below for how they are defined and implemented.

### Transport
### Transports

```ruby
# lib/puppet/transport/schema/nexus.rb
Expand Down Expand Up @@ -412,12 +412,26 @@ class Puppet::Transport::Nexus
def facts(context); {}; end
def close(context); end
end

# lib/puppet/util/network_device/nexus/device.rb
require 'puppet/resource_api/transport/wrapper'
require 'puppet/transport/schema/nexus'

module Puppet::Util::NetworkDevice::Nexus
class Device < Puppet::ResourceApi::Transport::Wrapper
def initialize(url_or_config, _options = {})
super('nexus', url_or_config)
end
end
end
```

A transport connects providers to the remote target. It consists of the schema and the implementation. The schema is defined in the same manner as a `Type`, except instead of `attributes` you define `connection_info` which describes the shape of the data which is passed to the implementation for a connection to be made.

Password attributes should also set `sensitive: true` to ensure that the data is handled securely. Attributes marked with this flag allow any UI based off this schema to make appropriate presentation choices. The value will be passed to the transport wrapped in a `Puppet::Pops::Types::PSensitiveType::Sensitive`. This will keep the value from being logged or saved inadvertently while it is being transmitted between components. To access the value within the Transport use the `unwrap` method. e.g. `connection_info[:password].unwrap`.

#### Schema

A transport schema accepts the following keywords:

* `uri`: use when you need to specify a specific URL to connect to. All of the following keys will be computed from the `uri` when possible. In the future more url parts may be computed from the URI.
Expand All @@ -436,6 +450,8 @@ Do not use the following keywords when writing a schema:
* `remote-*`: any key starting with `remote-` is reserved for future use.
* `implementations`: reserved by Bolt.

#### Implementation

The transport implementation must implement the following methods:

* `initialize(context, connection_info)`
Expand Down Expand Up @@ -465,42 +481,9 @@ To allow implementors a wide latitude in implementing connection and retry handl

1. Any other methods on the transport are to be used by providers and tasks talking to this kind of target. Those operations are free to use any error signalling mechanism that is convenient. Providers and tasks will have to provide appropriate error signalling to the runtime.

### Direct Access to Transports

It is possible to use a RSAPI Transport directly using the `connect` method:

`Puppet::ResourceApi::Transport.connect(name, config)`

1. `register` the transport schema for the remote resource by:
* Directly calling into `Puppet::ResourceApi.register_transport`
* Loading an existing schema using `require 'puppet/transport/schema/<transportname>`
* Setting up Puppet's autoloader for `'puppet/transport/schema'`
2. `connect` to the transport by name, passing the connection info
3. When the transport has been initialized, `connect` will return the `Transport` object

See above for possible exceptions coming from initializing a transport.

To get a list of all registered transports, call the `list` method:

`Puppet::ResourceApi::Transport.list`

It will return a hash of all registered transport schemas keyed by their name. Each entry in the list is the transport schema definition as passed to `register_transport`:
#### Bridging into Puppet


```ruby
{
'nexus' => {
name: 'nexus',
desc: 'Connects to a Cisco Nexus device',
# ...
# the rest of the transport schema definition here
},
}
```

### Legacy Support

Before Puppet 6.X (TBD), remote resources were only supported through the `Puppet::Util::NetworkDevice` namespace. To make a module useful on these older versions, a shim `Device` class needs to be provided to connect the dots:
Before the Resource API, remote resources were only supported through the `Puppet::Util::NetworkDevice` namespace. To connect your Transport in a way that Puppet understands, you need to provide a shim `Device` class.

```ruby
# lib/puppet/type/nx9k_vlan.rb
Expand All @@ -524,9 +507,11 @@ module Puppet::Util::NetworkDevice::Nexus
end
```

Inheriting from `Puppet::ResourceApi::Transport::Wrapper` will ensure that the necessary `Device` methods will be implemented using your transport.
Inheriting from `Puppet::ResourceApi::Transport::Wrapper` ensures that the necessary `Device` methods are implemented using your transport. Specify the transport name in the `super` call to make the connection.

> Note that because of the way the Resource API is bundled with Puppet agent packages, agent versions 6.0 through 6.3 are incompatible with this way of executing remote content. These versions will not be supported after [support for PE 2019.0 ends in August 2019](https://puppet.com/misc/puppet-enterprise-lifecycle).

### Porting existing code
#### Porting existing code

To port your existing device code as a transport, move the class to `Puppet::Transport`, remove mention of the `Puppet::Util::NetworkDevice::Simple::Device` (if it was used) and change the initialisation to accept and process a `connection_info` hash instead of the previous structures. When accessing the `connection_info` in your new transport, change all string keys to symbols (e.g. `'foo'` to `:foo`). Add `context` as first argument to your `initialize` and `facts` method.

Expand All @@ -539,6 +524,39 @@ The following replacements have provided a good starting point for these changes

Of course this list can't be exhaustive and will depend on the specifics of your codebase.

#### Direct Access to Transports

It is possible to use a RSAPI Transport directly using the `connect` method:

`Puppet::ResourceApi::Transport.connect(name, config)`

1. `register` the transport schema for the remote resource by:
* Directly calling into `Puppet::ResourceApi.register_transport`
* Loading an existing schema using `require 'puppet/transport/schema/<transportname>`
* Setting up Puppet's autoloader for `'puppet/transport/schema'`
2. `connect` to the transport by name, passing the connection info
3. When the transport has been initialized, `connect` will return the `Transport` object

See above for possible exceptions coming from initializing a transport.

To get a list of all registered transports, call the `list` method:

`Puppet::ResourceApi::Transport.list`

It will return a hash of all registered transport schemas keyed by their name. Each entry in the list is the transport schema definition as passed to `register_transport`:


```ruby
{
'nexus' => {
name: 'nexus',
desc: 'Connects to a Cisco Nexus device',
# ...
# the rest of the transport schema definition here
},
}
```


## Runtime environment

Expand Down