knife-vagrant2 0.0.5

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.
@@ -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: []