Restructre clusterctl cli interface

Turn the flat list of commands into a hierarchy of commands, converting
most required parameters into positional parameters and add plugin
loading support for clusterctl.
This commit is contained in:
Hornwitser committed 2020-08-18 18:52:11 +02:00
1 parent 9b870fde64
commit da1cbc16f7
10 files changed
+783 -428

No files matched your search

+162 -64
View File
@@ -19,6 +19,7 @@ Contents
- [Sending Link Messages](#sending-link-messages)
- [Handling connection events](#handling-connection-events)
- [Collecting Statistics](#collecting-statistics)
- [Adding Custom Commands to clusterctl](#adding-custom-commands-to-clusterctl)
Plugin Structure
@@ -31,6 +32,7 @@ The basic file structure of a plugin is the following.
+- package.json
+- master.js
+- instance.js
+- control.js
+- test/
| +- plugin.js
+- module/
@@ -94,6 +96,12 @@ The following properties are recognized:
configuration fields for this plugin. See [Plugin
Configuration](#plugin-configuration)
**controlEntrypoint**:
Path to a Node.js module relative to the plugin directory which
contains the ControlPlugin class definition for this plugin. This
is an optional paramater. A plugin can be made that only runs on
the clusterctl side.
**messages**:
Object with link messages definitions for this plugin. See guide
for [defining link messages](#defining-link-messages) below.
@@ -137,8 +145,9 @@ code can be used:
}
For the instance plugin it's exactly the same except "Master" is
replaced with "Instance". The available hooks that you can override are
documented in the base class [in lib/plugin.js](../lib/plugin.js).
replaced with "Instance", and for the clusterctl plugin "Control" is
used. The available hooks that you can override are documented in the
base class [in lib/plugin.js](../lib/plugin.js).
It's best to avoid defining a constructor, but if you insist on defining
one, forward all arguments to the base class. E.g.:
@@ -318,46 +327,60 @@ the event, for example the following could be defined in `info.js`:
},
This specifies an event that can be sent from the master to a slave,
and from a slave to an instance. It also specifies that the event must contain the
property `frobnication_type`, with a string value in the data payload.
It will also be forwarded by slaves to a specific instance.
and from a slave to an instance. It also specifies that the event must
contain the property `frobnication_type`, with a string value in the
data payload. It will also be forwarded by slaves to a specific
instance.
The following properties are recognized by the Event constructor:
**type**:
The message type sent over the wire. This must start with the name
of the plugin followed by colon and and be unique for the plugin.
The type of the message sent over the socket will have the suffix
`_event` appended to it.
#### type
**links**:
An array of strings describing which links this event can be sent
over. Direction matters, `'master-slave'` means the event can be
sent from the master to the slave, but can't be sent back the other way,
unless `'slave-master'` is also present in the links array.
The message type sent over the wire. This must start with the name of
the plugin followed by colon and and be unique for the plugin. The type
of the message sent over the socket will have the suffix `_event`
appended to it.
**forwardTo**:
Target to forward an event to. Can either be `'master'`, to indicate a
slave should forward it to the master server, or `'instance'`, to
indicate it should be forwarded to the instances specified by the
`instance_id` event property. This works by using a default handler
for the event at the links that forward it.
#### links
**broadcastTo**:
Target to broadcast this message towards. A value of "instance" means
the event will be broadcast to all instances downstream of the target
it's sent to, but not back from where it came from. Currently, only
"instance" is supported. This means that sending an event to a slave
that slave except for the instance it came from.
from an instance will cause it to be broadcast to all instances of
An array of strings describing which links this event can be sent over.
Direction matters, `'master-slave'` means the event can be sent from the
master to the slave, but can't be sent back the other way, unless
`'slave-master'` is also present in the links array.
**eventProperties**:
Object with properties mapping to a JSON schema of that property
that specifies what's valid to send in the event. This is
equivalent to using the `properties` keyword in JSON schema, except
that the properties specified are implicitly required and additional
properties are not allowed. See [this guide][guide] for an
introduction to writing JSON schemas.
The available endpoints are `master`, `slave`, `instance`, and
`control`. Master talks with slave and control, and slave talks to
instance. The full chain must be specified as the individual links in
order for a message to travers multiple hops, (i.e., for a message to go
from master to instance it must have both `'master-slave'` and
`'slave-instance'` in the links array). See `forwardTo` and
`broadcastTo` for ways to forward an event to the next link in a chain.
#### forwardTo
Target to forward an event to. Can either be `'master'`, to indicate a
slave should forward it to the master server, or `'instance'`, to
indicate it should be forwarded to the instances specified by the
`instance_id` event property. This works by using a default handler for
the event at the links that forward it.
#### broadcastTo
Target to broadcast this message towards. A value of "instance" means
the event will be broadcast to all instances downstream of the target
it's sent to, but not back from where it came from. Currently, only
"instance" is supported. This means that sending an event to a slave
that slave except for the instance it came from. from an instance will
cause it to be broadcast to all instances of
#### eventProperties
Object with properties mapping to a JSON schema of that property that
specifies what's valid to send in the event. This is equivalent to
using the `properties` keyword in JSON schema, except that the
properties specified are implicitly required and additional properties
are not allowed. See [this guide][guide] for an introduction to writing
JSON schemas.
The forwardTo and broadcastTo can be combined such that specifying
`'master'` as the forwardTo value and `'instance'` as the broadcastTo
@@ -404,39 +427,51 @@ instance specified by `instance_id`.
The following properties are recognized by the Request constructor:
**type**:
The message type sent over the wire. This must start with the name
of the plugin followed by colon and and be unique for the plugin.
The type of the message sent over the socket will have the suffix
`_request` appended to it for the request and `_response` appended
to it for the response.
#### type
**links**:
An array of strings describing which links this request can be sent
over. Direction matters; `'master-slave'` means the request can be
sent from the master to the slave and the slave can reply to the master,
but the slave can't send a request to the master unless
`'slave-master'` is also present in the links array.
The message type sent over the wire. This must start with the name of
the plugin followed by colon and and be unique for the plugin. The type
of the message sent over the socket will have the suffix `_request`
appended to it for the request and `_response` appended to it for the
response.
**forwardTo**:
Target to forward the request to. Can either be `'master'` to indicate
a slave should forward it to the master server when receiving it
from an instance, or `'instance'` to indicate it should be forwarded
to the instances specified by the `instance_id` request property.
This works by using a default handler for the request by the links
that forward it.
#### links
**requestProperties**:
Object with properties mapping to a JSON schema of that property
that specifies what's valid to send in the request. This is
equivalent to using the `properties` keyword in JSON schema, except
that the properties specified are implicitly required and additional
properties are not allowed. See [this guide][guide] for an
introduction to writing JSON schemas
An array of strings describing which links this request can be sent
over. Direction matters; `'master-slave'` means the request can be sent
from the master to the slave and the slave can reply to the master, but
the slave can't send a request to the master unless `'slave-master'` is
also present in the links array.
**responseProperties**:
Same as the requestProperties only for the response sent back by the
target.
The available endpoints are `master`, `slave`, `instance`, and
`control`. Master talks with slave and control, and slave talks to
instance. The full chain must be specified as the individual links in
order for a message to travers multiple hops, (i.e., for a message to go
from master to instance it must have both `'master-slave'` and
`'slave-instance'` in the links array). See `forwardTo` for ways to
forward a request to the next link in a chain.
#### forwardTo
Target to forward the request to. Can either be `'master'` to indicate
a slave should forward it to the master server when receiving it from an
instance, or `'instance'` to indicate it should be forwarded to the
instances specified by the `instance_id` request property. This works
by using a default handler for the request by the links that forward it.
#### requestProperties
Object with properties mapping to a JSON schema of that property that
specifies what's valid to send in the request. This is equivalent to
using the `properties` keyword in JSON schema, except that the
properties specified are implicitly required and additional properties
are not allowed. See [this guide][guide] for an introduction to writing
JSON schemas
#### responseProperties
Same as the requestProperties only for the response sent back by the
target.
Sending Link Messages
@@ -554,3 +589,66 @@ label matches the id of the instance shut down.
For statistics you need to update on collection there's an `onMetrics`
hook on both master and instance plugins that is run before the
metrics in the default registry are collected.
Adding Custom Commands to clusterctl
------------------------------------
The control entrypoint for plugins allows you to extend clustectl with
your own commands. The creation of custom commands typically starts
with defining a command tree for the plugin:
const { Command, CommandTree } = require("lib/command");
const fooFrobberCommands = new CommandTree({
name: "foo-frobber", description: "Foo Frobber Plugin commands"
});
Then commands are added to the the plugin's command tree:
const info = require("./info");
fooFrobberCommands.add(new Command({
definition: ["frobnicate <type>", "Do frobnications", (yargs) => {
yargs.positional("level", {
describe: "type of frobnication", type: "string"
});
}],
handler: async function(args, control) {
await info.messages.frobnicate.send(control, {
instance_name: "Console",
content: args.message,
});
},
}));
For a command the `definition` is the arguments to pass to
[yargs.command](http://yargs.js.org/docs/#api-reference-commandcmd-desc-builder-handler)
(see also
[yargs.positional](http://yargs.js.org/docs/#api-reference-positionalkey-opt)
and
[yargs.options](http://yargs.js.org/docs/#api-reference-optionskey-opt)
for setting up positional and optional arguments to commands). The
`handler` is an async function that's invoked when the command is
executed and it's passed the parsed command line arguments and a
reference to the `Control` class of clusterctl. It's possible to optain
a reference to the plugin class with `control.plugins.get(info.name)`.
Note that messages sent from clusterctl needs to have
`"control-master"` as a part of the links array for it to be accepted by
the master server, see [Defining Link Messages](#defining-link-messages)
for how to define the messages that can be sent to the master.
To have the command tree become part of clusterctl it needs to be added
to the rootCommand tree in `addCommands` callback of the Control plugin:
const plugin = require("lib/plugin");
class ControlPlugin extends plugin.BaseControlPlugin {
async addCommands(rootCommand) {
rootCommand.add(fooFrobberCommands);
}
}
module.exports = {
ControlPlugin,
}