Skip to content

Commit

Permalink
move details from README to wiki
Browse files Browse the repository at this point in the history
  • Loading branch information
danryan committed Sep 3, 2012
1 parent 66f3701 commit 1fdf7c3
Showing 1 changed file with 8 additions and 245 deletions.
253 changes: 8 additions & 245 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Mastermind uses a special domain-specific language for its process definitions,
Here's an example of a basic sysadmin workflow, as implemented by Mastermind. Here, we create and destroy an EC2 instance, while notifying a Campfire room of each action performed.

```ruby
definition = Definition.new({
defn = Definition.create({
name: "create_and_destroy_ec2_server",
content: %q{
create_ec2_server image_id: '${image_id}',
Expand All @@ -33,7 +33,7 @@ definition = Definition.new({

job = Job.new({
name: "new ec2 instance workflow",
definition: "create_and_destroy_ec2_server",
definition: defn,
fields: {
flavor_id: "t1.micro",
image_id: "ami-fe5bd4ce",
Expand All @@ -50,254 +50,17 @@ job.launch

Mastermind has four basic pieces: a job; a definition; a participant; and a target.

## Job
## Jobs

A job is a template, tied to a definition. Jobs launch workflows.
See [Jobs in the wiki](https://github.com/danryan/mastermind/wiki/Jobs)

### Attributes
## Participants

* name (String) - The name of the job.
* definition (String) - The name of the definition that this job will launch.
* fields (Hash) - The initial attributes used by the definition, and ultimately each participant.
See [Participants in the wiki](https://github.com/danryan/mastermind/wiki/Participants)

### Example job
## Targets

```ruby
job = Job.new({
name: "do the needful",
definition: "execute remote ssh",
fields: {
host: "db-master.example.com",
user: "root",
key_data: "-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----"
command: "rm -rf /"
}
})
```

## Definition

A definition is the workflow itself. It's the document that describes exactly what tasks will be performed, and in what order.

### Attributes

* name (String) - The name of the definition.
* content (Array) - The process definition.

### Dollar notation ${...}

Mastermind scans strings in process definition for `${...}` placeholders and substitutes them for fields provided by the job or fields added by participants during the execution of the process. Any field interpolated by `${...}` will be the string representation of the value. If the literal value of the field is needed (for instance, if a field holds an array of values), use the `$f:` notation instead.

Given the job fields:

```ruby
{
:name => "Dan Ryan",
:titles => [ "Future Mayor of Lansing, MI", "Thoulght Leader" ]
}
```

The following definition...:

```ruby
person :name => "${name}", :titles => "$f:titles"
```

...gets compiled to:

```ruby
person :name => "Dan Ryan", :titles => [ "Future Mayor of Lansing, MI", "Thoulght Leader" ]
```

## Example definition

```ruby
Definition.new(
:name => "standard syntax",
:content => %q{
run_ssh host: '${host}',
user: '${user}',
key_data: '${key_data}',
command: '${command}'
}
).to_pdef

# => compiled definition
#
# ["define",
# {"name"=>"watee"},
# [["run_ssh",
# {"host"=>"${host}",
# "user"=>"${user}",
# "key_data"=>"${key_data}",
# "command"=>"${command}"},
# []]]]
```

The previous example uses Ruby 1.9-style hash syntax. If you prefer the look of the "hash rocket", you are more than welcome to use it instead!

```ruby
Definition.new(
:name => "hash rockets",
:content => %q{
run_ssh :host => '${host}',
:user => '${user}',
:key_data => '${key_data}',
:command => '${command}'
}
).to_pdef

# => compiled definition
#
# ["define",
# {"name"=>"watee"},
# [["run_ssh",
# {"host"=>"${host}",
# "user"=>"${user}",
# "key_data"=>"${key_data}",
# "command"=>"${command}"},
# []]]]
```

You can even mix in plain old Ruby if you're feeling adventurous!
```ruby
hosts = %w( host1.example.com host2.example.com host3.example.com )

Definition.new(
:name => "plain ol' ruby!",
:content => %q{
hosts = %w( host1.example.com host2.example.com host3.example.com )
hosts.each do |host|
run_ssh :host => host,
:user => '${user}',
:key_data => '${key_data}',
:command => '${command}'
end
}
).to_pdef

# Same as:
Definition.new(
:name => "plain ol' ruby!",
:content => %q{
run_ssh :host => "host1.example.com",
:user => '${user}',
:key_data => '${key_data}',
:command => '${command}'
run_ssh :host => "host2.example.com",
:user => '${user}',
:key_data => '${key_data}',
:command => '${command}'
run_ssh :host => "host3.example.com",
:user => '${user}',
:key_data => '${key_data}',
:command => '${command}'
}
).to_pdef

# => compiled definition
#
# ["define",
# {"name"=>"watee"},
# [["run_ssh",
# {"host"=>"host1.example.com",
# "user"=>"${user}",
# "key_data"=>"${key_data}",
# "command"=>"${command}"},
# []],
# ["run_ssh",
# {"host"=>"host2.example.com",
# "user"=>"${user}",
# "key_data"=>"${key_data}",
# "command"=>"${command}"},
# []],
# ["run_ssh",
# {"host"=>"host3.example.com",
# "user"=>"${user}",
# "key_data"=>"${key_data}",
# "command"=>"${command}"},
# []]]]
```

## Participant

A participant is responsible for performing tasks as specified by the definition. There are many types of participants. A participant can provision a server, send notifications, or even execute remote commands.

### Attributes

Participant attributes vary depending on their purpose.

### Example participant

```ruby
require 'net/ssh'

module Participant::Remote
class SSH < Participant
register :ssh

action :run do
requires :host, :user, :key_data, :command

target.output = run_ssh(target.command)
return {}
end

def run_ssh(command)
session = Net::SSH.start(target.host, target.user, { key_data: target.key_data })
output = nil

session.open_channel do |ch|
ch.request_pty
ch.exec command do |ch, success|
raise ArgumentError, "Cannot execute #{target.command}" unless success
ch.on_data do |ichannel, data|
output = data
end
end
end

session.loop
session.close
return output if output
end
end
end
```

## Target

A target is the resource that the participant modifies. The target can have a variety of attributes, depending on the participant's needs. The target performs the majority of validations to ensure the participant has the right fields to execute its actions.

### Attributes

Target attributes vary depending on their purpose.

### Example target

```ruby
# Modules are used as namespaces.
module Target::Remote

# All targets inherit from Target
class SSH < Target

# We'll refer to this target elsewhere by the name we use to register it.
register :ssh

# Various attributes of the target. The :type option typecasts the attribute.
attribute :command, type: String
attribute :host, type: String
attribute :user, type: String
attribute :key_data, type: String
attribute :output, type: String

# Validate the target to ensure we have the right details.
validates! :command, :host, :user, :key_data,
presence: true
end
end
```
See [Targets in the wiki](https://github.com/danryan/mastermind/wiki/Targets)

# Dependencies

Expand Down

0 comments on commit 1fdf7c3

Please sign in to comment.