knife-vagrant3 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 903e63d91b1390d43259125945d61be2da6e0d94
4
+ data.tar.gz: a6639aab2fa6aa981343c1f81331b5a5f763a6d3
5
+ SHA512:
6
+ metadata.gz: ca1a239ff9aa4719786ae503ea52836262d7b80260aa7a32213468279036208fd84a5c5abbadf54a6d64fc96b2b8a12263c8768dea7d9438c620edc6639d3620
7
+ data.tar.gz: b98869c57ad6d4bd9be1ed25b20f0c67f40985a00803a4216da3788b4778728077e84afae20c0e2ffc7a4e8ce1df00cc289292bc3df6c60a95a92e7bf0cb4d9a
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ # OS files
2
+ .DS_Store
3
+ Icon?
4
+ .Spotlight-V100
5
+ .Trashes
6
+
7
+ *.gem
8
+ Gemfile.lock
9
+
data/CHANGELOG.md ADDED
@@ -0,0 +1,43 @@
1
+ # 0.0.5 (unreleased)
2
+
3
+ ## Changes and new features
4
+
5
+ ## Fixes
6
+
7
+
8
+ # 0.0.4 (2014-03-14)
9
+
10
+ ## Changes and new features
11
+
12
+ * Added --provider option to use vagrant providers other than virtualbox
13
+
14
+
15
+ # 0.0.3 (2014-01-28)
16
+
17
+ ## Fixes
18
+
19
+ * Fixed error when not using --vb-customize option
20
+ * Removed unneeded dependency on knife-ec2
21
+
22
+
23
+ # 0.0.2 (2013-12-17)
24
+
25
+ ## Changes and new features
26
+
27
+ * BREAKING: --share-folders option now takes HOST_PATH::GUEST_PATH instead of NAME::GUEST_PATH::HOST_PATH
28
+ * Added --port-forward option to map host ports to VMs
29
+ * Added --vb-customize option to pass arbitrary VirtualBox options to Vagrant
30
+
31
+
32
+ # 0.0.1 (2013-12-11)
33
+
34
+ ## Changes and new features
35
+
36
+ * Initial release
37
+
38
+
39
+ # Thanks to our contributors
40
+
41
+ * [Jāzeps Baško](https://github.com/jbasko)
42
+ * [Robert J. Berger](https://github.com/rberger)
43
+ * [xsunsmile](https://github.com/xsunsmile)
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2013 Markus Kern <chef@mkern.fastmail.fm>
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.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ knife-vagrant3
2
+ ==============
3
+
4
+ This plugin is an extended version of the [knife-vagrant2 plugin](https://github.com/makern/knife-vagrant2), that adds the ability to create an attach storage when launching the boxes.
data/Rakefile ADDED
@@ -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-vagrant3'
7
+ s.version = Knife::Vagrant::VERSION
8
+ s.authors = ['Markus Kern']
9
+ s.email = ['chef@mkern.fastmail.fm']
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,173 @@
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
+
28
+ # Dir.getwd throws an unknown exception when this plugin
29
+ # is run in a multi-threaded environment
30
+ begin
31
+ cwd = Dir.getwd()
32
+ rescue
33
+ no_cwd_change = true
34
+ end
35
+
36
+ Dir.chdir(File.join(locate_config_value(:vagrant_dir), instance))
37
+ end
38
+
39
+ cmd = "vagrant #{cmd}"
40
+ output = nil
41
+ if defined? Bundler
42
+ # Needed if we are run from inside a bundler environment and vagrant
43
+ # is installed as global gem. If this causes problems for anyone please
44
+ # file a bug report.
45
+ Bundler.with_clean_env do
46
+ output = fetch_output ? %x(#{cmd}) : system(cmd)
47
+ end
48
+ else
49
+ output = fetch_output ? %x(#{cmd}) : system(cmd)
50
+ end
51
+
52
+ unless no_cwd_change
53
+ Dir.chdir(cwd)
54
+ end
55
+ output
56
+ end
57
+
58
+ def vagrant_instance_state(instance)
59
+ output = vagrant_exec(instance, 'status', fetch_output: true)
60
+ state = /Current machine states:.+?default\s+(.+?)\s+\((.+?)\)/m.match(output)
61
+ unless state
62
+ ui.warn("Couldn't parse state of instance #{instance}")
63
+ return ['', '']
64
+ end
65
+ return [state[1], state[2]]
66
+ end
67
+
68
+ def vagrant_instance_list
69
+ # Memoize so we don't have to parse files multiple times
70
+ if @vagrant_instances
71
+ return @vagrant_instances
72
+ end
73
+
74
+ vangrant_dir = locate_config_value(:vagrant_dir)
75
+ @vagrant_instances = []
76
+
77
+ unless File.exist? vangrant_dir
78
+ return @vagrant_instances
79
+ end
80
+
81
+ Dir.foreach(vangrant_dir) { |subdir|
82
+ vagrant_file = File.join(vangrant_dir, subdir, 'Vagrantfile')
83
+ if File.exist? vagrant_file
84
+ instance = { :name => subdir,
85
+ :vagrant_file => vagrant_file
86
+ }
87
+ # Read settings from vagrant file
88
+ content = IO.read(vagrant_file)
89
+ instance[:ip_address] = /config\.vm\.network[^,]+,\s*ip:\s*"([0-9\.]+)"/.match(content) { |m| m[1] }
90
+ unless instance[:ip_address]
91
+ ui.warn("Couldn't find IP address in #{vagrant_file}. Is it malformed?")
92
+ end
93
+
94
+ instance[:box] = /config\.vm\.box[^=]*=\s*"([^"]+)"/.match(content) { |m| m[1] }
95
+ unless instance[:box]
96
+ ui.warn("Couldn't find box in #{vagrant_file}. Is it malformed?")
97
+ end
98
+ @vagrant_instances.push(instance)
99
+ end
100
+ }
101
+ @vagrant_instances
102
+ end
103
+
104
+ def write_insecure_key
105
+ # The private key most vagrant boxes use.
106
+ insecure_key = <<-EOF
107
+ -----BEGIN RSA PRIVATE KEY-----
108
+ MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI
109
+ w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP
110
+ kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2
111
+ hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO
112
+ Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW
113
+ yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd
114
+ ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1
115
+ Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf
116
+ TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK
117
+ iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A
118
+ sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf
119
+ 4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP
120
+ cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk
121
+ EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN
122
+ CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX
123
+ 3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG
124
+ YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj
125
+ 3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+
126
+ dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz
127
+ 6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC
128
+ P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF
129
+ llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ
130
+ kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH
131
+ +vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ
132
+ NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s=
133
+ -----END RSA PRIVATE KEY-----
134
+ EOF
135
+ insecure_key
136
+
137
+ # Write key to vagrant folder if it's not there yet.
138
+ key_file = File.join(locate_config_value(:vagrant_dir), 'insecure_key')
139
+ unless File.exist? key_file
140
+ ui.msg("Creating #{key_file}")
141
+ FileUtils.mkdir_p(locate_config_value(:vagrant_dir))
142
+ File.open(key_file, 'w') { |f| f.write(insecure_key) }
143
+ File.chmod(0600, key_file)
144
+ end
145
+ end
146
+
147
+ def locate_config_value(key)
148
+ key = key.to_sym
149
+ config[key] || Chef::Config[:knife][key]
150
+ end
151
+
152
+ def msg_pair(label, value, color=:cyan)
153
+ if value && !value.to_s.empty?
154
+ puts "#{ui.color(label, color)}: #{value}"
155
+ end
156
+ end
157
+
158
+ def colored_vagrant_state(state)
159
+ case state
160
+ when 'saved','paused','poweroff', 'stuck', 'aborted', 'gurumeditation', 'inaccessible'
161
+ ui.color(state, :red)
162
+ when 'saving'
163
+ ui.color(state, :yellow)
164
+ else
165
+ ui.color(state, :green)
166
+ end
167
+ end
168
+
169
+ end
170
+ end
171
+ end
172
+
173
+
@@ -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,473 @@
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_parameters,
161
+ :long => "--vmx-parameters VMX_PARAMETERS",
162
+ :description => "List of parameters for the vmware fusion/workstation vagrant provider 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 :inline_config,
170
+ :long => "--inline_config snippet",
171
+ :description => "A snippet of configuration to be inserted into the Vagrantfile."
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,instance_dir)
220
+ vmx_params = ""
221
+ if customize
222
+ customize.chomp! if customize.end_with?("\n")
223
+ disks = {}
224
+ customize.split(/::/).each do |p|
225
+ kv = p.split('=')
226
+ k = kv[0].strip
227
+ v = kv[1].strip
228
+ if v
229
+ if k.start_with?('scsi')
230
+ ss = k.split('.')
231
+ disks[ss[0]] ||= {}
232
+ case ss[1]
233
+ when 'fileName'
234
+ if v.start_with?('/')
235
+ disks[ss[0]]['fileName'] = v
236
+ else
237
+ v = File.expand_path(v,instance_dir)
238
+ disks[ss[0]]['fileName'] = v
239
+ end
240
+ when 'fileSize'
241
+ disks[ss[0]]['fileSize'] = v
242
+ next
243
+ end
244
+ end
245
+ vmx_params += "v.vmx[\"#{k}\"] = \"#{v}\"\n"
246
+ end
247
+ end
248
+ if locate_config_value(:provider).start_with?('vmware')
249
+ # Create extra disks as unlike virtual box vmware-fusion/workstation
250
+ # will not create disks automatically based on configuration params
251
+ disks.each_value do |f|
252
+ vdiskmgr = %x(which vmware-vdiskmanager)
253
+ vdiskmgr = "/Applications/VMware Fusion.app/Contents/Library/vmware-vdiskmanager" if vdiskmgr.empty?
254
+ if File.exist?(vdiskmgr)
255
+ disk = f['fileName']
256
+ %x("#{vdiskmgr}" -c -t 0 -s #{f['fileSize']} -a ide #{disk}) unless File.exist?(f['fileName'])
257
+ unless File.exist?(disk)
258
+ ui.error("Disk #{disk} could not be created.")
259
+ exit 1
260
+ end
261
+ else
262
+ ui.error("Unable to determine path to vmware-vdiskmanager to create the requested additional disk.")
263
+ exit 1
264
+ end
265
+ end
266
+ end
267
+ end
268
+ vmx_params
269
+ end
270
+
271
+ def build_shares(share_folders)
272
+ share_folders.collect do |share|
273
+ host, guest = share.chomp.split "::"
274
+ "config.vm.synced_folder '#{host}', '#{guest}'"
275
+ end.join("\n")
276
+ end
277
+
278
+ def write_vagrantfile
279
+
280
+ # Create folder and write Vagrant file
281
+ instance_dir = File.join(locate_config_value(:vagrant_dir), @server.name)
282
+ FileUtils.mkdir_p(instance_dir)
283
+
284
+ additions = []
285
+ if @server.use_cachier
286
+ additions << 'config.cache.auto_detect = true' # enable vagarant-cachier
287
+ end
288
+ if @server.inline_config
289
+ additions << @server.inline_config
290
+ end
291
+
292
+ file = <<-EOF
293
+ Vagrant.configure("2") do |config|
294
+ config.vm.box = "#{@server.box}"
295
+ config.vm.box_url = "#{@server.box_url}"
296
+ config.vm.hostname = "#{@server.name}"
297
+
298
+ config.vm.network :private_network, ip: "#{@server.ip_address}"
299
+ #{build_port_forwards(@server.port_forward)}
300
+
301
+ #{build_shares(@server.share_folders)}
302
+
303
+ config.vm.provider :virtualbox do |vb|
304
+ vb.customize [ "modifyvm", :id, "--memory", #{@server.memsize} ]
305
+ #{build_vb_customize(@server.vb_customize)}
306
+ end
307
+
308
+ config.vm.provider :vmware_fusion do |v|
309
+ v.vmx["memsize"] = "#{@server.memsize}"
310
+ #{build_vmx_customize(@server.vmx_parameters,instance_dir)}
311
+ end
312
+
313
+ config.vm.provider :vmware_workstation do |v|
314
+ v.vmx["memsize"] = "#{@server.memsize}"
315
+ #{build_vmx_customize(@server.vmx_parameters,instance_dir)}
316
+ end
317
+
318
+ #{additions.join("\n")}
319
+ end
320
+ EOF
321
+ file
322
+
323
+ instance_file = File.join(instance_dir, 'Vagrantfile')
324
+ ui.msg("Creating #{instance_file}")
325
+ File.open(instance_file, 'w') { |f| f.write(file) }
326
+ end
327
+
328
+ def bootstrap_node(server,ssh_host)
329
+ bootstrap = Chef::Knife::Bootstrap.new
330
+ bootstrap.name_args = [ssh_host]
331
+ bootstrap.config[:ssh_user] = config[:ssh_user]
332
+ bootstrap.config[:ssh_port] = config[:ssh_port]
333
+ bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
334
+ bootstrap.config[:identity_file] = config[:identity_file] || File.join(locate_config_value(:vagrant_dir), 'insecure_key')
335
+ bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || server.ip
336
+ bootstrap.config[:distro] = locate_config_value(:distro) || "chef-full"
337
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
338
+ bootstrap.config[:host_key_verify] = config[:host_key_verify]
339
+
340
+ bootstrap.config[:run_list] = config[:run_list]
341
+ bootstrap.config[:prerelease] = config[:prerelease]
342
+ bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
343
+ bootstrap.config[:distro] = locate_config_value(:distro)
344
+ bootstrap.config[:template_file] = locate_config_value(:template_file)
345
+ bootstrap.config[:environment] = locate_config_value(:environment)
346
+ bootstrap.config[:prerelease] = config[:prerelease]
347
+ bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
348
+ bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
349
+ bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
350
+ bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
351
+ bootstrap.config[:secret] = locate_config_value(:secret)
352
+ bootstrap.config[:secret_file] = locate_config_value(:secret_file)
353
+ # Modify global configuration state to ensure hint gets set by
354
+ # knife-bootstrap
355
+ Chef::Config[:knife][:hints] ||= {}
356
+ Chef::Config[:knife][:hints]["vagrant"] ||= {} # Cargo Cult programming FTW?
357
+
358
+ msg_pair("SSH User", bootstrap.config[:ssh_user])
359
+ msg_pair("SSH identity file", bootstrap.config[:identity_file])
360
+ bootstrap
361
+ end
362
+
363
+ def validate!
364
+ unless locate_config_value(:box) || locate_config_value(:box_url)
365
+ ui.error("You need to either specify --box or --box-url")
366
+ exit 1
367
+ end
368
+ end
369
+
370
+ def find_available_ip
371
+ subnet = locate_config_value('subnet')
372
+ IPAddr.new(subnet).to_range().each { |ip|
373
+ # 192.168.3.0/24 should yield 192.168.3.2 through 192.168.3.254
374
+ # 192.168.3.1 cannot be used because virtual box uses it for the router
375
+ mask = IPAddr::IN4MASK ^ ip.instance_variable_get("@mask_addr")
376
+ unless [0, 1, mask].include? (ip & mask) or vagrant_instance_list.detect { |i| i[:ip_address] == ip.to_s }
377
+ return ip.to_s
378
+ end
379
+ }
380
+ ui.error("No unused IP address available in subnet #{subnet}")
381
+ exit 1
382
+ end
383
+
384
+ def create_server_def
385
+ server_def = {
386
+ :box => locate_config_value(:box),
387
+ :box_url => locate_config_value(:box_url),
388
+ :memsize => locate_config_value(:memsize),
389
+ :share_folders => config[:share_folders],
390
+ :port_forward => config[:port_forward],
391
+ :use_cachier => config[:use_cachier],
392
+ :vb_customize => locate_config_value(:vb_customize),
393
+ :vmx_parameters => locate_config_value(:vmx_parameters),
394
+ :inline_config => config[:inline_config]
395
+ }
396
+
397
+ # Get specified IP address for new instance or pick an unused one from the subnet pool.
398
+ server_def[:ip_address] = config[:ip_address] || find_available_ip
399
+
400
+ collision = vagrant_instance_list.detect { |i| i[:ip_address] == server_def[:ip_address] }
401
+ if collision
402
+ ui.error("IP address #{server_def[:ip_address]} already in use by instance #{collision[:name]}")
403
+ exit 1
404
+ end
405
+
406
+ # Derive name for vagrant instance from chef node name or IP
407
+ server_def[:name] = locate_config_value(:chef_node_name) || server_def[:ip_address]
408
+
409
+ # Turn it into and object like thing
410
+ OpenStruct.new(server_def)
411
+ end
412
+
413
+ def wait_for_sshd(hostname)
414
+ config[:ssh_gateway] ? wait_for_tunnelled_sshd(hostname) : wait_for_direct_sshd(hostname, config[:ssh_port])
415
+ end
416
+
417
+ def wait_for_tunnelled_sshd(hostname)
418
+ print(".")
419
+ print(".") until tunnel_test_ssh(hostname) {
420
+ sleep @initial_sleep_delay ||= 2
421
+ puts("done")
422
+ }
423
+ end
424
+
425
+ def tunnel_test_ssh(hostname, &block)
426
+ gw_host, gw_user = config[:ssh_gateway].split('@').reverse
427
+ gw_host, gw_port = gw_host.split(':')
428
+ gateway = Net::SSH::Gateway.new(gw_host, gw_user, :port => gw_port || 22)
429
+ status = false
430
+ gateway.open(hostname, config[:ssh_port]) do |local_tunnel_port|
431
+ status = tcp_test_ssh('localhost', local_tunnel_port, &block)
432
+ end
433
+ status
434
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
435
+ sleep 2
436
+ false
437
+ rescue Errno::EPERM, Errno::ETIMEDOUT
438
+ false
439
+ end
440
+
441
+ def wait_for_direct_sshd(hostname, ssh_port)
442
+ print(".") until tcp_test_ssh(hostname, ssh_port) {
443
+ sleep @initial_sleep_delay ||= 2
444
+ puts("done")
445
+ }
446
+ end
447
+
448
+ def tcp_test_ssh(hostname, ssh_port)
449
+ tcp_socket = TCPSocket.new(hostname, ssh_port)
450
+ readable = IO.select([tcp_socket], nil, nil, 5)
451
+ if readable
452
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
453
+ yield
454
+ true
455
+ else
456
+ false
457
+ end
458
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
459
+ sleep 2
460
+ false
461
+ rescue Errno::EPERM, Errno::ETIMEDOUT
462
+ false
463
+ # This happens on some mobile phone networks
464
+ rescue Errno::ECONNRESET
465
+ sleep 2
466
+ false
467
+ ensure
468
+ tcp_socket && tcp_socket.close
469
+ end
470
+
471
+ end
472
+ end
473
+ 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.1"
4
+ MAJOR, MINOR, TINY = VERSION.split('.')
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-vagrant3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
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
+ - chef@mkern.fastmail.fm
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-vagrant3.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.4.3
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Vagrant support for Chef's knife command
89
+ test_files: []