Overview
RightScale provides a plugin framework that allows for any service with an HTTP-based API to be incorporated into the RightScale platform for use in Self-Service CAT files and Cloud Workflow code. This allows the use of many common services, such as cloud provider services, 3rd party vendors, and on-premises systems, within your application lifecycle management in Self-Service.
There are two primary components to the plugins in Self-Service, both of which are represented as declarations within a CAT file. The two declarations are:
plugin
- describes the external API resources, actions, types, structure, and more. This is a generic definition that can be reused and shared.resource_pool
- references aplugin
declaration and provides the usage-specific elements such as endpoint information and credentials
Once a plugin and resource pool have been defined in the CAT, then any resource type defined in the plugin can be used in the CAT definition. Primarily these are used in the CAT language to define new resources that are part of the application. For example, the following declaration defines a DigitalOcean droplet resource using a plugin called do
:
resource "my_droplet", type: "do.droplets" do
name "my_droplet"
region "us-east"
size "small"
end
Additionally, the plugin can define custom actions on any resource that can be leveraged in Cloud Workflow code. For example, if the droplet
resource has a custom action defined on it called reboot
, that action can be called directly from Cloud Workflow.
@my_droplet.reboot()
For a comprehensive reference of each plugin declaration please refer to the CAT Plugins reference.
If a resource provider doesn't support an HTTP-based mechanism for interacting with it, a Plugin Proxy can be built and deployed to act as the mediator between the RightScale platform and the provider. When building such a service, it is recommended that you use Goa or Praxis frameworks as this will simplify the definition of the Plugin in RightScale.
Plugins
A plugin in RightScale is simply a set of metadata about a service provider that the RightScale system can use to interact with the provider.
For any given provider, the plugin defines a set of resources that the provider has available, defined using the type
resource within the plugin. For each resource, the plugin defines:
- the fields of the resource that are needed (or optional) to create a new resource
- the structure of the API call needed to interact with this resource type
- the properties of the resource that can be used as
outputs
once a resource is created - the logic needed to create and destroy this kind of resource
- the operational actions available on this resource
- any links this resource has to other kinds of resources
In addition to resource information, the plugin also defines common attributes of the service, such as the base HTTP path to use for this service, any HTTP headers that must be sent along with a request, and other HTTP connection information.
Lastly, plugins may optionally contain any number of parameters
, configuration items that can be set when the plugin is instantiated.
Resource Pools
A Resource Pool is simply an instance of a Plugin, tied to a specific account
on the provider, made available to the RightScale platform to interact with. Many Resource Pools may be registered for any given plugin.
The Resource Pool defines any parameter values required by the Plugin, specifies the specific URL/host to use for this service, and specifies the authentication information needed to interact with this instance of the plugin.
Building a Plugin
The plugin declaration
The plugin
declaration in CAT is made up of 3 primary sections:
- Parameter - 0 or more configurable variables
- Endpoint - describes the HTTP endpoint information
- Resource Types - describe the resources made available by this plugin
Parameter
A plugin lists the parameters it uses to describe how to make the action requests. The syntax used to declare each parameter is identical to the CAT parameter syntax.
For more details, see the reference documentation.
Endpoint
The next section is the plugin endpoint section. It defines:
- the base path used by all requests made to the service
- the common HTTP headers used by all requests made to the service
- a default host and scheme for the service (the plugin resource pool declaration sets the final values)
Parameters may be used to define the values for any of the properties listed above.
For more details, see the reference documentation.
Resource Types
Finally, plugins also provide information about the resources made available from the given provider. This is the most powerful and complex part of the plugin definition. Each type declaration provides the following information:
- How to make HTTP requests to the type actions (both the CRUD and custom actions)
- How to extract resource hrefs from API responses to build resulting resource collections
- How to provision and delete resources via RCL definitions
- The fields required to build a resource collection of that type
- The fields exposed by the resource collection so that for example CloudApp outputs can use them
- The actions supported by the type and for custom actions the details needed to make the HTTP requests if the default doesn't work
- The links supported by the type including the information needed to build the link hrefs
An important design goal of the declaration syntax is to make it simple to describe simple cases. In that vein most type fields are optional and only required to override the defaults when they don't work.
For more details, see the reference documentation.
Creating an instance of a plugin
Actually using the resources defined in a plugin requires the use of the resource_pool
declaration. This declaration instantiates a plugin by providing actual values to the parameters and an actual endpoint and associated auth (required only if a default host is not defined in the plugin declaration). The auth
declaration allows specifying how requests sent to the service should be signed.
The syntax for a resource pool declaration is:
resource_pool "kube" do
# Name of plugin
plugin "kube"
# Values for plugin parameters
parameter_values do
namespace "us_east"
end
# Specify host, potentially overriding template's default host
host "kube-east.example.com"
# Service auth, see
auth "key", type: "api_key" do
key cred("KUBE_US_EAST_API_TOKEN")
format "Bearer $key"
location "header"
end
end
Using Plugins
Once the plugin has been declared and a resource pool has been instantiated from the plugin the resources it exposes can be taken advantage of in a CAT and RCL code the same way RightScale resources can.
Defining plugin packages
Plugins are intended to be defined inside of packages which are then used by other CATs. Note that in order to contain a plugin
declaration, a CAT must be of type 'plugin'
. These plugin-type CATs can contain other declarations and can be launched only as Test CloudApps (i.e. from Designer) -- with the intent that launching a plugin
type CAT is done during testing to ensure the plugin definition works as intended.
Once a plugin definition is complete, it is common to then import the plugin CAT into an application
CAT which can be published to the Catalog.
The following is an example of a plugin
CAT that is contained in the plugins/kubernetes
package.
name "Kube Plugin"
rs_ca_ver 20161221
short_description "A CAT defining the Kubernates plugin"
type 'plugin'
package "plugins/kubernetes"
plugin "kube" do
# Parameter definitions for the plugin
parameter "namespace" do
type "string"
label "Kubernates Namespace"
description "Namespace used to create all resources"
default "us-east"
end
parameter "region" do
type "string"
label "Kubernates Region"
description "The region in which the resources are created"
end
parameter "grouping" do
type "string"
label "Grouping"
description "The grouping"
end
# Endpoint definition
endpoint do
default_host "kube-$region.example.com" # `$region` is substituted with the value for the `region` parameter
default_scheme "https"
path "/api/v1/namespaces/$namespace"
headers do {
"User-Agent" => "Kube namespace"
} end
no_cert_check false
end
type "pods" do
href_templates "/namespaces/:metadata.namespace:/pods/:metadata.name:",
"/namespaces/:items[*].metadata.namespace:/pods/:items[*].metadata.name:"
provision "provision_pod"
delete "delete_pod"
field "kind" do
type "string"
required true
end
field "metadata" do
type "composite"
end
field "spec" do
type "composite"
end
output "kind", "apiVersion", "metadata", "spec", "status"
output "namespace" do
path "metadata.namespace"
end
output "status_phase" do
path "status.phase"
end
action "create" do
path "/pods"
verb "POST"
end
action "list" do
path "/pods"
verb "GET"
output_path "items"
end
action "get"
action "destroy"
end
end
define provision_pod(@raw) return @pod do
$raw = to_object(@raw)
$fields = $raw["fields"]
@pod = kube.pods.create($fields)
sleep_until @pod.status_phase == "Running"
end
define delete_pod(@pod) do
@operation = @pod.destroy()
end
Importing plugin packages
Once a plugin has been defined in a package then it can be imported and used within other CATs. Imported plugins are automatically made available -- there's no need to define a plugin which is like
them. They also do not need to be scoped by the imported package name.
Note the example below which imports the above plugin CAT and defines a resource_pool
for it. The below CAT is an application
CAT by default and can be published to the Catalog.
name "Kube Plugin"
rs_ca_ver 20161221
short_description "A CAT defining the Kubernates plugin"
import "plugins/kubernetes"
parameter "kub_region" do
type "string"
label "Kubernates Region"
description "Region used to create all resources"
end
mapping "grouping_map" do {
"grouping" => {
"1-a" => "Group 1",
"2-a" => "Group 2"
}
} end
resource_pool "kube_pool" do
plugin $kube # $kube is defined in "plugins/kubernetes"
host "mykube.example.com"
parameter_values do
region $kub_region
grouping map($grouping_map, "grouping", $kub_region)
end
auth "my_basic_auth", type: "basic" do
username "user"
password cred("MY_PASSWORD")
end
auth "my_JWT_auth", type: "jwt" do
basic @my_basic_auth
token_url "https://www.example.com/tokens"
field "Authorization"
end
end
output "custom_output" do
label "custom_output"
default_value @my_pod.status
description "Pod status from plugin output field"
end
resource "my_pod", type: "kube.pods" do
kind "Pod"
metadata do {
"image" => "nginx:latest"
} end
end
Plugins in CAT
The syntax consists of using the plugin name followed by a period (.
) as prefix to the resource name, taking the droplet resource type as an example:
# Assumes "docean" is the name of the plugin
resource "my_droplet", type: "docean.droplets" do
# "name" field values inherit from the resource name by default
name "my_droplet"
region "us-east"
size "small"
end
The resource is created running the corresponding provision definition and deleted using the delete definition. Authentication to the service is done using the information provided in the resource pool declaration auth section.
Other resource fields and outputs in the CAT may use the outputs defined by the plugin resource type, for example the droplet type above defines a memory output so a CAT using that type could define a droplet_memory
output as follows:
output "droplet_memory" do
label "Droplet RAM"
category "Configuration"
default_value @my_droplet.memory
end
XML Support
Simple types (string, number, boolean) can be assumed to be attributes. An array can be assumed to be an element and a (hash) map can be assumed to be a complex type (object). In order to convert a simple type to be an element instead of an attribute, it would have to be represented as a one-sized array containing only the simple type.
#defining a composite plugin field
field "create_hosted_zone_request" do
alias_for "CreateHostedZoneRequest"
type "composite"
required true
location "body"
end
Example 1: Resource Declaration
xmlns
is a stringName
is a single item arrayCallerReference
is a single item array
# plugin resource declaration
resource "hostedzone", type: "rs_aws_route53.hosted_zone" do
create_hosted_zone_request do {
"xmlns" => "https://route53.amazonaws.com/doc/2013-04-01/", # Simple type: Attribute
"Name" => [ join([first(split(uuid(),'-')), ".rs-example.com"]) ], # One-Sized Arrays become nested elements
"CallerReference" => [ uuid() ] # One-Sized Arrays become nested elements
} end
end
Example 1: Expected Output
<CreateHostedZoneRequest xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
<CallerReference>c0975dc6-8652-4bd8-9ba2-fc43b5006c79</CallerReference>
<Name>3898363a.rs-example.com</Name>
</CreateHostedZoneRequest>
Example 2: Resource Declaration
xmlns
is a stringName
is a single item arrayCallerReference
is a single item arrayHostedZoneConfig
is a hash
resource "hostedzone1", type: "rs_aws_route53.hosted_zone" do
create_hosted_zone_request do {
"xmlns" => "https://route53.amazonaws.com/doc/2013-04-01/", # Simple type: Attribute
"Name" => [ join([first(split(uuid(),'-')), ".rs-example.com"]) ], # One-Sized Arrays become nested elements
"CallerReference" => [ uuid() ], # One-Sized Arrays become nested elements
"HostedZoneConfig" => { # Hash
"Comment" => ["Test Zone"], # One-Sized Arrays become nested elements of Hash
"PrivateZone" => [ false ] # One-Sized Arrays become nested elements of Hash
}
} end
end
Example 2: Expected Output
<CreateHostedZoneRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\">
<CallerReference>f4ac75a9-efa9-4731-9920-3c96b8db07fd</CallerReference>
<HostedZoneConfig>
<Comment>Test Zone</Comment>
<PrivateZone>false</PrivateZone>
</HostedZoneConfig>
<Name>bdf6c3d1.rs-example.com</Name>
</CreateHostedZoneRequest>
Plugins in RCL
All the actions defined on the resource type in the plugin are available to RCL code. This makes it possible to define operations in the CAT that can take advantage of the custom actions, for example a backup
operation may take advantage of the snapshot
custom action exposed by a volume
plugin resource type.
define main(@my_droplet) do
@my_droplet.operate({action_name: "stop"})
end