knife-vagrant2 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ee941f5425873c296f1813ea350eee29f1c1320
4
+ data.tar.gz: c7254238b04f65993b8fb7de035b0d581ff62c5d
5
+ SHA512:
6
+ metadata.gz: 21875e807264aaeaaeebd2577c3ef99b2ff921b865be5e341dc988f2f7488d37a2bfb505b9d48807a6f6dd0baca3b0e79cf7b222a2a1bc7ccb966de9f8838d99
7
+ data.tar.gz: 4508b2332c2c063871df1b3ddd376b61883ef495f7ca311868d0680ce5ac233002513f2f3e8e06e31280f82fd4a331a6402e4041f1ba97b5ea783bc84c517c8c
@@ -0,0 +1,9 @@
1
+ # OS files
2
+ .DS_Store
3
+ Icon?
4
+ .Spotlight-V100
5
+ .Trashes
6
+
7
+ *.gem
8
+ Gemfile.lock
9
+
@@ -0,0 +1,51 @@
1
+ # 0.0.5 (2014-12-11)
2
+
3
+ ## Changes and new features
4
+
5
+ * Added --vmx-customize option to pass arbitrary parameters to VMware providers
6
+ * Added --vagrant-config option to extend Vagrantfile with custom options
7
+
8
+
9
+ ## Fixes
10
+
11
+ * Fix missing `ostruct` dependency with newer Chef versions
12
+ * Fix `--distro` default value change in newer Chef versions
13
+
14
+
15
+ # 0.0.4 (2014-03-14)
16
+
17
+ ## Changes and new features
18
+
19
+ * Added --provider option to use vagrant providers other than virtualbox
20
+
21
+
22
+ # 0.0.3 (2014-01-28)
23
+
24
+ ## Fixes
25
+
26
+ * Fixed error when not using --vb-customize option
27
+ * Removed unneeded dependency on knife-ec2
28
+
29
+
30
+ # 0.0.2 (2013-12-17)
31
+
32
+ ## Changes and new features
33
+
34
+ * BREAKING: --share-folders option now takes HOST_PATH::GUEST_PATH instead of NAME::GUEST_PATH::HOST_PATH
35
+ * Added --port-forward option to map host ports to VMs
36
+ * Added --vb-customize option to pass arbitrary VirtualBox options to Vagrant
37
+
38
+
39
+ # 0.0.1 (2013-12-11)
40
+
41
+ ## Changes and new features
42
+
43
+ * Initial release
44
+
45
+
46
+ # Thanks to our contributors
47
+
48
+ * [Jāzeps Baško](https://github.com/jbasko)
49
+ * [Robert J. Berger](https://github.com/rberger)
50
+ * [xsunsmile](https://github.com/xsunsmile)
51
+ * [Mevan Samaratunga](https://github.com/mevansam)
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2013-2014 Markus Kern
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,48 @@
1
+ knife-vagrant2
2
+ ==============
3
+
4
+ This plugin gives knife the ability to create, bootstrap, and manage Vagrant instances.
5
+
6
+ The plugin is a rewrite of the original [knife-vagrant](https://github.com/garrettux/knife-vagrant) but more closely resembles knife-ec2 behaviour. Specifically it does _not_ use Vagrant's built in Chef provisioner and instead relies on knife to bootstrap the VM. It will work nicely with [knife-solo](https://github.com/matschaffer/knife-solo) and doesn't require a Chef server.
7
+
8
+
9
+ Installation
10
+ ------------
11
+ If you are using bundler, simply add Chef and knife-vagrant2 to your `Gemfile`:
12
+
13
+ ```ruby
14
+ gem 'chef'
15
+ gem 'knife-vagrant2'
16
+ ```
17
+
18
+ If you are not using bundler, you can install the gem manually:
19
+
20
+ $ gem install knife-vagrant2
21
+
22
+
23
+ Usage
24
+ -----
25
+ knife-vagrant2 creates a `/vagrant` subfolder in your project which it uses to manage Vagrant files for all the instances you launch. You should add this folder to your `.gitignore` file so it is never checked into version control.
26
+
27
+ To launch a new VM use the `server create` command:
28
+
29
+ knife vagrant server create --box-url http://files.vagrantup.com/precise32.box -N db -r role[db]
30
+
31
+ This will launch a new VM using the `precise32` box, give the node a Chef name of `db` and then use knife to provision the VM with the `db` role.
32
+
33
+ If a box is already installed into vagrant use `--box` instead of `--box-url` to reference it.
34
+
35
+ By default knife-vagrant2 picks a private IP address from a predefined pool and assigns it to the VM. You can specify the IP pool using `--subnet` or assign a specfic IP with `--private-ip-address`.
36
+
37
+ To map folders between host and VM use `--share-folders NAME::GUEST_PATH::HOST_PATH`.
38
+
39
+ After a VM has been created its Chef name is used to reference it in future commands. Most commands map directly to the Vagrant ones:
40
+
41
+ knife vagrant server suspend SERVER [SERVER]
42
+ knife vagrant server resume SERVER [SERVER]
43
+ knife vagrant server halt SERVER [SERVER]
44
+ knife vagrant server up SERVER [SERVER]
45
+ knife vagrant server delete SERVER [SERVER] (options)
46
+ knife vagrant server list
47
+
48
+ All commands can be applied to one or more VMs.
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'knife-vagrant/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'knife-vagrant2'
7
+ s.version = Knife::Vagrant::VERSION
8
+ s.authors = ['Markus Kern']
9
+ s.email = ['makern@users.noreply.github.com']
10
+ s.homepage = 'https://github.com/makern/knife-vagrant2'
11
+ s.summary = %q{Vagrant support for Chef's knife command}
12
+ s.description = s.summary
13
+ s.license = 'Apache 2.0'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+
17
+ s.add_dependency 'chef', '>= 0.10.10'
18
+ s.add_development_dependency 'rake'
19
+
20
+ s.require_paths = ['lib']
21
+ end
@@ -0,0 +1,165 @@
1
+ require 'chef/knife'
2
+
3
+ class Chef
4
+ class Knife
5
+ module VagrantBase
6
+
7
+ # :nodoc:
8
+ # Would prefer to do this in a rational way, but can't be done b/c of
9
+ # Mixlib::CLI's design :(
10
+ def self.included(includer)
11
+ includer.class_eval do
12
+
13
+ option :vagrant_dir,
14
+ :short => '-D PATH',
15
+ :long => '--vagrant-dir PATH',
16
+ :description => "Path to where Vagrant files are stored. Defaults to cwd/vagrant (#{Dir.pwd}/vagrant)",
17
+ :proc => Proc.new { |key| Chef::Config[:knife][:vagrant_dir] = key },
18
+ :default => File.join(Dir.pwd, '/vagrant')
19
+ end
20
+ end
21
+
22
+ def vagrant_exec(instance, cmd, opts = {})
23
+ fetch_output = opts[:fetch_output] || false
24
+ no_cwd_change = opts[:no_cwd_change] || false
25
+
26
+ unless no_cwd_change
27
+ cwd = Dir.getwd()
28
+ Dir.chdir(File.join(locate_config_value(:vagrant_dir), instance))
29
+ end
30
+
31
+ cmd = "vagrant #{cmd}"
32
+ output = nil
33
+ if defined? Bundler
34
+ # Needed if we are run from inside a bundler environment and vagrant
35
+ # is installed as global gem. If this causes problems for anyone please
36
+ # file a bug report.
37
+ Bundler.with_clean_env do
38
+ output = fetch_output ? %x(#{cmd}) : system(cmd)
39
+ end
40
+ else
41
+ output = fetch_output ? %x(#{cmd}) : system(cmd)
42
+ end
43
+
44
+ unless no_cwd_change
45
+ Dir.chdir(cwd)
46
+ end
47
+ output
48
+ end
49
+
50
+ def vagrant_instance_state(instance)
51
+ output = vagrant_exec(instance, 'status', fetch_output: true)
52
+ state = /Current machine states:.+?default\s+(.+?)\s+\((.+?)\)/m.match(output)
53
+ unless state
54
+ ui.warn("Couldn't parse state of instance #{instance}")
55
+ return ['', '']
56
+ end
57
+ return [state[1], state[2]]
58
+ end
59
+
60
+ def vagrant_instance_list
61
+ # Memoize so we don't have to parse files multiple times
62
+ if @vagrant_instances
63
+ return @vagrant_instances
64
+ end
65
+
66
+ vangrant_dir = locate_config_value(:vagrant_dir)
67
+ @vagrant_instances = []
68
+
69
+ unless File.exist? vangrant_dir
70
+ return @vagrant_instances
71
+ end
72
+
73
+ Dir.foreach(vangrant_dir) { |subdir|
74
+ vagrant_file = File.join(vangrant_dir, subdir, 'Vagrantfile')
75
+ if File.exist? vagrant_file
76
+ instance = { :name => subdir,
77
+ :vagrant_file => vagrant_file
78
+ }
79
+ # Read settings from vagrant file
80
+ content = IO.read(vagrant_file)
81
+ instance[:ip_address] = /config\.vm\.network[^,]+,\s*ip:\s*"([0-9\.]+)"/.match(content) { |m| m[1] }
82
+ unless instance[:ip_address]
83
+ ui.warn("Couldn't find IP address in #{vagrant_file}. Is it malformed?")
84
+ end
85
+
86
+ instance[:box] = /config\.vm\.box[^=]*=\s*"([^"]+)"/.match(content) { |m| m[1] }
87
+ unless instance[:box]
88
+ ui.warn("Couldn't find box in #{vagrant_file}. Is it malformed?")
89
+ end
90
+ @vagrant_instances.push(instance)
91
+ end
92
+ }
93
+ @vagrant_instances
94
+ end
95
+
96
+ def write_insecure_key
97
+ # The private key most vagrant boxes use.
98
+ insecure_key = <<-EOF
99
+ -----BEGIN RSA PRIVATE KEY-----
100
+ MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI
101
+ w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP
102
+ kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2
103
+ hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO
104
+ Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW
105
+ yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd
106
+ ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1
107
+ Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf
108
+ TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK
109
+ iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A
110
+ sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf
111
+ 4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP
112
+ cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk
113
+ EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN
114
+ CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX
115
+ 3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG
116
+ YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj
117
+ 3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+
118
+ dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz
119
+ 6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC
120
+ P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF
121
+ llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ
122
+ kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH
123
+ +vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ
124
+ NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s=
125
+ -----END RSA PRIVATE KEY-----
126
+ EOF
127
+ insecure_key
128
+
129
+ # Write key to vagrant folder if it's not there yet.
130
+ key_file = File.join(locate_config_value(:vagrant_dir), 'insecure_key')
131
+ unless File.exist? key_file
132
+ ui.msg("Creating #{key_file}")
133
+ FileUtils.mkdir_p(locate_config_value(:vagrant_dir))
134
+ File.open(key_file, 'w') { |f| f.write(insecure_key) }
135
+ File.chmod(0600, key_file)
136
+ end
137
+ end
138
+
139
+ def locate_config_value(key)
140
+ key = key.to_sym
141
+ config[key] || Chef::Config[:knife][key]
142
+ end
143
+
144
+ def msg_pair(label, value, color=:cyan)
145
+ if value && !value.to_s.empty?
146
+ puts "#{ui.color(label, color)}: #{value}"
147
+ end
148
+ end
149
+
150
+ def colored_vagrant_state(state)
151
+ case state
152
+ when 'saved','paused','poweroff', 'stuck', 'aborted', 'gurumeditation', 'inaccessible'
153
+ ui.color(state, :red)
154
+ when 'saving'
155
+ ui.color(state, :yellow)
156
+ else
157
+ ui.color(state, :green)
158
+ end
159
+ end
160
+
161
+ end
162
+ end
163
+ end
164
+
165
+
@@ -0,0 +1,15 @@
1
+ require 'chef/knife/vagrant_base'
2
+
3
+ class Chef
4
+ class Knife
5
+
6
+ class VagrantBoxList < Knife
7
+ include Knife::VagrantBase
8
+ banner "knife vagrant box list"
9
+ def run
10
+ vagrant_exec('.', 'box list', no_cwd_change: true)
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,423 @@
1
+ require 'chef/knife/vagrant_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VagrantServerCreate < Knife
6
+
7
+ include Knife::VagrantBase
8
+ deps do
9
+ require 'ostruct'
10
+ require 'ipaddr'
11
+ require 'chef/knife/bootstrap'
12
+ Chef::Knife::Bootstrap.load_deps
13
+ end
14
+
15
+ banner "knife vagrant server create (options)"
16
+
17
+ option :box,
18
+ :short => "-b BOX",
19
+ :long => "--box BOX",
20
+ :description => "The vagrant box to use for the server",
21
+ :proc => Proc.new { |b| Chef::Config[:knife][:box] = b }
22
+
23
+ option :box_url,
24
+ :short => '-U URL',
25
+ :long => '--box-url URL',
26
+ :description => 'URL of pre-packaged vbox template. Can be a local path or an HTTP URL.',
27
+ :proc => Proc.new { |b| Chef::Config[:knife][:box_url] = b }
28
+
29
+ option :memsize,
30
+ :short => '-m MEMORY',
31
+ :long => '--memsize MEMORY',
32
+ :description => 'Amount of RAM to allocate to provisioned VM, in MB. Defaults to 1024',
33
+ :proc => Proc.new { |m| Chef::Config[:knife][:memsize] = m },
34
+ :default => 1024
35
+
36
+ option :share_folders,
37
+ :short => '-F',
38
+ :long => '--share-folders SHARES',
39
+ :description => 'Comma separated list of share folders in the form of HOST_PATH::GUEST_PATH',
40
+ :proc => lambda { |o| o.split(/[\s,]+/) },
41
+ :default => []
42
+
43
+ option :use_cachier,
44
+ :short => '-C',
45
+ :long => '--use-cachier',
46
+ :description => 'Enables VM to use the vagrant-cachier plugin',
47
+ :boolean => true,
48
+ :default => false
49
+
50
+ option :chef_node_name,
51
+ :short => "-N NAME",
52
+ :long => "--node-name NAME",
53
+ :description => "The Chef node name for your new node"
54
+ # :proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
55
+
56
+ option :ssh_user,
57
+ :short => "-x USERNAME",
58
+ :long => "--ssh-user USERNAME",
59
+ :description => "The ssh username",
60
+ :default => "vagrant"
61
+
62
+ option :ssh_password,
63
+ :short => "-P PASSWORD",
64
+ :long => "--ssh-password PASSWORD",
65
+ :description => "The ssh password"
66
+
67
+ option :ssh_port,
68
+ :short => "-p PORT",
69
+ :long => "--ssh-port PORT",
70
+ :description => "The ssh port",
71
+ :default => "22",
72
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
73
+
74
+ option :ssh_gateway,
75
+ :short => "-w GATEWAY",
76
+ :long => "--ssh-gateway GATEWAY",
77
+ :description => "The ssh gateway server",
78
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
79
+
80
+ option :identity_file,
81
+ :short => "-i IDENTITY_FILE",
82
+ :long => "--identity-file IDENTITY_FILE",
83
+ :description => "The SSH identity file used for authentication"
84
+
85
+ option :prerelease,
86
+ :long => "--prerelease",
87
+ :description => "Install the pre-release chef gems"
88
+
89
+ option :bootstrap_version,
90
+ :long => "--bootstrap-version VERSION",
91
+ :description => "The version of Chef to install",
92
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
93
+
94
+ option :distro,
95
+ :short => "-d DISTRO",
96
+ :long => "--distro DISTRO",
97
+ :description => "Bootstrap a distro using a template; default is 'chef-full'",
98
+ :default => 'chef-full',
99
+ :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d }
100
+
101
+ option :template_file,
102
+ :long => "--template-file TEMPLATE",
103
+ :description => "Full path to location of template to use",
104
+ :proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
105
+ :default => false
106
+
107
+ option :run_list,
108
+ :short => "-r RUN_LIST",
109
+ :long => "--run-list RUN_LIST",
110
+ :description => "Comma separated list of roles/recipes to apply",
111
+ :proc => lambda { |o| o.split(/[\s,]+/) }
112
+
113
+ option :secret,
114
+ :short => "-s SECRET",
115
+ :long => "--secret ",
116
+ :description => "The secret key to use to encrypt data bag item values",
117
+ :proc => lambda { |s| Chef::Config[:knife][:secret] = s }
118
+
119
+ option :secret_file,
120
+ :long => "--secret-file SECRET_FILE",
121
+ :description => "A file containing the secret key to use to encrypt data bag item values",
122
+ :proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
123
+
124
+ option :json_attributes,
125
+ :short => "-j JSON",
126
+ :long => "--json-attributes JSON",
127
+ :description => "A JSON string to be added to the first run of chef-client",
128
+ :proc => lambda { |o| JSON.parse(o) }
129
+
130
+ option :subnet,
131
+ :short => "-s SUBNET",
132
+ :long => "--subnet SUBNET",
133
+ :description => "Subnet from which to pick instance IP address. Default: 192.168.67/24",
134
+ :proc => Proc.new { |key| Chef::Config[:knife][:subnet] = key },
135
+ :default => '192.168.67.0/24'
136
+
137
+ option :ip_address,
138
+ :short => "-I IP-ADDRESS",
139
+ :long => "--private-ip-address IP-ADDRESS",
140
+ :description => "Use this IP address for the new instance"
141
+ # :proc => Proc.new { |ip| Chef::Config[:knife][:ip_address] = ip }
142
+
143
+ option :port_forward,
144
+ :short => '-f PORTS',
145
+ :long => '--port-forward PORTS',
146
+ :description => "Comma separated list of HOST:GUEST ports to forward",
147
+ :proc => lambda { |o| Hash[o.split(/,/).collect { |a| a.split(/:/) }] },
148
+ :default => {}
149
+
150
+ option :host_key_verify,
151
+ :long => "--[no-]host-key-verify",
152
+ :description => "Verify host key, enabled by default.",
153
+ :boolean => true,
154
+ :default => true
155
+
156
+ option :vb_customize,
157
+ :long => "--vb-customize VBOXMANAGE_COMMANDS",
158
+ :description => "List of customize options for the virtualbox vagrant provider separated by ::"
159
+
160
+ option :vmx_customize,
161
+ :long => "--vmx-customize VMX_PARAMETERS",
162
+ :description => "List of parameters for the vmware vagrant providers separated by ::"
163
+
164
+ option :provider,
165
+ :long => "--provider PROVIDER",
166
+ :description => "The vagrant provider to use for the server",
167
+ :default => 'virtualbox'
168
+
169
+ option :vagrant_config,
170
+ :long => "--vagrant-config SETTINGS",
171
+ :description => "Lines of configuration to be inserted into the Vagrantfile separated by ::"
172
+
173
+ def run
174
+ $stdout.sync = true
175
+ validate!
176
+
177
+ @server = create_server_def
178
+
179
+ msg_pair("Instance name", @server.name)
180
+ msg_pair("Instance IP", @server.ip_address)
181
+ msg_pair("Box", @server.box || @server.box_url)
182
+
183
+ if vagrant_instance_list.detect { |i| i[:name] == @server.name }
184
+ ui.error("Instance #{@server.name} already exists")
185
+ exit 1
186
+ end
187
+
188
+ # Create Vagrant file for new instance
189
+ print "\n#{ui.color("Launching instance", :magenta)}\n"
190
+ write_vagrantfile
191
+ cmd = "up --provider #{locate_config_value(:provider)}"
192
+ vagrant_exec(@server.name, cmd)
193
+
194
+ write_insecure_key
195
+ print "\n#{ui.color("Waiting for sshd", :magenta)}"
196
+ wait_for_sshd(@server.ip_address)
197
+
198
+ print "\n#{ui.color("Bootstraping instance", :magenta)}\n"
199
+ bootstrap_node(@server,@server.ip_address).run
200
+
201
+
202
+ puts "\n"
203
+ msg_pair("Instance Name", @server.name)
204
+ msg_pair("Box", @server.box || @server.box_url)
205
+ msg_pair("IP Address", @server.ip_address)
206
+ msg_pair("Environment", locate_config_value(:environment) || '_default')
207
+ msg_pair("Run List", (config[:run_list] || []).join(', '))
208
+ msg_pair("JSON Attributes", config[:json_attributes]) unless !config[:json_attributes] || config[:json_attributes].empty?
209
+ end
210
+
211
+ def build_port_forwards(ports)
212
+ ports.collect { |k, v| "config.vm.network :forwarded_port, host: #{k}, guest: #{v}, host_ip: '127.0.0.1'" }.join("\n")
213
+ end
214
+
215
+ def build_vb_customize(customize)
216
+ customize.split(/::/).collect { |k| "vb.customize [ #{k} ]" }.join("\n") if customize
217
+ end
218
+
219
+ def build_vmx_customize(customize)
220
+ customize.split(/::/).collect { |k| "v.vmx [ #{k} ]" }.join("\n") if customize
221
+ end
222
+
223
+ def build_shares(share_folders)
224
+ share_folders.collect do |share|
225
+ host, guest = share.chomp.split "::"
226
+ "config.vm.synced_folder '#{host}', '#{guest}'"
227
+ end.join("\n")
228
+ end
229
+
230
+ def write_vagrantfile
231
+ additions = []
232
+ if @server.use_cachier
233
+ additions << 'config.cache.auto_detect = true' # enable vagarant-cachier
234
+ end
235
+ if @server.vagrant_config
236
+ additions << @server.vagrant_config.split(/::/)
237
+ end
238
+
239
+ file = <<-EOF
240
+ Vagrant.configure("2") do |config|
241
+ config.vm.box = "#{@server.box}"
242
+ config.vm.box_url = "#{@server.box_url}"
243
+ config.vm.hostname = "#{@server.name}"
244
+
245
+ config.vm.network :private_network, ip: "#{@server.ip_address}"
246
+ #{build_port_forwards(@server.port_forward)}
247
+
248
+ #{build_shares(@server.share_folders)}
249
+
250
+ config.vm.provider :virtualbox do |vb|
251
+ vb.customize [ "modifyvm", :id, "--memory", #{@server.memsize} ]
252
+ #{build_vb_customize(@server.vb_customize)}
253
+ end
254
+
255
+ config.vm.provider :vmware_fusion do |v|
256
+ v.vmx["memsize"] = "#{@server.memsize}"
257
+ #{build_vmx_customize(@server.vmx_customize)}
258
+ end
259
+
260
+ config.vm.provider :vmware_workstation do |v|
261
+ v.vmx["memsize"] = "#{@server.memsize}"
262
+ #{build_vmx_customize(@server.vmx_customize)}
263
+ end
264
+
265
+ #{additions.join("\n")}
266
+ end
267
+ EOF
268
+ file
269
+
270
+ # Create folder and write Vagrant file
271
+ instance_dir = File.join(locate_config_value(:vagrant_dir), @server.name)
272
+ instance_file = File.join(instance_dir, 'Vagrantfile')
273
+ ui.msg("Creating #{instance_file}")
274
+ FileUtils.mkdir_p(instance_dir)
275
+ File.open(instance_file, 'w') { |f| f.write(file) }
276
+ end
277
+
278
+ def bootstrap_node(server,ssh_host)
279
+ bootstrap = Chef::Knife::Bootstrap.new
280
+ bootstrap.name_args = [ssh_host]
281
+ bootstrap.config[:ssh_user] = config[:ssh_user]
282
+ bootstrap.config[:ssh_port] = config[:ssh_port]
283
+ bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
284
+ bootstrap.config[:identity_file] = config[:identity_file] || File.join(locate_config_value(:vagrant_dir), 'insecure_key')
285
+ bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || server.ip
286
+ bootstrap.config[:distro] = locate_config_value(:distro) || "chef-full"
287
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
288
+ bootstrap.config[:host_key_verify] = config[:host_key_verify]
289
+
290
+ bootstrap.config[:run_list] = config[:run_list]
291
+ bootstrap.config[:prerelease] = config[:prerelease]
292
+ bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
293
+ bootstrap.config[:distro] = locate_config_value(:distro)
294
+ bootstrap.config[:template_file] = locate_config_value(:template_file)
295
+ bootstrap.config[:environment] = locate_config_value(:environment)
296
+ bootstrap.config[:prerelease] = config[:prerelease]
297
+ bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
298
+ bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
299
+ bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
300
+ bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
301
+ bootstrap.config[:secret] = locate_config_value(:secret)
302
+ bootstrap.config[:secret_file] = locate_config_value(:secret_file)
303
+ # Modify global configuration state to ensure hint gets set by
304
+ # knife-bootstrap
305
+ Chef::Config[:knife][:hints] ||= {}
306
+ Chef::Config[:knife][:hints]["vagrant"] ||= {} # Cargo Cult programming FTW?
307
+
308
+ msg_pair("SSH User", bootstrap.config[:ssh_user])
309
+ msg_pair("SSH identity file", bootstrap.config[:identity_file])
310
+ bootstrap
311
+ end
312
+
313
+ def validate!
314
+ unless locate_config_value(:box) || locate_config_value(:box_url)
315
+ ui.error("You need to either specify --box or --box-url")
316
+ exit 1
317
+ end
318
+ end
319
+
320
+ def find_available_ip
321
+ subnet = locate_config_value('subnet')
322
+ IPAddr.new(subnet).to_range().each { |ip|
323
+ # 192.168.3.0/24 should yield 192.168.3.2 through 192.168.3.254
324
+ # 192.168.3.1 cannot be used because virtual box uses it for the router
325
+ mask = IPAddr::IN4MASK ^ ip.instance_variable_get("@mask_addr")
326
+ unless [0, 1, mask].include? (ip & mask) or vagrant_instance_list.detect { |i| i[:ip_address] == ip.to_s }
327
+ return ip.to_s
328
+ end
329
+ }
330
+ ui.error("No unused IP address available in subnet #{subnet}")
331
+ exit 1
332
+ end
333
+
334
+ def create_server_def
335
+ server_def = {
336
+ :box => locate_config_value(:box),
337
+ :box_url => locate_config_value(:box_url),
338
+ :memsize => locate_config_value(:memsize),
339
+ :share_folders => config[:share_folders],
340
+ :port_forward => config[:port_forward],
341
+ :use_cachier => config[:use_cachier],
342
+ :vb_customize => locate_config_value(:vb_customize),
343
+ :vmx_customize => locate_config_value(:vmx_customize),
344
+ :vagrant_config => locate_config_value(:vagrant_config)
345
+ }
346
+
347
+ # Get specified IP address for new instance or pick an unused one from the subnet pool.
348
+ server_def[:ip_address] = config[:ip_address] || find_available_ip
349
+
350
+ collision = vagrant_instance_list.detect { |i| i[:ip_address] == server_def[:ip_address] }
351
+ if collision
352
+ ui.error("IP address #{server_def[:ip_address]} already in use by instance #{collision[:name]}")
353
+ exit 1
354
+ end
355
+
356
+ # Derive name for vagrant instance from chef node name or IP
357
+ server_def[:name] = locate_config_value(:chef_node_name) || server_def[:ip_address]
358
+
359
+ # Turn it into and object like thing
360
+ OpenStruct.new(server_def)
361
+ end
362
+
363
+ def wait_for_sshd(hostname)
364
+ config[:ssh_gateway] ? wait_for_tunnelled_sshd(hostname) : wait_for_direct_sshd(hostname, config[:ssh_port])
365
+ end
366
+
367
+ def wait_for_tunnelled_sshd(hostname)
368
+ print(".")
369
+ print(".") until tunnel_test_ssh(hostname) {
370
+ sleep @initial_sleep_delay ||= 2
371
+ puts("done")
372
+ }
373
+ end
374
+
375
+ def tunnel_test_ssh(hostname, &block)
376
+ gw_host, gw_user = config[:ssh_gateway].split('@').reverse
377
+ gw_host, gw_port = gw_host.split(':')
378
+ gateway = Net::SSH::Gateway.new(gw_host, gw_user, :port => gw_port || 22)
379
+ status = false
380
+ gateway.open(hostname, config[:ssh_port]) do |local_tunnel_port|
381
+ status = tcp_test_ssh('localhost', local_tunnel_port, &block)
382
+ end
383
+ status
384
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
385
+ sleep 2
386
+ false
387
+ rescue Errno::EPERM, Errno::ETIMEDOUT
388
+ false
389
+ end
390
+
391
+ def wait_for_direct_sshd(hostname, ssh_port)
392
+ print(".") until tcp_test_ssh(hostname, ssh_port) {
393
+ sleep @initial_sleep_delay ||= 2
394
+ puts("done")
395
+ }
396
+ end
397
+
398
+ def tcp_test_ssh(hostname, ssh_port)
399
+ tcp_socket = TCPSocket.new(hostname, ssh_port)
400
+ readable = IO.select([tcp_socket], nil, nil, 5)
401
+ if readable
402
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
403
+ yield
404
+ true
405
+ else
406
+ false
407
+ end
408
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
409
+ sleep 2
410
+ false
411
+ rescue Errno::EPERM, Errno::ETIMEDOUT
412
+ false
413
+ # This happens on some mobile phone networks
414
+ rescue Errno::ECONNRESET
415
+ sleep 2
416
+ false
417
+ ensure
418
+ tcp_socket && tcp_socket.close
419
+ end
420
+
421
+ end
422
+ end
423
+ end
@@ -0,0 +1,73 @@
1
+ require 'chef/knife/vagrant_base'
2
+
3
+ # These two are needed for the '--purge' deletion case
4
+ require 'chef/node'
5
+ require 'chef/api_client'
6
+
7
+ class Chef
8
+ class Knife
9
+ class VagrantServerDelete < Knife
10
+
11
+ include Knife::VagrantBase
12
+
13
+ banner "knife vagrant server delete SERVER [SERVER] (options)"
14
+
15
+ attr_reader :server
16
+
17
+ option :purge,
18
+ :short => "-P",
19
+ :long => "--purge",
20
+ :boolean => true,
21
+ :default => false,
22
+ :description => "Destroy corresponding node and client on the Chef Server, in addition to destroying the Vagrant node itself."
23
+
24
+ # Extracted from Chef::Knife.delete_object, because it has a
25
+ # confirmation step built in... By specifying the '--purge'
26
+ # flag (and also explicitly confirming the server destruction!)
27
+ # the user is already making their intent known. It is not
28
+ # necessary to make them confirm two more times.
29
+ def destroy_item(klass, name, type_name)
30
+ begin
31
+ object = klass.load(name)
32
+ object.destroy
33
+ ui.warn("Deleted #{type_name} #{name}")
34
+ rescue Net::HTTPServerException
35
+ ui.warn("Could not find a #{type_name} named #{name} to delete!")
36
+ end
37
+ end
38
+
39
+ def run
40
+
41
+ @name_args.each do |name|
42
+ instance = vagrant_instance_list.detect { |i| i[:name] == name }
43
+ unless instance
44
+ ui.error("No instance named #{name}")
45
+ next
46
+ end
47
+
48
+ msg_pair("Instance Name", instance[:name])
49
+ msg_pair("Box", instance[:box])
50
+ msg_pair("Vagrant File", instance[:vagrant_file])
51
+ msg_pair("IP Address", instance[:ip_address])
52
+
53
+ puts "\n"
54
+ confirm("Do you really want to delete this instance")
55
+
56
+ vagrant_exec(instance[:name], 'destroy -f')
57
+ instance_dir = File.join(locate_config_value(:vagrant_dir), instance[:name])
58
+ FileUtils.rm_rf(instance_dir)
59
+
60
+ ui.warn("Deleted instance #{instance[:name]}")
61
+
62
+ if config[:purge]
63
+ destroy_item(Chef::Node, instance[:name], "node")
64
+ destroy_item(Chef::ApiClient, instance[:name], "client")
65
+ else
66
+ ui.warn("Corresponding node and client for the #{instance[:name]} instance were not deleted and remain registered with the Chef Server")
67
+ end
68
+ end
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,33 @@
1
+ require 'chef/knife/vagrant_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VagrantServerHalt < Knife
6
+
7
+ include Knife::VagrantBase
8
+
9
+ banner "knife vagrant server halt SERVER [SERVER]"
10
+
11
+ def run
12
+ $stdout.sync = true
13
+
14
+ @name_args.each do |instance|
15
+ unless vagrant_instance_list.detect { |i| i[:name] == instance }
16
+ ui.error("No instance named #{instance}")
17
+ next
18
+ end
19
+
20
+ state, provider = vagrant_instance_state(instance)
21
+
22
+ unless state == 'running' or state == 'saved'
23
+ ui.error("Instance #{instance} needs to be running or suspended for halt. Current state is #{colored_vagrant_state(state)}")
24
+ next
25
+ end
26
+
27
+ vagrant_exec(instance, 'halt')
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ require 'chef/knife/vagrant_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VagrantServerList < Knife
6
+
7
+ include Knife::VagrantBase
8
+
9
+ banner "knife vagrant server list"
10
+
11
+ def run
12
+ $stdout.sync = true
13
+
14
+ server_list = [
15
+ ui.color('Instance Name', :bold),
16
+ ui.color('IP Address', :bold),
17
+ ui.color('Box', :bold),
18
+ ui.color('Provider', :bold),
19
+ ui.color('State', :bold)
20
+ ].flatten.compact
21
+
22
+ output_column_count = server_list.length
23
+
24
+ vagrant_instance_list.each do |server|
25
+ server_list << server[:name]
26
+ server_list << server[:ip_address]
27
+ server_list << server[:box]
28
+
29
+ state, provider = vagrant_instance_state(server[:name])
30
+ server_list << provider
31
+ server_list << colored_vagrant_state(state)
32
+ end
33
+
34
+ puts ui.list(server_list, :uneven_columns_across, output_column_count)
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require 'chef/knife/vagrant_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VagrantServerResume < Knife
6
+
7
+ include Knife::VagrantBase
8
+
9
+ banner "knife vagrant server resume SERVER [SERVER]"
10
+
11
+ def run
12
+ $stdout.sync = true
13
+
14
+ @name_args.each do |instance|
15
+ unless vagrant_instance_list.detect { |i| i[:name] == instance }
16
+ ui.error("No instance named #{instance}")
17
+ next
18
+ end
19
+
20
+ state, provider = vagrant_instance_state(instance)
21
+
22
+ # saved: VirtualBox
23
+ # suspended: VMWare Fusion
24
+ unless state == 'saved' or state == 'suspended'
25
+ ui.error("Instance #{instance} needs to be suspended for resume. Current state is #{colored_vagrant_state(state)}")
26
+ next
27
+ end
28
+
29
+ vagrant_exec(instance, 'resume')
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ require 'chef/knife/vagrant_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VagrantServerSuspend < Knife
6
+
7
+ include Knife::VagrantBase
8
+
9
+ banner "knife vagrant server suspend SERVER [SERVER]"
10
+
11
+ def run
12
+ $stdout.sync = true
13
+
14
+ @name_args.each do |instance|
15
+ unless vagrant_instance_list.detect { |i| i[:name] == instance }
16
+ ui.error("No instance named #{instance}")
17
+ next
18
+ end
19
+
20
+ state, provider = vagrant_instance_state(instance)
21
+
22
+ unless state == 'running'
23
+ ui.error("Instance #{instance} needs to be running for suspend. Current state is #{colored_vagrant_state(state)}")
24
+ next
25
+ end
26
+
27
+ vagrant_exec(instance, 'suspend')
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ require 'chef/knife/vagrant_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VagrantServerUp < Knife
6
+
7
+ include Knife::VagrantBase
8
+
9
+ banner "knife vagrant server up SERVER [SERVER]"
10
+
11
+ def run
12
+ $stdout.sync = true
13
+
14
+ @name_args.each do |instance|
15
+ unless vagrant_instance_list.detect { |i| i[:name] == instance }
16
+ ui.error("No instance named #{instance}")
17
+ next
18
+ end
19
+
20
+ state, provider = vagrant_instance_state(instance)
21
+
22
+ unless state == 'poweroff'
23
+ ui.error("Instance #{instance} needs to be powered off for up. Current state is #{colored_vagrant_state(state)}")
24
+ next
25
+ end
26
+
27
+ vagrant_exec(instance, 'up')
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,6 @@
1
+ module Knife
2
+ module Vagrant
3
+ VERSION = "0.0.5"
4
+ MAJOR, MINOR, TINY = VERSION.split('.')
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-vagrant2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Markus Kern
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.10.10
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.10.10
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Vagrant support for Chef's knife command
42
+ email:
43
+ - makern@users.noreply.github.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - CHANGELOG.md
50
+ - Gemfile
51
+ - LICENSE
52
+ - README.md
53
+ - Rakefile
54
+ - knife-vagrant2.gemspec
55
+ - lib/chef/knife/vagrant_base.rb
56
+ - lib/chef/knife/vagrant_box.rb
57
+ - lib/chef/knife/vagrant_server_create.rb
58
+ - lib/chef/knife/vagrant_server_delete.rb
59
+ - lib/chef/knife/vagrant_server_halt.rb
60
+ - lib/chef/knife/vagrant_server_list.rb
61
+ - lib/chef/knife/vagrant_server_resume.rb
62
+ - lib/chef/knife/vagrant_server_suspend.rb
63
+ - lib/chef/knife/vagrant_server_up.rb
64
+ - lib/knife-vagrant/version.rb
65
+ homepage: https://github.com/makern/knife-vagrant2
66
+ licenses:
67
+ - Apache 2.0
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.2.2
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Vagrant support for Chef's knife command
89
+ test_files: []