vagrant-butcher 0.0.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  .idea/
19
+ .vagrant/
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ script: bundle exec rspec spec/
data/Gemfile CHANGED
@@ -2,3 +2,10 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in vagrant-butcher.gemspec
4
4
  gemspec
5
+
6
+ group :development do
7
+ # We depend on Vagrant for development, but we don't add it as a
8
+ # gem dependency because we expect to be installed within the
9
+ # Vagrant environment itself using `vagrant plugin`.
10
+ gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git"
11
+ end
data/README.md CHANGED
@@ -1,45 +1,61 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/vagrant-butcher.png)](http://badge.fury.io/rb/vagrant-butcher)
2
+ [![Build Status](https://travis-ci.org/cassianoleal/vagrant-butcher.png)](https://travis-ci.org/cassianoleal/vagrant-butcher)
3
+ [![Code Climate](https://codeclimate.com/github/cassianoleal/vagrant-butcher.png)](https://codeclimate.com/github/cassianoleal/vagrant-butcher)
2
4
 
3
5
  # Vagrant::Butcher
4
6
 
5
- If you're using Vagrant with the Chef-Client provisioner, e.g. for creating cookbooks, it creates a client and a node on the Chef server. Once you destroy the VM, both the client and node will be kept on the server, which may cause problems if you fire up the same VM again.
7
+ If you're using Vagrant with the Chef-Client provisioner it creates a client and a node on the Chef server when the VM spins up.
6
8
 
7
- This gem attempts to correct that.
9
+ This plugin will automatically get rid of that cruft for you when you destroy the VM.
8
10
 
9
- ## Installation
11
+ ## Changelog
10
12
 
11
- Installation will depend on whether you're using bundler or not.
13
+ ### 1.0.0
12
14
 
13
- ### If you're using Bundler
15
+ * Support for Vagrant 1.x (it's been tested on 1.4, but should work on previous minor releases) -- if you're using a pre-1.0 Vagrant release, stick to vagrant-butcher 0.0.3.
16
+ * [Configuration](#usage) change.
17
+ * [Installation](#install) via `vagrant plugin` only.
18
+ * Provider-independent. _[Read more](#caveats)_
14
19
 
15
- Add this line to your cookbook's Gemfile:
20
+ ### 0.0.3
16
21
 
17
- gem 'vagrant-butcher'
22
+ * Uses chef.node_name if set. Otherwise, fall back to vm.host_name (as before), or vm.box. -- _Kudos to [pikesley](https://github.com/pikesley)_.
18
23
 
19
- And then execute:
24
+ ## <a id="install"></a>Installation
20
25
 
21
- $ bundle
26
+ Starting with version 1.0.0, installation is made via Vagrant plugins only:
22
27
 
23
- ### If you're not using bundler
28
+ $ vagrant plugin install vagrant-butcher
24
29
 
25
- You have to install this plugin via Vagrant:
26
-
27
- $ vagrant gem install 'vagrant-butcher'
28
-
29
- Explanation for that is found on the [Vagrant Plugins Documentation](http://vagrantup.com/v1/docs/extending/types.html)
30
-
31
- ## Usage
30
+ ## <a id='usage'></a>Usage
32
31
 
33
32
  The plugin is loaded automatically once installed.
34
33
 
35
- By default, the gem looks for the Chef server settings on `$HOME/.chef/knife.rb`. This can be overrdidden by setting:
34
+ By default, the gem looks for the Chef server settings on `$HOME/.chef/knife.rb`. This can be overridden by setting:
36
35
 
37
- Vagrant::Config.run do |config|
38
- config.vm.provision :chef_solo do |chef|
39
- chef.knife_config = '/path/to/knife.rb'
36
+ Vagrant.configure("2") do |config|
37
+ config.butcher.knife_config_file = '/path/to/knife.rb'
38
+ config.vm.provision :chef_client do |chef|
39
+ # Chef Client provisioner configuration
40
40
  end
41
41
  end
42
42
 
43
+ _Note that beginning with 1.0, the configuration is done outside of the `chef_client` provisioner._
44
+
45
+ This is the output of the plugin when it runs successfully:
46
+
47
+ $ vagrant destroy -f
48
+ [Butcher] knife.rb location set to '/path/to/knife.rb'
49
+ [Butcher] Chef node 'node_name' successfully butchered from the server...
50
+ [Butcher] Chef client 'node_name' successfully butchered from the server...
51
+ [default] Forcing shutdown of VM...
52
+ [default] Destroying VM and associated drives...
53
+
54
+ ## <a id='caveats'></a>Caveats
55
+
56
+ * Version 1.0 has only been tested with Vagrant 1.1+. If you're using an older version, it's probably best to stick to 0.0.3
57
+ * So far this has only been tested and confirmed to run with the VirtualBox and Rackspace provisioners. It should work with others, but if you run into issues please file a bug.
58
+
43
59
  ## Contributing
44
60
 
45
61
  1. Fork it
@@ -1,4 +1,14 @@
1
- require "vagrant/provisioners/chef"
2
- require "vagrant-butcher/version"
3
- require "vagrant-butcher/cleanup"
4
- require "chef"
1
+ require 'vagrant'
2
+ require 'vagrant-butcher/version'
3
+ require 'vagrant-butcher/errors'
4
+
5
+ module Vagrant
6
+ module Butcher
7
+ autoload :Action, 'vagrant-butcher/action'
8
+ autoload :Config, 'vagrant-butcher/config'
9
+ autoload :Env, 'vagrant-butcher/env'
10
+ autoload :EnvHelpers, 'vagrant-butcher/env_helpers'
11
+ end
12
+ end
13
+
14
+ require 'vagrant-butcher/plugin'
@@ -0,0 +1,20 @@
1
+ module Vagrant
2
+ module Butcher
3
+ module Action
4
+ autoload :Cleanup, 'vagrant-butcher/action/cleanup'
5
+
6
+ def self.cleanup
7
+ ::Vagrant::Action::Builder.new.tap do |b|
8
+ b.use setup
9
+ b.use Vagrant::Butcher::Action::Cleanup
10
+ end
11
+ end
12
+
13
+ def self.setup
14
+ @setup ||= ::Vagrant::Action::Builder.new.tap do |b|
15
+ b.use ::Vagrant::Action::Builtin::EnvSet, butcher: Vagrant::Butcher::Env.new
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ require 'chef/config'
2
+ require 'chef/rest'
3
+ require 'chef/api_client'
4
+
5
+ module Vagrant
6
+ module Butcher
7
+ module Action
8
+ class Cleanup
9
+ include ::Vagrant::Butcher::EnvHelpers
10
+
11
+ def initialize(app, env)
12
+ @app = app
13
+ @env = env
14
+ end
15
+
16
+ def victim
17
+ @victim ||= chef_provisioner(@env).node_name || vm_config(@env).hostname || vm_config(@env).box
18
+ end
19
+
20
+ def delete_resource(resource)
21
+ begin
22
+ chef_api(@env).delete_rest("#{resource}s/#{victim}")
23
+ @env[:butcher].ui.success "Chef #{resource} '#{victim}' successfully butchered from the server..."
24
+ rescue Exception => e
25
+ @env[:butcher].ui.warn "Could not remove #{resource} #{victim}: #{e.message}"
26
+ end
27
+ end
28
+
29
+ def call(env)
30
+ if chef_client?(env)
31
+ begin
32
+ ::Chef::Config.from_file knife_config_file(env)
33
+ rescue Errno::ENOENT => e
34
+ raise ::Vagrant::Butcher::VagrantWrapperError.new(e)
35
+ end
36
+
37
+ %w(node client).each { |resource| delete_resource(resource) }
38
+ end
39
+
40
+ @app.call(env)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,27 @@
1
+ module Vagrant
2
+ module Butcher
3
+ class Config < ::Vagrant.plugin('2', :config)
4
+ attr_accessor :knife_config_file
5
+
6
+ def initialize
7
+ super
8
+ @knife_config_file = UNSET_VALUE
9
+ end
10
+
11
+ def knife_config_file=(value)
12
+ @knife_config_file = File.expand_path(value)
13
+ end
14
+
15
+ def validate(machine)
16
+ errors = []
17
+ errors << "Knife configuration not found at #{@knife_config_file}." if !File.exists?(@knife_config_file)
18
+
19
+ { "butcher configuration" => errors }
20
+ end
21
+
22
+ def finalize!
23
+ @knife_config_file = File.expand_path "#{ENV['HOME']}/.chef/knife.rb" if @knife_config_file == UNSET_VALUE
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Vagrant
2
+ module Butcher
3
+ class Env
4
+ attr_accessor :ui
5
+
6
+ def initialize
7
+ @ui = ::Vagrant::UI::Colored.new('Butcher')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ module Vagrant
2
+ module Butcher
3
+ module EnvHelpers
4
+ def vm_config(env)
5
+ @vm_config ||= env[:machine].config.vm
6
+ end
7
+
8
+ def chef_api(env)
9
+ ::Chef::REST.new(chef_provisioner(env).chef_server_url)
10
+ end
11
+
12
+ def chef_provisioner(env)
13
+ @chef_provisioner ||= vm_config(env).provisioners.find do |p|
14
+ p.config.is_a? VagrantPlugins::Chef::Config::ChefClient
15
+ end.config
16
+ end
17
+
18
+ def chef_client?(env)
19
+ vm_config(env).provisioners.select { |p| p.name == :chef_client }.any?
20
+ end
21
+
22
+ def knife_config_file(env)
23
+ # Make sure that the default is set
24
+ env[:machine].config.butcher.finalize!
25
+
26
+ env[:butcher].ui.info "knife.rb location set to '#{env[:machine].config.butcher.knife_config_file}'"
27
+ env[:machine].config.butcher.knife_config_file
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ require 'vagrant/errors'
2
+
3
+ module Vagrant
4
+ module Butcher
5
+ # This is a copy/adaptation of the VagrantWrapperError class of berkshelf-vagrant
6
+ # The original can be found on https://github.com/RiotGames/berkshelf-vagrant
7
+
8
+ # @author Jamie Winsor <reset@riotgames.com>
9
+ #
10
+ # A wrapper for a BerkshelfError for Vagrant. All Berkshelf exceptions should be
11
+ # wrapped in this proxy object so they are properly handled when Vagrant encounters
12
+ # an exception.
13
+ #
14
+ # @example wrapping an error encountered within the Vagrant plugin
15
+ # rescue BerkshelfError => e
16
+ # VagrantWrapperError.new(e)
17
+ # end
18
+ class VagrantWrapperError < ::Vagrant::Errors::VagrantError
19
+ # @param [BerkshelfError]
20
+ attr_reader :original
21
+
22
+ # @param [BerkshelfError] original
23
+ def initialize(original)
24
+ @original = original
25
+ end
26
+
27
+ def to_s
28
+ "#{original.class}: #{original.to_s}"
29
+ end
30
+
31
+ private
32
+
33
+ def method_missing(fun, *args, &block)
34
+ original.send(fun, *args, &block)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ module Vagrant
2
+ module Butcher
3
+ class Plugin < Vagrant.plugin('2')
4
+ name "vagrant-butcher"
5
+ description <<-DESC
6
+ Delete node and client from the Chef server when destroying the VM.
7
+ DESC
8
+
9
+ action_hook(:vagrant_butcher_cleanup, :machine_action_destroy) do |hook|
10
+ hook.after(::Vagrant::Action::Builtin::ConfigValidate, Vagrant::Butcher::Action.cleanup)
11
+ end
12
+
13
+ config("butcher") do
14
+ Config
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  module Vagrant
2
2
  module Butcher
3
- VERSION = "0.0.3"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'vagrant-butcher'
@@ -0,0 +1,42 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Vagrant::Butcher::Config do
4
+ subject { described_class.new }
5
+
6
+ it "has the option to set knife.rb path" do
7
+ subject.should respond_to(:knife_config_file)
8
+ end
9
+
10
+ it "sets knife.rb default path" do
11
+ subject.finalize!.should eql(File.expand_path("#{ENV['HOME']}/.chef/knife.rb"))
12
+ end
13
+
14
+ describe "#validate" do
15
+ let(:env) { double('env') }
16
+ let(:config) { double('config', butcher: subject) }
17
+ let(:machine) { double('machine', config: config, env: env) }
18
+ let(:result) { subject.validate(machine) }
19
+
20
+ context "when validations pass" do
21
+ before(:each) do
22
+ File.should_receive(:exists?).with(subject.knife_config_file).and_return(true)
23
+ end
24
+
25
+ it "contains an empty Array for the 'butcher configuration' key" do
26
+ result["butcher configuration"].should be_a(Array)
27
+ result["butcher configuration"].should be_empty
28
+ end
29
+ end
30
+
31
+ context "when validations fail" do
32
+ before(:each) do
33
+ File.should_receive(:exists?).with(subject.knife_config_file).and_return(false)
34
+ end
35
+
36
+ it "contains an empty Array for the 'butcher configuration' key" do
37
+ result["butcher configuration"].should be_a(Array)
38
+ result["butcher configuration"].should_not be_empty
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Vagrant::Butcher::Plugin do
4
+ end
@@ -17,6 +17,9 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- gem.add_dependency "vagrant", "~> 1.0"
21
- gem.add_dependency "chef"
20
+ gem.add_dependency "chef", ">= 11.2.0"
21
+
22
+ gem.add_development_dependency "rspec"
23
+ gem.add_development_dependency "pry-debugger"
24
+ gem.add_development_dependency "bundler", "~> 1.3"
22
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-butcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,33 +9,33 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-21 00:00:00.000000000 Z
12
+ date: 2013-04-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: vagrant
15
+ name: chef
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ~>
19
+ - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: '1.0'
21
+ version: 11.2.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ~>
27
+ - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: '1.0'
29
+ version: 11.2.0
30
30
  - !ruby/object:Gem::Dependency
31
- name: chef
31
+ name: rspec
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
35
  - - ! '>='
36
36
  - !ruby/object:Gem::Version
37
37
  version: '0'
38
- type: :runtime
38
+ type: :development
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  none: false
@@ -43,6 +43,38 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: pry-debugger
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
46
78
  description: Delete Chef client and node when destroying Vagrant VM
47
79
  email:
48
80
  - cassianoleal@gmail.com
@@ -51,14 +83,23 @@ extensions: []
51
83
  extra_rdoc_files: []
52
84
  files:
53
85
  - .gitignore
86
+ - .travis.yml
54
87
  - Gemfile
55
88
  - LICENSE.txt
56
89
  - README.md
57
90
  - Rakefile
58
91
  - lib/vagrant-butcher.rb
59
- - lib/vagrant-butcher/cleanup.rb
92
+ - lib/vagrant-butcher/action.rb
93
+ - lib/vagrant-butcher/action/cleanup.rb
94
+ - lib/vagrant-butcher/config.rb
95
+ - lib/vagrant-butcher/env.rb
96
+ - lib/vagrant-butcher/env_helpers.rb
97
+ - lib/vagrant-butcher/errors.rb
98
+ - lib/vagrant-butcher/plugin.rb
60
99
  - lib/vagrant-butcher/version.rb
61
- - lib/vagrant_init.rb
100
+ - spec/spec_helper.rb
101
+ - spec/unit/vagrant_butcher/config_spec.rb
102
+ - spec/unit/vagrant_butcher_spec.rb
62
103
  - vagrant-butcher.gemspec
63
104
  homepage: https://github.com/cassianoleal/vagrant-butcher
64
105
  licenses: []
@@ -86,4 +127,7 @@ specification_version: 3
86
127
  summary: When a Vagrant VM that was spun up using Chef-Client is destroyed, it leaves
87
128
  behind a client and a node on the Chef server. What butcher does is to clean up
88
129
  those during the destroy operation.
89
- test_files: []
130
+ test_files:
131
+ - spec/spec_helper.rb
132
+ - spec/unit/vagrant_butcher/config_spec.rb
133
+ - spec/unit/vagrant_butcher_spec.rb
@@ -1,39 +0,0 @@
1
- module Vagrant
2
- module Provisioners
3
- class ChefClient < Chef
4
- class Config < Chef::Config
5
- attr_accessor :knife_config
6
- def knife_config; @knife_config || "#{ENV['HOME']}/.chef/knife.rb"; end
7
- end
8
-
9
- def cleanup_chef_server(node_name)
10
- chef_api = ::Chef::REST.new(::Chef::Config[:chef_server_url])
11
- %w(node client).each do |resource|
12
- env[:ui].info "Removing Chef #{resource} \"#{node_name}\"..."
13
- begin
14
- chef_api.delete_rest("#{resource}s/#{node_name}")
15
- rescue Exception => e
16
- env[:ui].warn "Could not remove #{resource} #{node_name}: #{e.message}"
17
- end
18
- end
19
- end
20
-
21
- def cleanup
22
- ::Chef::Config.from_file(config.knife_config)
23
- conf = env[:vm].config.vm
24
-
25
- # If a node_name is given to the chef-client provisioner, use it
26
- chef_provisioner = conf.provisioners.find { |p| p.provisioner.name == "Vagrant::Provisioners::ChefClient" }
27
- victim = chef_provisioner.config.node_name
28
-
29
- # If no node_name, fall back to host_name or box
30
- if not victim then
31
- env[:ui].info "No chef.node_name set, falling back to vm.host_name or vm.box."
32
- victim ||= conf.host_name || conf.box
33
- end
34
-
35
- cleanup_chef_server(victim)
36
- end
37
- end
38
- end
39
- end
@@ -1,3 +0,0 @@
1
- # This file is automatically loaded by Vagrant. We use this to kick-start
2
- # our plugin.
3
- require 'vagrant-butcher'