vagabond 0.1.4 → 0.2.0

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.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## v0.2.0
2
+ * Migrate to thor
3
+ * Clean up option usage
4
+ * Add support for custom template builds
5
+ * DRY out some reusable stuffs
6
+ * Change Vagabondfile key :boxes to :nodes
7
+ * Start on defined color codes for actions/states (not implemented)
8
+ * Add some validation and error checking
9
+ * Make server stuff work better
10
+ * Add integrated support for test kitchen 1.0
11
+ * Add new 'cluster' support for test kitchen 1.0
12
+
1
13
  ## v0.1.4
2
14
  * Fix color option to do the right thing
3
15
 
data/README.md CHANGED
@@ -14,6 +14,10 @@ easily and most importantly, quickly. It uses Linux containers
14
14
  instead of full blown VMs which means things are faster. Lots
15
15
  faster.
16
16
 
17
+ Vagabond is built for Chef. The tooling within Vagabond is targeted
18
+ at Chef. As the initial development push on Vagabond slows, this
19
+ tooling will become optionally swappable (applicable bits at least).
20
+
17
21
  ## Installation
18
22
 
19
23
  As a rubygem:
@@ -22,16 +26,16 @@ As a rubygem:
22
26
  $ gem install vagabond
23
27
  ```
24
28
 
25
- ## How it is?
29
+ ## How does it work
26
30
 
27
31
  Currently, this is built to run within a classic Chef repository.
28
- It requires a Vagabond file, that simply outputs a Hash. The file
32
+ It requires a `Vagabond` file, that simply outputs a Hash. The file
29
33
  is Ruby though, so you can do lots of crazy stuff to build the
30
34
  Hash you return. Heres a simple example:
31
35
 
32
36
  ```ruby
33
37
  {
34
- :boxes => {
38
+ :nodes => {
35
39
  :precise => {
36
40
  :template => 'ubuntu_1204',
37
41
  :run_list => %w(role[base])
@@ -48,6 +52,12 @@ Hash you return. Heres a simple example:
48
52
  }
49
53
  ```
50
54
 
55
+ Now, to create a node, simply run:
56
+
57
+ ```
58
+ $ vagabond up db
59
+ ```
60
+
51
61
  Pretty simple, right?
52
62
 
53
63
  ### Templates available
@@ -64,49 +74,8 @@ Currently builtin templates:
64
74
 
65
75
  ## Commands
66
76
 
67
- Lots of commands. What to see them all? Just ask:
68
-
69
- ```
70
- $ vagabond --help
71
- Nodes:
72
- vagabond create NODE [options]
73
- vagabond destroy NODE [options]
74
- vagabond freeze NODE [options]
75
- vagabond provision NODE [options]
76
- vagabond rebuild NODE [options]
77
- vagabond ssh NODE [options]
78
- vagabond start NODE [options]
79
- vagabond status NODE [options]
80
- vagabond thaw NODE [options]
81
- vagabond up NODE [options]
82
- Server:
83
- vagabond server auto_upload [options]
84
- vagabond server create [options]
85
- vagabond server destroy [options]
86
- vagabond server freeze [options]
87
- vagabond server provision [options]
88
- vagabond server rebuild [options]
89
- vagabond server ssh [options]
90
- vagabond server start [options]
91
- vagabond server status [options]
92
- vagabond server stop [options]
93
- vagabond server thaw [options]
94
- vagabond server up [options]
95
- vagabond server upload_cookbooks [options]
96
- vagabond server upload_databags [options]
97
- vagabond server upload_environments [options]
98
- vagabond server upload_roles [options]
99
- Knife:
100
- vagabond knife COMMAND [knife_options]
101
- Options:
102
- --color
103
- --debug
104
- --disable-auto-provision
105
- --disable-local-server
106
- --disable-configure
107
- --force-configure
108
- -f, --vagabond-file FILE
109
- ```
77
+ See the `USAGE` file for an overview of available commands and their
78
+ usage.
110
79
 
111
80
  ## Local chef server?
112
81
 
@@ -133,6 +102,35 @@ vagabond knife SOME COOL KNIFE COMMAND
133
102
 
134
103
  This will just push the command into the local chef server.
135
104
 
105
+ ## Test Kitchen
106
+
107
+ Vagabond provides test kitchen 1.0 support. It will map boxes defined
108
+ within platforms to platform templates available (to the best of its
109
+ ability). No need to spin up vagrant VMs, or use another tool. Point
110
+ vagabond at the cookbook, and let it handle the details.
111
+
112
+ In the TODO pipeline is allowing platform mapping in the Vagabondfile
113
+ so custom templates (with memory limits for example) can be used
114
+ instead of the base templates.
115
+
116
+ ### Cluster testing
117
+
118
+ Vagabond adds an extra feature to test kitchen: cluster testing. This
119
+ type of testing uses the local chef server, and provides an extreme
120
+ amount of power to tests. Instead of provisioning a node and simply
121
+ testing it in isolation, cluster testing provides the support to
122
+ provision multiple nodes against the local chef server. Once all
123
+ nodes have been successfully provisioned, vagabond will go back through
124
+ each node and run tests.
125
+
126
+ Seems simple, right? It is, but it's also extremely powerful. Instead
127
+ of testing things individually and isolated, this allows for real
128
+ integration testing. Tests can be applied to discovery, slaving,
129
+ and all the other fun things nodes may be doing that require a
130
+ chef server. Looking for example? See the `USAGE` file!
131
+
132
+ Double awesome
133
+
136
134
  ## Important note
137
135
 
138
136
  Until namespaces hit Linux proper, vagabond `sudo`s its way around. You
@@ -152,8 +150,10 @@ boring old `sudo`, you can do that to:
152
150
 
153
151
  ## Extra note
154
152
 
155
- This is still very much in alpha testing phase. So if you find bugs, please
156
- report them!
153
+ This thing is still very new and shiny with lots of sharp edges. They
154
+ are getting sanded down as quickly as possible. If you find bugs, are
155
+ confused about some of the available functionality, or just want to point
156
+ out some stupidity that I have implemented, please file an issue on github!
157
157
 
158
158
  ## Contributing
159
159
 
data/USAGE.md ADDED
@@ -0,0 +1,197 @@
1
+ # Vagabond USAGE
2
+
3
+ ## Requirements
4
+
5
+ * Chef repository structure
6
+ * Ubuntu >= 12.04
7
+ * This requirement is soft. It is only here because it is the only place it has currently been tested. Testing on centos, debian, and arch is coming soon.
8
+
9
+ ## Setup
10
+
11
+ * Install vagabond either in a Gemfile, or via gem:
12
+
13
+ ```
14
+ $ gem install vagabond
15
+ ```
16
+
17
+ Drop a Vagabondfile in the root of your Chef repository. Here is a
18
+ very simple Vagabond file to start with:
19
+
20
+ ```ruby
21
+ {
22
+ :nodes => {
23
+ :my_precise_node => {
24
+ :template => 'ubuntu_1204',
25
+ :run_list => ['role[base]']
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ This assumes you have a role named base, and that it and its dependencies
32
+ are currently pushed up to your configured chef server. So, we create our
33
+ node:
34
+
35
+ ```
36
+ $ vagabond up my_precise_node
37
+ ```
38
+
39
+ The first time this runs, it will take some time. Vagabond will provision
40
+ your local system (using chef-solo) to install the required LXC tools, and
41
+ to build the templates in use by your Vagabond file. Vagabond will do
42
+ this any time it determines it's required, generally when new templates
43
+ are discovered that does not already have a base container built. So sit
44
+ back, the first build will take a few.
45
+
46
+ ## Custom templates
47
+
48
+ Custom templates are templates that are based on the builtin templates
49
+ but have some restriction placed on them, for example memory usage. Using
50
+ our current Vagabond file example, lets say we wanted to use a container
51
+ that only had 512MB of memory available to it, and no swap space. We
52
+ would provide the details for a custom template, and set the `my_precise_node`
53
+ to then use that template:
54
+
55
+
56
+ ```ruby
57
+ {
58
+ :nodes => {
59
+ :my_precise_node => {
60
+ :template => 'custom_1204_512',
61
+ :run_list => ['role[base]']
62
+ }
63
+ },
64
+ :templates => {
65
+ :custom_1204_512 => {
66
+ :base => 'ubuntu_1204',
67
+ :memory => {
68
+ :ram => '512M',
69
+ :swap => 0
70
+ }
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## vagabond
77
+
78
+ The `vagabond` command is used for interaction with nodes. Simply running:
79
+
80
+ ```
81
+ $ vagabond
82
+ ```
83
+
84
+ will provide a list of available actions.
85
+
86
+ ## vagabond ssh
87
+
88
+ This tool just provides an easy way to SSH into running nodes. Just
89
+ provide the name and it will drop you into a root session:
90
+
91
+ ```
92
+ $ vagabond ssh my_precise_node
93
+ ```
94
+
95
+ ## vagabond server
96
+
97
+ Vagabond will optionally allow the installation of a chef server that is
98
+ localized to the chef repository the Vagabondfile is kept within. This
99
+ is enabled in the Vagabondfile. To do this, our Vagabondfile would now
100
+ look like this:
101
+
102
+ ```ruby
103
+ {
104
+ :nodes => {
105
+ :my_precise_node => {
106
+ :template => 'ubuntu_1204',
107
+ :run_list => ['role[base]']
108
+ }
109
+ },
110
+ :local_chef_server => {
111
+ :enabled => true
112
+ }
113
+ }
114
+ ```
115
+
116
+ The next command run will trigger vagabond to reprovision and will create
117
+ a server container. The chef server will be auto configured using your
118
+ existing client information. It will seamlessly take over while using
119
+ Vagabond. The server commands are explicitly for the server container.
120
+ The commands are similar to the basic `vagabond` commands, with a few
121
+ extra commands as well.
122
+
123
+ The `:local_chef_server` hash also has two helper keys for setting up
124
+ the server:
125
+
126
+ * `:auto_upload` - Uploads all cookbooks, roles, data bags and environments after build
127
+ * `:berkshelf` - Uses berkshelf for cookbook upload instead of knife
128
+
129
+ ## vagabond knife
130
+
131
+ This tool will let you communicate with the chef server provided by Vagabond.
132
+ Just pass arguments to it like you would knife regularly, and they will
133
+ be set to the local chef server instead:
134
+
135
+ ```
136
+ $ vagabond knife cookbook list
137
+ ```
138
+
139
+ # Testing
140
+
141
+ Vagabond has built in support for test-kitchen 1.0. However, it does things
142
+ just a little bit differently. First, it will map the platform defined to an lxc
143
+ template. Second, it will use Librarian to resolve dependencies for the
144
+ cookbook and use those for testing.
145
+
146
+ Usage is straightforward:
147
+
148
+ ```
149
+ $ vagabond kitchen test COOKBOOK
150
+ ```
151
+
152
+ You can limit what you test at once by providing `--platform` and/or `--suites`
153
+ options to the test. Tests are run the same was as t-k 1.0. Run lists will be
154
+ merged, attributes pushed to the node, and solo to provision the node.
155
+
156
+ ## Clusters
157
+
158
+ Vagabond offers an extra type of testing. Instead of simply providing an isolated
159
+ place to test a cookbook (on a single node), Vagabond allows you to run tests against
160
+ a cluster of nodes, with a real chef server. This allows for real integration testing
161
+ within a real isolated environment. Multiple nodes. Real Chef server. A real environment
162
+ that can properly test the behavior of an infrastructure.
163
+
164
+ ## How it works
165
+
166
+ Currently, Vagabond adds an extra key the `.kitchen.yml` file of a cookbook. It's
167
+ the `clusters` key. It's a simple hash. The keys are the names of the cluster. The
168
+ values are arrays of strings that identify the suites to be built for the cluster.
169
+ A simple example looks like this:
170
+
171
+ ```
172
+ clusters:
173
+ default:
174
+ - cool_suite1
175
+ - cool_suite2
176
+ ```
177
+
178
+ And now, we can spin this up doing:
179
+
180
+ ```
181
+ $ vagabond kitchen test COOKBOOK --cluster default --platform ubuntu-12.04
182
+ ```
183
+
184
+ First, this will restrict our testing to just the ubuntu-12.04 platform. Next
185
+ it will upload all assets to the local chef server. It then provisions two
186
+ nodes. First it provisions cool_suite1. Next it provisions cool_suite2. These
187
+ nodes are defined by their configuration in the suites. After the nodes
188
+ have successfully provisioned, test kitchen is then run on each of the nodes
189
+ in the cluster sequentially.
190
+
191
+ Simple, yet so very very awesome. \o/
192
+
193
+ ## More to come
194
+
195
+ Testing support is still very young, just like Vagabond. But there are plans
196
+ in the works for more features. Take a look at the issues section in github
197
+ with `kitchen` tags. Feel free to add more if you see something missing!
data/bin/vagabond CHANGED
@@ -1,6 +1,28 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ Signal.trap('INT'){ exit 255 }
4
+
3
5
  require 'rubygems'
4
- require 'vagabond/commands'
6
+ require 'vagabond'
5
7
 
6
- Vagabond::Commands.new.run!(ARGV)
8
+ if(ARGV.include?('--version') || ARGV.include?('-v'))
9
+ require 'vagabond/vagabond'
10
+ Vagabond::Vagabond.new.send(:version)
11
+ else
12
+ case arg = ARGV.shift
13
+ when 'server'
14
+ require 'vagabond/server'
15
+ Vagabond::Server
16
+ when 'knife'
17
+ require 'vagabond/knife'
18
+ ARGV.unshift(arg) unless ARGV.empty?
19
+ Vagabond::Knife
20
+ when 'test', 'kitchen'
21
+ require 'vagabond/kitchen'
22
+ Vagabond::Kitchen
23
+ else
24
+ require 'vagabond/vagabond'
25
+ ARGV.unshift(arg)
26
+ Vagabond::Vagabond
27
+ end.start
28
+ end
@@ -1,10 +1,11 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module Create
4
- def create
4
+ def _create
5
+ name_required!
5
6
  if(lxc.exists?)
6
7
  ui.warn "Node already exists: #{name}" unless name == 'server'
7
- start
8
+ _start
8
9
  else
9
10
  ui.info "#{ui.color('Vagabond:', :bold)} Creating #{ui.color(name, :green)}"
10
11
  do_create
@@ -15,12 +16,20 @@ module Vagabond
15
16
  private
16
17
 
17
18
  def do_create
18
- com = "#{sudo}lxc-start-ephemeral -d -o #{config[:template]}"
19
+ tmpl = config[:template]
20
+ if(internal_config[:template_mappings].keys.include?(tmpl))
21
+ tmpl = internal_config[:template_mappings][tmpl]
22
+ elsif(!BASE_TEMPLATES.include?(tmpl))
23
+ ui.fatal "Template requested for node does not exist: #{tmpl}"
24
+ exit EXIT_CODES[:invalid_template]
25
+ end
26
+ bind_path = File.expand_path(File.dirname(vagabondfile.path))
27
+ com = "#{sudo}lxc-start-ephemeral -d -b #{bind_path} -o #{tmpl}"
19
28
  debug(com)
20
- c = Mixlib::ShellOut.new("#{com} && sleep 3", :live_stream => Config[:debug])
29
+ c = Mixlib::ShellOut.new("#{com} && sleep 3", :live_stream => options[:debug])
21
30
  c.run_command
22
31
  e_name = c.stdout.split("\n").last.split(' ').last.strip
23
- @internal_config[:mappings][name] = e_name
32
+ @internal_config[mappings_key][name] = e_name
24
33
  @internal_config.save
25
34
  @lxc = Lxc.new(e_name)
26
35
  end
@@ -1,7 +1,9 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module Destroy
4
- def destroy
4
+
5
+ def _destroy
6
+ name_required!
5
7
  if(lxc.exists?)
6
8
  ui.info "#{ui.color('Vagabond:', :bold)} Destroying node: #{ui.color(name, :red)}"
7
9
  do_destroy
@@ -15,24 +17,24 @@ module Vagabond
15
17
 
16
18
  def do_destroy
17
19
  lxc.stop if lxc.running?
18
- com = "#{Config[:sudo]}lxc-destroy -n #{lxc.name}"
20
+ com = "#{options[:sudo]}lxc-destroy -n #{lxc.name}"
19
21
  debug(com)
20
- cmd = Mixlib::ShellOut.new(com, :live_stream => Config[:debug])
22
+ cmd = Mixlib::ShellOut.new(com, :live_stream => options[:debug])
21
23
  cmd.run_command
22
24
  cmd.error!
23
25
  if(cmd.stderr.include?('skipping'))
24
26
  ui.info ui.color(' -> Failed to unmount some resources. Forcing manually.', :yellow)
25
27
  %w(rootfs ephemeralbind).each do |mnt|
26
- com = "#{Config[:sudo]}umount /var/lib/lxc/#{lxc.name}/#{mnt}"
28
+ com = "#{options[:sudo]}umount /var/lib/lxc/#{lxc.name}/#{mnt}"
27
29
  debug(com)
28
- cmd = Mixlib::ShellOut.new(com, :live_stream => Config[:debug])
30
+ cmd = Mixlib::ShellOut.new(com, :live_stream => options[:debug])
29
31
  cmd.run_command
30
- com = "#{Config[:sudo]}lxc-destroy -n #{lxc.name}"
32
+ com = "#{options[:sudo]}lxc-destroy -n #{lxc.name}"
31
33
  debug(com)
32
- cmd = Mixlib::ShellOut.new(com, :live_stream => Config[:debug])
34
+ cmd = Mixlib::ShellOut.new(com, :live_stream => options[:debug])
33
35
  cmd.run_command
34
36
  cmd.error!
35
- internal_config[:mappings].delete(name)
37
+ internal_config[mappings_key].delete(name)
36
38
  end
37
39
  internal_config.save
38
40
  end
@@ -1,7 +1,8 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module Freeze
4
- def freeze
4
+ def _freeze
5
+ name_required!
5
6
  if(lxc.exists?)
6
7
  ui.info "#{ui.color('Vagabond:', :bold)} Freezing node: #{ui.color(name, :blue)}"
7
8
  if(lxc.running?)
@@ -1,7 +1,8 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module Provision
4
- def provision
4
+ def _provision
5
+ name_required!
5
6
  if(lxc.exists?)
6
7
  if(lxc.running?)
7
8
  do_provision
@@ -22,8 +23,8 @@ module Vagabond
22
23
  if(config[:environment])
23
24
  com << "-E #{config[:environment]}"
24
25
  end
25
- if(Config[:knife_opts])
26
- com << Config[:knife_opts]
26
+ if(options[:knife_opts])
27
+ com << options[:knife_opts]
27
28
  end
28
29
  debug(com)
29
30
  # Send the live stream out since people will generally want to
@@ -33,8 +34,10 @@ module Vagabond
33
34
  # NOTE: cmd.status.success? won't be valid, so check for FATAL
34
35
  unless(cmd.stdout.split("\n").last.to_s.include?('FATAL'))
35
36
  ui.info ui.color(' -> PROVISIONED', :magenta)
37
+ true
36
38
  else
37
39
  ui.info ui.color(' -> PROVISION FAILED', :red)
40
+ false
38
41
  end
39
42
  end
40
43
 
@@ -1,15 +1,16 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module Rebuild
4
- def rebuild
4
+ def _rebuild
5
+ name_required!
5
6
  ui.info "#{ui.color('Vagabond:', :bold)} Rebuilding #{ui.color(name, :blue)}"
6
- destroy
7
+ _destroy
7
8
  @lxc = Lxc.new(name)
8
- destroy
9
- Config[:force_solo] = true
9
+ _destroy
10
+ options[:force_solo] = true
10
11
  ui.info ui.color(' -> DESTROYED!', :red)
11
12
  internal_config.run_solo
12
- internal_config[:mappings].delete(name)
13
+ internal_config[mappings_key].delete(name)
13
14
  internal_config.save
14
15
  ui.info ui.color(' -> REBUILT!', :green)
15
16
  end
@@ -1,11 +1,12 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module SSH
4
- def ssh
4
+ def _ssh
5
+ name_required!
5
6
  if(lxc.exists?)
6
7
  if(lxc.running?)
7
8
  ui.info "#{ui.color('Vagabond:', :bold)} SSH connect to: #{ui.color(name, :cyan)}"
8
- exec("#{Config[:sudo]}ssh root@#{lxc.container_ip(10, true)} -i /opt/hw-lxc-config/id_rsa -oStrictHostKeyChecking=no")
9
+ exec("#{options[:sudo]}ssh root@#{lxc.container_ip(10, true)} -i /opt/hw-lxc-config/id_rsa -oStrictHostKeyChecking=no")
9
10
  else
10
11
  ui.error "Node not running: #{name}"
11
12
  end
@@ -2,7 +2,8 @@ module Vagabond
2
2
  module Actions
3
3
  module Start
4
4
 
5
- def start
5
+ def _start
6
+ name_required!
6
7
  ui.info "#{ui.color('Vagabond:', :bold)} Starting node: #{ui.color(name, :green)}"
7
8
  do_start
8
9
  ui.info ui.color(' -> STARTED', :green)
@@ -1,12 +1,28 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module Status
4
- def status
4
+ class << self
5
+ def included(klass)
6
+ klass.class_eval do
7
+ class << self
8
+ def _status_desc
9
+ if(defined?(Server) && self == Server)
10
+ ['status', 'Status of server']
11
+ else
12
+ ['status [NODE]', 'Status of NODE or all nodes']
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ def _status
5
21
  ui.info ui.color("Vagabond node status:\n", :bold)
6
22
  if(name)
7
23
  status_for(name)
8
24
  else
9
- (Array(vagabondfile[:boxes].keys) | Array(internal_config[:mappings].keys)).sort.each do |n|
25
+ (Array(vagabondfile[:boxes].keys) | Array(internal_config[mappings_key].keys)).sort.each do |n|
10
26
  status_for(n)
11
27
  end
12
28
  end
@@ -15,7 +31,7 @@ module Vagabond
15
31
  private
16
32
 
17
33
  def status_for(c_name)
18
- m_name = internal_config[:mappings][c_name]
34
+ m_name = internal_config[mappings_key][c_name]
19
35
  state = nil
20
36
  status = []
21
37
  if(Lxc.exists?(m_name))
@@ -1,7 +1,8 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module Thaw
4
- def thaw
4
+ def _thaw
5
+ name_required!
5
6
  if(lxc.exists?)
6
7
  if(lxc.frozen?)
7
8
  ui.info "#{ui.color('Vagabond:', :bold)} Thawing node: #{ui.color(name, :yellow)}"
@@ -1,7 +1,20 @@
1
1
  module Vagabond
2
2
  module Actions
3
3
  module Up
4
- def up
4
+ class << self
5
+ def included(klass)
6
+ klass.class_eval do
7
+ class << self
8
+ def _up_options
9
+ [[:auto_provision, :type => :boolean, :default => true]]
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ def _up
17
+ name_required!
5
18
  if(lxc.exists?)
6
19
  if(lxc.running?)
7
20
  ui.error "Node already exists and is running: #{name}"
@@ -11,9 +24,9 @@ module Vagabond
11
24
  ui.info ui.color(' -> STARTED', :green)
12
25
  end
13
26
  else
14
- create
27
+ _create
15
28
  end
16
- do_provision unless Config[:disable_auto_provision]
29
+ do_provision if options[:auto_provision]
17
30
  end
18
31
 
19
32
  end
@@ -0,0 +1,36 @@
1
+ require 'chef/mash'
2
+
3
+ module Vagabond
4
+ BASE_TEMPLATES = File.readlines(
5
+ File.join(File.dirname(__FILE__), 'cookbooks/vagabond/attributes/default.rb')
6
+ ).map do |l|
7
+ l.scan(%r{bases\]\[:([^\]]+)\]}).flatten.first
8
+ end.compact.uniq
9
+
10
+ COLORS = Mash.new(
11
+ :success => :green,
12
+ :create => :green,
13
+ :setup => :blue,
14
+ :error => :red,
15
+ :failed => :red,
16
+ :verified => :yellow,
17
+ :converged => :magenta,
18
+ :destroyed => :red,
19
+ :kitchen => [:cyan, :bold]
20
+ )
21
+
22
+ EXIT_CODES = Mash.new(
23
+ :success => 0,
24
+ :reserved_name => 1,
25
+ :invalid_name => 2,
26
+ :invalid_base_template => 3,
27
+ :invalid_action => 4,
28
+ :invalid_template => 5,
29
+ :kitchen_missing_yml => 6,
30
+ :kitchen_no_cookbook_arg => 7,
31
+ :kitchen_too_many_args => 8,
32
+ :kitchen_invalid_platform => 9,
33
+ :missing_node_name => 10,
34
+ :cluster_invalid => 11
35
+ )
36
+ end