vagabond 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/CHANGELOG.md +18 -0
  2. data/README.md +125 -4
  3. data/bin/vagabond +43 -16
  4. data/lib/vagabond/actions/cluster.rb +66 -0
  5. data/lib/vagabond/actions/create.rb +12 -7
  6. data/lib/vagabond/actions/destroy.rb +69 -15
  7. data/lib/vagabond/actions/init.rb +75 -0
  8. data/lib/vagabond/actions/provision.rb +4 -2
  9. data/lib/vagabond/actions/status.rb +33 -22
  10. data/lib/vagabond/actions/up.rb +8 -1
  11. data/lib/vagabond/bootstraps/server-zero.erb +20 -0
  12. data/lib/vagabond/bootstraps/server.erb +3 -2
  13. data/lib/vagabond/constants.rb +0 -15
  14. data/lib/vagabond/cookbooks/lxc/CHANGELOG.md +16 -0
  15. data/lib/vagabond/cookbooks/lxc/Gemfile +3 -2
  16. data/lib/vagabond/cookbooks/lxc/Gemfile.lock +30 -121
  17. data/lib/vagabond/cookbooks/lxc/README.md +43 -14
  18. data/lib/vagabond/cookbooks/lxc/attributes/default.rb +3 -3
  19. data/lib/vagabond/cookbooks/lxc/files/default/lxc-awesome-ephemeral +499 -0
  20. data/lib/vagabond/cookbooks/lxc/libraries/lxc.rb +223 -58
  21. data/lib/vagabond/cookbooks/lxc/libraries/lxc_file_config.rb +3 -0
  22. data/lib/vagabond/cookbooks/lxc/libraries/monkey.rb +51 -0
  23. data/lib/vagabond/cookbooks/lxc/metadata.rb +6 -5
  24. data/lib/vagabond/cookbooks/lxc/providers/config.rb +9 -16
  25. data/lib/vagabond/cookbooks/lxc/providers/container.rb +241 -229
  26. data/lib/vagabond/cookbooks/lxc/providers/default.rb +57 -0
  27. data/lib/vagabond/cookbooks/lxc/providers/ephemeral.rb +40 -0
  28. data/lib/vagabond/cookbooks/lxc/providers/fstab.rb +13 -54
  29. data/lib/vagabond/cookbooks/lxc/providers/interface.rb +13 -67
  30. data/lib/vagabond/cookbooks/lxc/providers/service.rb +14 -14
  31. data/lib/vagabond/cookbooks/lxc/recipes/default.rb +17 -4
  32. data/lib/vagabond/cookbooks/lxc/recipes/install_dependencies.rb +1 -1
  33. data/lib/vagabond/cookbooks/lxc/resources/config.rb +2 -2
  34. data/lib/vagabond/cookbooks/lxc/resources/container.rb +31 -6
  35. data/lib/vagabond/cookbooks/lxc/resources/default.rb +12 -0
  36. data/lib/vagabond/cookbooks/lxc/resources/ephemeral.rb +13 -0
  37. data/lib/vagabond/cookbooks/lxc/resources/fstab.rb +2 -1
  38. data/lib/vagabond/cookbooks/lxc/resources/interface.rb +6 -3
  39. data/lib/vagabond/cookbooks/lxc/resources/service.rb +1 -1
  40. data/lib/vagabond/cookbooks/lxc/templates/default/file_content.erb +2 -0
  41. data/lib/vagabond/cookbooks/lxc/templates/default/interface.erb +9 -3
  42. data/lib/vagabond/cookbooks/vagabond/README.md +10 -0
  43. data/lib/vagabond/cookbooks/vagabond/attributes/default.rb +1 -0
  44. data/lib/vagabond/cookbooks/vagabond/files/default/lxc-centos +13 -6
  45. data/lib/vagabond/cookbooks/vagabond/metadata.rb +1 -0
  46. data/lib/vagabond/cookbooks/vagabond/recipes/default.rb +46 -4
  47. data/lib/vagabond/cookbooks/vagabond/recipes/zero.rb +9 -0
  48. data/lib/vagabond/errors.rb +23 -0
  49. data/lib/vagabond/helpers.rb +41 -14
  50. data/lib/vagabond/internal_configuration.rb +120 -27
  51. data/lib/vagabond/kitchen.rb +143 -63
  52. data/lib/vagabond/knife.rb +8 -5
  53. data/lib/vagabond/layout.rb +16 -0
  54. data/lib/vagabond/monkey/kitchen_config.rb +23 -0
  55. data/lib/vagabond/server.rb +79 -63
  56. data/lib/vagabond/spec.rb +345 -0
  57. data/lib/vagabond/uploader.rb +30 -0
  58. data/lib/vagabond/uploader/berkshelf.rb +53 -0
  59. data/lib/vagabond/uploader/knife.rb +24 -0
  60. data/lib/vagabond/uploader/librarian.rb +31 -0
  61. data/lib/vagabond/vagabond.rb +30 -11
  62. data/lib/vagabond/vagabondfile.rb +40 -5
  63. data/lib/vagabond/version.rb +1 -1
  64. data/vagabond.gemspec +5 -2
  65. metadata +75 -15
  66. data/lib/vagabond/cookbooks/lxc/resources/#container.rb# +0 -28
  67. data/lib/vagabond/cookbooks/lxc/test/kitchen/Kitchenfile +0 -7
  68. data/lib/vagabond/cookbooks/lxc/test/kitchen/cookbooks/lxc_test/metadata.rb +0 -2
  69. data/lib/vagabond/cookbooks/lxc/test/kitchen/cookbooks/lxc_test/recipes/centos_lxc.rb +0 -0
  70. data/lib/vagabond/cookbooks/lxc/test/kitchen/cookbooks/lxc_test/recipes/chef-bootstrap.rb +0 -0
  71. data/lib/vagabond/cookbooks/lxc/test/kitchen/cookbooks/lxc_test/recipes/lxc_files.rb +0 -0
  72. data/lib/vagabond/cookbooks/lxc/test/kitchen/cookbooks/lxc_test/recipes/lxc_templates.rb +0 -0
  73. data/lib/vagabond/cookbooks/lxc/test/kitchen/cookbooks/lxc_test/recipes/ubuntu_lxc.rb +0 -0
@@ -1,3 +1,21 @@
1
+ ## v0.2.2
2
+ * Migration to elecksee gem for LXC management
3
+ * Addition of chef-zero support for local chef server
4
+ * Updated testing for standalone cookbook testing
5
+ * Updated support for dependency resolvers (librarian and berkshelf)
6
+ * Addition of `spec` support
7
+ * Better isolation around sudo usage
8
+ * Cleaned `internal_configuration` and added automatic reloading on detected changes
9
+ * Added `cluster` support to Vagabondfile for easily building multiple nodes
10
+ * New `init` command for base setup
11
+ * Lots and lots of bug fixes and feature enhancements
12
+ * Output relevant information on bad command
13
+ * Huge thanks to all those that helped test, debug, and add features especially:
14
+ * Jesse Nelson - https://github.com/spheromak
15
+ * Bryan Berry - https://github.com/bryanwb
16
+ * Sean Escriva - https://github.com/webframp
17
+
18
+
1
19
  ## v0.2.0
2
20
  * Migrate to thor
3
21
  * Clean up option usage
data/README.md CHANGED
@@ -15,8 +15,8 @@ instead of full blown VMs which means things are faster. Lots
15
15
  faster.
16
16
 
17
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).
18
+ at Chef. After the initial development has slowed, the Chef specifics
19
+ will be pulled into a plugin.
20
20
 
21
21
  ## Installation
22
22
 
@@ -29,7 +29,7 @@ $ gem install vagabond
29
29
  ## How does it work
30
30
 
31
31
  Currently, this is built to run within a classic Chef repository.
32
- It requires a `Vagabond` file, that simply outputs a Hash. The file
32
+ It requires a `Vagabondfile` file, that simply outputs a Hash. The file
33
33
  is Ruby though, so you can do lots of crazy stuff to build the
34
34
  Hash you return. Heres a simple example:
35
35
 
@@ -58,6 +58,23 @@ Now, to create a node, simply run:
58
58
  $ vagabond up db
59
59
  ```
60
60
 
61
+ This command will bootstrap the installation of LXC utilities and base
62
+ containers prior to starting up a linux container. It does this by
63
+ running the vagabond chef recipe embedded in this gem at
64
+ `lib/vagabond/cookbooks/vagabond/recipes/default.rb`.
65
+
66
+ To only prepare your system for LXC fun and generate a simple vagabond
67
+ file, do the following:
68
+
69
+ ```
70
+ $ vagabond init
71
+ ```
72
+
73
+ This command runs the chef recipe and generates a basic Vagabondfile and
74
+ creates the base containers specified in that file. Those base
75
+ containers are Ubuntu 12.04 and CentOS 6.3. This creation process will
76
+ take a while so now is a good time to get a fresh pot of coffee.
77
+
61
78
  Pretty simple, right?
62
79
 
63
80
  ### Templates available
@@ -131,6 +148,103 @@ chef server. Looking for example? See the `USAGE` file!
131
148
 
132
149
  Double awesome
133
150
 
151
+ ## Infrastructure testing
152
+
153
+ Cookbook tests are great and they help keep cookbooks stable and prevent
154
+ regressions. But what about tests for integrating cookbooks into an existing
155
+ infrastructure? Or upgrading an existing cookbook? The tests bundled with
156
+ the cookbook can happily pass with no indication of how it may affect other
157
+ resources within the infrastructure. So lets fix this.
158
+
159
+ Currently infrastructure tests are built using serverspec[1]. Test kitchen support
160
+ for infrastructure testing is in the works, but is still a moving target. So
161
+ lets look at how we can set this up. First, initialize the specs:
162
+
163
+ ```
164
+ $ vagabond spec init
165
+ ```
166
+
167
+ Next, we need to define the layout of the infrastructure. This is done by
168
+ populating the `Layout` file in the `spec` directory. Just like everything
169
+ else, this is just a ruby file that is expected to spit out a Hash. An
170
+ example file would look like this:
171
+
172
+ ```ruby
173
+ # spec/Layout
174
+ {
175
+ :defaults => {
176
+ :platform => 'ubuntu_1204',
177
+ :environment => 'testing',
178
+ :union => 'aufs'
179
+ },
180
+ :definitions => {
181
+ :test_node => {
182
+ :run_list => %w(role[base])
183
+ }
184
+ },
185
+ :clusters => {
186
+ :my_cluster => {
187
+ :overrides => {
188
+ :environment => '_default'
189
+ },
190
+ :nodes => ['test_node'] * 3
191
+ }
192
+ }
193
+ }
194
+ ```
195
+
196
+ ### :defaults
197
+
198
+ These are the default configuration options used for creating the containers
199
+ for testing. These are the same configurations used when creating the nodes
200
+ in the Vagabondfile.
201
+
202
+ ### :definitions
203
+
204
+ These are the definitions of your nodes. Any options here that were defined
205
+ within the `:defaults` section will be overridden.
206
+
207
+ ### :clusters
208
+
209
+ These are the clusters of nodes that describe the infrastructure. The key
210
+ provides the identifier name used from the `spec` command. The `:overrides`
211
+ are cluster specific overrides that are applied to all nodes when created.
212
+ The `:nodes` is an array of `:definitions` keys for nodes to build. The
213
+ keys can be repeated `n` times to provide multiple nodes of a specific type.
214
+
215
+ ### Usage
216
+
217
+ ```
218
+ $ vagabond spec my_cluster
219
+ ```
220
+
221
+ ### Applying specs
222
+
223
+ Specs are applied based on the run list describing the node. After the local
224
+ chef server has been created (if required), all nodes have been created, and
225
+ all nodes provisioned vagabond will run back through all nodes applying the
226
+ applicable specs. Specs are very straight forward and only use SSH connections
227
+ to spec the node. An example spec:
228
+
229
+ ```ruby
230
+ require 'spec_helper'
231
+
232
+ describe 'cron' do
233
+ it{ should be_enabled }
234
+ it{ should be_running }
235
+ end
236
+ ```
237
+
238
+ ### Spec real infrastructure
239
+
240
+ Since specs only require an SSH connection to test nodes, we can run specs
241
+ against actual live infrastructure to see if it is currently in a valid
242
+ state based on existing specs. Awesome!
243
+
244
+ ```
245
+ $ vagabond spec my_cluster --environment production
246
+ ```
247
+
134
248
  ## Important note
135
249
 
136
250
  Until namespaces hit Linux proper, vagabond `sudo`s its way around. You
@@ -163,6 +277,13 @@ get help making things better!
163
277
  * Base updates and pull requests on the `develop` branch
164
278
  * Please don't update core files like `version.rb` or `vagabond.gemspec`
165
279
 
280
+ ## Contributors
281
+
282
+ * [Bryan Berry](https://github.com/bryanwb)
283
+ * [Jesse Nelson](https://github.com/spheromak)
284
+ * [Sean Escriva](https://github.com/webframp)
285
+ * [Xabier de Zuazo](https://github.com/zuazo)
286
+
166
287
  ## Infos
167
288
 
168
- * Repository: https://github.com/chrisroberts/vagabond
289
+ * Repository: https://github.com/chrisroberts/vagabond
@@ -4,25 +4,52 @@ Signal.trap('INT'){ exit 255 }
4
4
 
5
5
  require 'rubygems'
6
6
  require 'vagabond'
7
+ require 'vagabond/errors'
7
8
 
8
9
  if(ARGV.include?('--version') || ARGV.include?('-v'))
9
10
  require 'vagabond/vagabond'
10
11
  Vagabond::Vagabond.new.send(:version)
12
+ elsif(%w(help --help -h).include?(ARGV.first))
13
+ puts "-- Vagabond --\n"
14
+ require 'vagabond/vagabond'
15
+ Object.send(:remove_const, :ARGV)
16
+ ARGV = %w(help)
17
+ Vagabond::Vagabond.start
18
+ %w(server knife kitchen spec).each do |key|
19
+ puts "-- #{key.capitalize} --\n"
20
+ require "vagabond/#{key}"
21
+ Object.send(:remove_const, :ARGV)
22
+ ARGV = %w(help)
23
+ Vagabond.const_get(key.capitalize).start
24
+ end
11
25
  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
26
+ begin
27
+ case arg = ARGV.shift
28
+ when 'server'
29
+ require 'vagabond/server'
30
+ Vagabond::Server
31
+ when 'knife'
32
+ require 'vagabond/knife'
33
+ ARGV.unshift(arg) unless ARGV.empty?
34
+ Vagabond::Knife
35
+ when 'test', 'kitchen'
36
+ require 'vagabond/kitchen'
37
+ Vagabond::Kitchen
38
+ when 'spec'
39
+ require 'vagabond/spec'
40
+ Vagabond::Spec
41
+ else
42
+ require 'vagabond/vagabond'
43
+ ARGV.unshift(arg)
44
+ Vagabond::Vagabond
45
+ end.start
46
+ rescue Vagabond::VagabondError => e
47
+ exit e.exit_code
48
+ rescue Exception => e
49
+ $stderr.puts "Vagabond exiting. Reason: #{e}"
50
+ if(ENV['VAGABOND_EXIT_DEBUG'])
51
+ puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
52
+ end
53
+ exit -1
54
+ end
28
55
  end
@@ -0,0 +1,66 @@
1
+ module Vagabond
2
+ module Actions
3
+ module Cluster
4
+ class << self
5
+ def included(klass)
6
+ klass.class_eval do
7
+ class << self
8
+ def _cluster_options
9
+ [[
10
+ :auto_provision, :type => :boolean,
11
+ :desc => 'Automatically provision nodes', :default => true
12
+ ],
13
+ [
14
+ :delay, :type => :numeric, :default => 0,
15
+ :desc => 'Add delay between provisions (helpful for indexing)'
16
+ ],
17
+ [
18
+ :parallel, :type => :boolean, :default => false,
19
+ :desc => 'Build nodes in parallel'
20
+ ]
21
+ ]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def _cluster
29
+ clr = vagabondfile[:clusters][name] if vagabondfile[:clusters]
30
+ if(clr)
31
+ ui.info "#{ui.color('Vagabond:', :bold)} Building cluster - #{ui.color(name, :green)}"
32
+ if(vagabondfile[:local_chef_server] && vagabondfile[:local_chef_server][:enabled])
33
+ require 'vagabond/server'
34
+ srv = ::Vagabond::Server.new
35
+ srv.send(:setup, 'up')
36
+ srv.execute
37
+ # Reload so we get proper values
38
+ load_configurations
39
+ end
40
+ cluster_instances = clr.map do |n|
41
+ ui.info "Building #{n} for cluster!"
42
+ v_inst = Vagabond.new
43
+ v_inst.options = options.dup
44
+ v_inst.send(:setup, 'up', n, :ui => ui)
45
+ v_inst.execute
46
+ if(options[:delay].to_i > 0 && n != clr.last)
47
+ ui.warn "Delay requested between node processing. Sleeping for #{options[:delay].to_i} seconds."
48
+ sleep(options[:delay].to_i)
49
+ end
50
+ v_inst
51
+ end
52
+ if(options[:parallel])
53
+ ui.info "Waiting for parallel completes!"
54
+ cluster_instances.map do |inst|
55
+ inst.wait_for_completion
56
+ end
57
+ end
58
+ ui.info " -> #{ui.color("Built cluster #{name}", :green)}"
59
+ else
60
+ ui.error "Cluster name provided does not exist: #{name}"
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,5 @@
1
+ require 'elecksee/ephemeral'
2
+
1
3
  module Vagabond
2
4
  module Actions
3
5
  module Create
@@ -21,14 +23,17 @@ module Vagabond
21
23
  tmpl = internal_config[:template_mappings][tmpl]
22
24
  elsif(!BASE_TEMPLATES.include?(tmpl))
23
25
  ui.fatal "Template requested for node does not exist: #{tmpl}"
24
- exit EXIT_CODES[:invalid_template]
26
+ raise VagabondError::InvalidTemplate.new(tmpl)
27
+ end
28
+ unless(config[:device])
29
+ config[:directory] = true
25
30
  end
26
- bind_path = File.expand_path(File.dirname(vagabondfile.path))
27
- com = "#{sudo}lxc-start-ephemeral -d -b #{bind_path} -o #{tmpl}"
28
- debug(com)
29
- c = Mixlib::ShellOut.new("#{com} && sleep 3", :live_stream => options[:debug])
30
- c.run_command
31
- e_name = c.stdout.split("\n").last.split(' ').last.strip
31
+ config[:daemon] = true
32
+ config[:original] = tmpl
33
+ config[:bind] = File.expand_path(File.dirname(vagabondfile.store_path))
34
+ ephemeral = Lxc::Ephemeral.new(config)
35
+ ephemeral.start!(:fork)
36
+ e_name = ephemeral.name
32
37
  @internal_config[mappings_key][name] = e_name
33
38
  @internal_config.save
34
39
  @lxc = Lxc.new(e_name)
@@ -2,27 +2,76 @@ module Vagabond
2
2
  module Actions
3
3
  module Destroy
4
4
 
5
+ class << self
6
+ def included(klass)
7
+ klass.class_eval do
8
+ class << self
9
+ def _cluster_options
10
+ [
11
+ [
12
+ :cluster, :type => :boolean,
13
+ :desc => 'Destroy cluster of nodes with provided name', :default => false
14
+ ]
15
+ ]
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+
5
23
  def _destroy
6
24
  name_required!
7
- if(lxc.exists?)
8
- ui.info "#{ui.color('Vagabond:', :bold)} Destroying node: #{ui.color(name, :red)}"
9
- do_destroy
10
- ui.info ui.color(' -> DESTROYED', :red)
25
+ if(options[:cluster])
26
+ nodes = vagabondfile[:clusters][name] if vagabondfile[:clusters]
27
+ if(nodes)
28
+ ui.info "#{ui.color('Vagabond:', :bold)} Destroying cluster - #{ui.color(name, :red)}"
29
+ else
30
+ ui.error "Cluster name provided does not exist: #{name}"
31
+ nodes = []
32
+ end
11
33
  else
12
- ui.error "Node not created: #{name}"
34
+ nodes = [name, @leftover_args].flatten.compact
35
+ end
36
+ nodes.each do |n|
37
+ next unless n.is_a?(String)
38
+ @name = n
39
+ load_configurations
40
+ if(lxc.exists?)
41
+ ui.info "#{ui.color('Vagabond:', :bold)} Destroying node: #{ui.color(name, :red)}"
42
+ do_destroy
43
+ ui.info ui.color(' -> DESTROYED', :red)
44
+ else
45
+ ui.error "Node not created: #{name}"
46
+ end
13
47
  end
14
48
  end
15
49
 
16
50
  private
17
51
 
18
52
  def do_destroy
19
- lxc.stop if lxc.running?
20
- com = "#{options[:sudo]}lxc-destroy -n #{lxc.name}"
21
- debug(com)
22
- cmd = Mixlib::ShellOut.new(com, :live_stream => options[:debug])
23
- cmd.run_command
24
- cmd.error!
25
- if(cmd.stderr.include?('skipping'))
53
+ lxc.shutdown if lxc.running?
54
+ ui.info 'Waiting for graceful shutdown and cleanup...'
55
+ 5.times do
56
+ break unless lxc.exists?
57
+ sleep(1)
58
+ end
59
+ if(lxc.exists?)
60
+ com = "#{options[:sudo]}lxc-destroy -n #{lxc.name}"
61
+ debug(com)
62
+ cmd = Mixlib::ShellOut.new(com, :live_stream => options[:debug])
63
+ cmd.run_command
64
+ force_umount_if_required!
65
+ end
66
+ internal_config[mappings_key].delete(name)
67
+ internal_config.save
68
+ end
69
+
70
+ def force_umount_if_required!
71
+ mount = %x{mount}.split("\n").find_all do |line|
72
+ line.include?(lxc.name)
73
+ end
74
+ unless(mount.empty?)
26
75
  ui.info ui.color(' -> Failed to unmount some resources. Forcing manually.', :yellow)
27
76
  %w(rootfs ephemeralbind).each do |mnt|
28
77
  com = "#{options[:sudo]}umount /var/lib/lxc/#{lxc.name}/#{mnt}"
@@ -33,10 +82,15 @@ module Vagabond
33
82
  debug(com)
34
83
  cmd = Mixlib::ShellOut.new(com, :live_stream => options[:debug])
35
84
  cmd.run_command
36
- cmd.error!
37
- internal_config[mappings_key].delete(name)
38
85
  end
39
- internal_config.save
86
+ # check for tmpfs and umount too
87
+ tmp = mount.detect{|x|x.include?('rootfs')}.scan(%r{upperdir=[^,]+}).first.to_s.split('=').last
88
+ if(tmp)
89
+ com = "#{options[:sudo]}umount #{tmp}"
90
+ debug(com)
91
+ cmd = Mixlib::ShellOut.new(com, :live_stream => options[:debug])
92
+ cmd.run_command
93
+ end
40
94
  end
41
95
  end
42
96
  end