chef-metal-vagrant 0.1

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: 741c6c6f6607861923ef353ce6267bc3fb9df4b3
4
+ data.tar.gz: 98283bbb8116bd9d287bfb48b028cf7673cfbbbe
5
+ SHA512:
6
+ metadata.gz: af69bcaf65bf10d31a2032113c9abcbc12ec5b64c22560a5a2432904fffc79f8838f9a8acb9d491bc971911b9d5eb5aee76e5042a512db33454f692cb78971bf
7
+ data.tar.gz: 3f880fd4e7fbd28b26ea9412ccf3dcbe10c785dc6a0a33a2b50ab65cd3f185da6c6a4fb389140627b51dea641f02fdaa51c82319480556fd2276aa9db20da34a
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,3 @@
1
+ # chef-metal-fog
2
+
3
+ This is the Fog provisioner for chef-metal. It provides EC2, DigitalOcean and Openstack functionality.
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ require 'bundler/gem_tasks'
3
+
4
+ task :spec do
5
+ require File.expand_path('spec/run')
6
+ end
@@ -0,0 +1,44 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'chef/mixin/shell_out'
3
+
4
+ class Chef::Provider::VagrantBox < Chef::Provider::LWRPBase
5
+
6
+ use_inline_resources
7
+
8
+ include Chef::Mixin::ShellOut
9
+
10
+ def whyrun_supported?
11
+ true
12
+ end
13
+
14
+ action :create do
15
+ if !list_boxes.has_key?(new_resource.name)
16
+ if new_resource.url
17
+ converge_by "run 'vagrant box add #{new_resource.name} #{new_resource.url}'" do
18
+ shell_out("vagrant box add #{new_resource.name} #{new_resource.url}").error!
19
+ end
20
+ else
21
+ raise "Box #{new_resource.name} does not exist"
22
+ end
23
+ end
24
+ end
25
+
26
+ action :delete do
27
+ if list_boxes.has_key?(new_resource.name)
28
+ converge_by "run 'vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]}'" do
29
+ shell_out("vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]}").error!
30
+ end
31
+ end
32
+ end
33
+
34
+ def list_boxes
35
+ @list_boxes ||= shell_out("vagrant box list").stdout.lines.inject({}) do |result, line|
36
+ line =~ /^(\S+)\s+\((.+)\)\s*$/
37
+ result[$1] = $2
38
+ result
39
+ end
40
+ end
41
+
42
+ def load_current_resource
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'chef_metal/provider_action_handler'
3
+
4
+ class Chef::Provider::VagrantCluster < Chef::Provider::LWRPBase
5
+
6
+ include ChefMetal::ProviderActionHandler
7
+
8
+ use_inline_resources
9
+
10
+ def whyrun_supported?
11
+ true
12
+ end
13
+
14
+ action :create do
15
+ the_base_path = new_resource.path
16
+ ChefMetal.inline_resource(self) do
17
+ directory the_base_path
18
+ file ::File.join(the_base_path, 'Vagrantfile') do
19
+ content <<EOM
20
+ Dir.glob('#{::File.join(the_base_path, '*.vm')}') do |vm_file|
21
+ eval(IO.read(vm_file), nil, vm_file)
22
+ end
23
+ EOM
24
+ end
25
+ end
26
+ end
27
+
28
+ action :delete do
29
+ the_base_path = new_resource.path
30
+ ChefMetal.inline_resource(self) do
31
+ file ::File.join(the_base_path, 'Vagrantfile') do
32
+ action :delete
33
+ end
34
+ directory the_base_path do
35
+ action :delete
36
+ end
37
+ end
38
+ end
39
+
40
+ def load_current_resource
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ require 'chef/resource/lwrp_base'
2
+ require 'chef_metal_vagrant'
3
+
4
+ class Chef::Resource::VagrantBox < Chef::Resource::LWRPBase
5
+ self.resource_name = 'vagrant_box'
6
+
7
+ actions :create, :delete, :nothing
8
+ default_action :create
9
+
10
+ attribute :name, :kind_of => String, :name_attribute => true
11
+ attribute :url, :kind_of => String
12
+ attribute :provisioner_options, :kind_of => Hash
13
+
14
+ def after_created
15
+ super
16
+ ChefMetal.with_vagrant_box self
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ require 'chef/resource/lwrp_base'
2
+ require 'chef_metal_vagrant'
3
+
4
+ class Chef::Resource::VagrantCluster < Chef::Resource::LWRPBase
5
+ self.resource_name = 'vagrant_cluster'
6
+
7
+ actions :create, :delete, :nothing
8
+ default_action :create
9
+
10
+ attribute :path, :kind_of => String, :name_attribute => true
11
+
12
+ def after_created
13
+ super
14
+ ChefMetal.with_vagrant_cluster path
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ require 'chef_metal_vagrant/vagrant_provisioner'
2
+
3
+ ChefMetal.add_registered_provisioner_class("vagrant_cluster",
4
+ ChefMetalVagrant::VagrantProvisioner)
@@ -0,0 +1,39 @@
1
+ require 'chef_metal'
2
+ require 'chef/resource/vagrant_cluster'
3
+ require 'chef/provider/vagrant_cluster'
4
+ require 'chef/resource/vagrant_box'
5
+ require 'chef/provider/vagrant_box'
6
+ require 'chef_metal_vagrant/vagrant_provisioner'
7
+
8
+ module ChefMetal
9
+ def self.with_vagrant_cluster(cluster_path, &block)
10
+ with_provisioner(ChefMetalVagrant::VagrantProvisioner.new(cluster_path), &block)
11
+ end
12
+
13
+ def self.with_vagrant_box(box_name, provisioner_options = nil, &block)
14
+ if box_name.is_a?(Chef::Resource::VagrantBox)
15
+ provisioner_options ||= box_name.provisioner_options || {}
16
+ provisioner_options['vagrant_options'] ||= {}
17
+ provisioner_options['vagrant_options']['vm.box'] = box_name.name
18
+ provisioner_options['vagrant_options']['vm.box_url'] = box_name.url if box_name.url
19
+ else
20
+ provisioner_options ||= {}
21
+ provisioner_options['vagrant_options'] ||= {}
22
+ provisioner_options['vagrant_options']['vm.box'] = box_name
23
+ end
24
+
25
+ with_provisioner_options(provisioner_options, &block)
26
+ end
27
+ end
28
+
29
+ class Chef
30
+ class Recipe
31
+ def with_vagrant_cluster(cluster_path, &block)
32
+ ChefMetal.with_vagrant_cluster(cluster_path, &block)
33
+ end
34
+
35
+ def with_vagrant_box(box_name, vagrant_options = {}, &block)
36
+ ChefMetal.with_vagrant_box(box_name, vagrant_options, &block)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,344 @@
1
+ require 'chef/mixin/shell_out'
2
+ require 'chef_metal/provisioner'
3
+ require 'chef_metal/machine/windows_machine'
4
+ require 'chef_metal/machine/unix_machine'
5
+ require 'chef_metal/convergence_strategy/install_msi'
6
+ require 'chef_metal/convergence_strategy/install_cached'
7
+ require 'chef_metal/transport/winrm'
8
+ require 'chef_metal/transport/ssh'
9
+
10
+ module ChefMetalVagrant
11
+ # Provisions machines in vagrant.
12
+ class VagrantProvisioner < ChefMetal::Provisioner
13
+
14
+ include Chef::Mixin::ShellOut
15
+
16
+ # Create a new vagrant provisioner.
17
+ #
18
+ # ## Parameters
19
+ # cluster_path - path to the directory containing the vagrant files, which
20
+ # should have been created with the vagrant_cluster resource.
21
+ def initialize(cluster_path)
22
+ @cluster_path = cluster_path
23
+ end
24
+
25
+ attr_reader :cluster_path
26
+
27
+ # Inflate a provisioner from node information; we don't want to force the
28
+ # driver to figure out what the provisioner really needs, since it varies
29
+ # from provisioner to provisioner.
30
+ #
31
+ # ## Parameters
32
+ # node - node to inflate the provisioner for
33
+ #
34
+ # returns a VagrantProvisioner
35
+ def self.inflate(node)
36
+ node_url = node['normal']['provisioner_output']['provisioner_url']
37
+ cluster_path = node_url.split(':', 2)[1].sub(/^\/\//, "")
38
+ self.new(cluster_path)
39
+ end
40
+
41
+ # Acquire a machine, generally by provisioning it. Returns a Machine
42
+ # object pointing at the machine, allowing useful actions like setup,
43
+ # converge, execute, file and directory. The Machine object will have a
44
+ # "node" property which must be saved to the server (if it is any
45
+ # different from the original node object).
46
+ #
47
+ # ## Parameters
48
+ # action_handler - the action_handler object that is calling this method; this
49
+ # is generally a action_handler, but could be anything that can support the
50
+ # ChefMetal::ActionHandler interface (i.e., in the case of the test
51
+ # kitchen metal driver for acquiring and destroying VMs; see the base
52
+ # class for what needs providing).
53
+ # node - node object (deserialized json) representing this machine. If
54
+ # the node has a provisioner_options hash in it, these will be used
55
+ # instead of options provided by the provisioner. TODO compare and
56
+ # fail if different?
57
+ # node will have node['normal']['provisioner_options'] in it with any options.
58
+ # It is a hash with this format:
59
+ #
60
+ # -- provisioner_url: vagrant:<cluster_path>
61
+ # -- vagrant_options: hash of properties of the "config"
62
+ # object, i.e. "vm.box" => "ubuntu12" and "vm.box_url"
63
+ # -- vagrant_config: string containing other vagrant config.
64
+ # Should assume the variable "config" represents machine config.
65
+ # Will be written verbatim into the vm's Vagrantfile.
66
+ # -- transport_options: hash of options specifying the transport.
67
+ # :type => :ssh
68
+ # :type => :winrm
69
+ # If not specified, ssh is used unless vm.guest is :windows. If that is
70
+ # the case, the windows options are used and the port forward for 5985
71
+ # is detected.
72
+ # -- up_timeout: maximum time, in seconds, to wait for vagrant
73
+ # to bring up the machine. Defaults to 10 minutes.
74
+ #
75
+ # node['normal']['provisioner_output'] will be populated with information
76
+ # about the created machine. For vagrant, it is a hash with this
77
+ # format:
78
+ #
79
+ # -- provisioner_url: vagrant_cluster://<current_node>/<cluster_path>
80
+ # -- vm_name: name of vagrant vm created
81
+ # -- vm_file_path: path to machine-specific vagrant config file
82
+ # on disk
83
+ # -- forwarded_ports: hash with key as guest_port => host_port
84
+ #
85
+ def acquire_machine(action_handler, node)
86
+ # Set up the modified node data
87
+ provisioner_options = node['normal']['provisioner_options']
88
+ vm_name = node['name']
89
+ old_provisioner_output = node['normal']['provisioner_output']
90
+ node['normal']['provisioner_output'] = provisioner_output = {
91
+ 'provisioner_url' => provisioner_url(action_handler),
92
+ 'vm_name' => vm_name,
93
+ 'vm_file_path' => File.join(cluster_path, "#{vm_name}.vm")
94
+ }
95
+ # Preserve existing forwarded ports
96
+ provisioner_output['forwarded_ports'] = old_provisioner_output['forwarded_ports'] if old_provisioner_output
97
+
98
+ # TODO compare new options to existing and fail if we cannot change it
99
+ # over (perhaps introduce a boolean that will force a delete and recreate
100
+ # in such a case)
101
+
102
+ # Determine contents of vm file
103
+ vm_file_content = "Vagrant.configure('2') do |outer_config|\n"
104
+ vm_file_content << " outer_config.vm.define #{vm_name.inspect} do |config|\n"
105
+ merged_vagrant_options = { 'vm.hostname' => node['name'] }
106
+ merged_vagrant_options.merge!(provisioner_options['vagrant_options']) if provisioner_options['vagrant_options']
107
+ merged_vagrant_options.each_pair do |key, value|
108
+ vm_file_content << " config.#{key} = #{value.inspect}\n"
109
+ end
110
+ vm_file_content << provisioner_options['vagrant_config'] if provisioner_options['vagrant_config']
111
+ vm_file_content << " end\nend\n"
112
+
113
+ # Set up vagrant file
114
+ vm_file = ChefMetal.inline_resource(action_handler) do
115
+ file provisioner_output['vm_file_path'] do
116
+ content vm_file_content
117
+ action :create
118
+ end
119
+ end
120
+
121
+ # Check current status of vm
122
+ current_status = vagrant_status(vm_name)
123
+ up_timeout = provisioner_options['up_timeout'] || 10*60
124
+
125
+ if current_status != 'running'
126
+ # Run vagrant up if vm is not running
127
+ action_handler.perform_action "run vagrant up #{vm_name} (status was '#{current_status}')" do
128
+ result = shell_out("vagrant up #{vm_name}", :cwd => cluster_path, :timeout => up_timeout)
129
+ if result.exitstatus != 0
130
+ raise "vagrant up #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
131
+ end
132
+ parse_vagrant_up(result.stdout, node)
133
+ end
134
+ elsif vm_file.updated_by_last_action?
135
+ # Run vagrant reload if vm is running and vm file changed
136
+ action_handler.perform_action "run vagrant reload #{vm_name}" do
137
+ result = shell_out("vagrant reload #{vm_name}", :cwd => cluster_path, :timeout => up_timeout)
138
+ if result.exitstatus != 0
139
+ raise "vagrant reload #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
140
+ end
141
+ parse_vagrant_up(result.stdout, node)
142
+ end
143
+ end
144
+
145
+ # Create machine object for callers to use
146
+ machine_for(node)
147
+ end
148
+
149
+ # Connect to machine without acquiring it
150
+ def connect_to_machine(node)
151
+ machine_for(node)
152
+ end
153
+
154
+ def delete_machine(action_handler, node)
155
+ if node['normal'] && node['normal']['provisioner_output']
156
+ provisioner_output = node['normal']['provisioner_output']
157
+ else
158
+ provisioner_output = {}
159
+ end
160
+ vm_name = provisioner_output['vm_name'] || node['name']
161
+ current_status = vagrant_status(vm_name)
162
+ if current_status != 'not created'
163
+ action_handler.perform_action "run vagrant destroy -f #{vm_name} (status was '#{current_status}')" do
164
+ result = shell_out("vagrant destroy -f #{vm_name}", :cwd => cluster_path)
165
+ if result.exitstatus != 0
166
+ raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
167
+ end
168
+ end
169
+ end
170
+
171
+ convergence_strategy_for(node).cleanup_convergence(action_handler, node)
172
+
173
+ vm_file_path = provisioner_output['vm_file_path'] || File.join(cluster_path, "#{vm_name}.vm")
174
+ ChefMetal.inline_resource(action_handler) do
175
+ file vm_file_path do
176
+ action :delete
177
+ end
178
+ end
179
+ end
180
+
181
+ def stop_machine(action_handler, node)
182
+ if node['normal'] && node['normal']['provisioner_output']
183
+ provisioner_output = node['normal']['provisioner_output']
184
+ else
185
+ provisioner_output = {}
186
+ end
187
+ vm_name = provisioner_output['vm_name'] || node['name']
188
+ current_status = vagrant_status(vm_name)
189
+ if current_status == 'running'
190
+ action_handler.perform_action "run vagrant halt #{vm_name} (status was '#{current_status}')" do
191
+ result = shell_out("vagrant halt #{vm_name}", :cwd => cluster_path)
192
+ if result.exitstatus != 0
193
+ raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+
200
+ # Used by vagrant_cluster and machine to get the string used to configure vagrant
201
+ def self.vagrant_config_string(vagrant_config, variable, line_prefix)
202
+ hostname = name.gsub(/[^A-Za-z0-9\-]/, '-')
203
+
204
+ result = ''
205
+ vagrant_config.each_pair do |key, value|
206
+ result += "#{line_prefix}#{variable}.#{key} = #{value.inspect}\n"
207
+ end
208
+ result
209
+ end
210
+
211
+ protected
212
+
213
+ def provisioner_url(action_handler)
214
+ "vagrant_cluster://#{action_handler.node['name']}#{cluster_path}"
215
+ end
216
+
217
+ def parse_vagrant_up(output, node)
218
+ # Grab forwarded port info
219
+ in_forwarding_ports = false
220
+ output.lines.each do |line|
221
+ if in_forwarding_ports
222
+ if line =~ /-- (\d+) => (\d+)/
223
+ node['normal']['provisioner_output']['forwarded_ports'][$1] = $2
224
+ else
225
+ in_forwarding_ports = false
226
+ end
227
+ elsif line =~ /Forwarding ports...$/
228
+ node['normal']['provisioner_output']['forwarded_ports'] = {}
229
+ in_forwarding_ports = true
230
+ end
231
+ end
232
+ end
233
+
234
+ def machine_for(node)
235
+ if vagrant_option(node, 'vm.guest').to_s == 'windows'
236
+ ChefMetal::Machine::WindowsMachine.new(node, transport_for(node), convergence_strategy_for(node))
237
+ else
238
+ ChefMetal::Machine::UnixMachine.new(node, transport_for(node), convergence_strategy_for(node))
239
+ end
240
+ end
241
+
242
+ def convergence_strategy_for(node)
243
+ if vagrant_option(node, 'vm.guest').to_s == 'windows'
244
+ @windows_convergence_strategy ||= begin
245
+ ChefMetal::ConvergenceStrategy::InstallMsi.new
246
+ end
247
+ else
248
+ @unix_convergence_strategy ||= begin
249
+ ChefMetal::ConvergenceStrategy::InstallCached.new
250
+ end
251
+ end
252
+ end
253
+
254
+ def transport_for(node)
255
+ if vagrant_option(node, 'vm.guest').to_s == 'windows'
256
+ create_winrm_transport(node)
257
+ else
258
+ create_ssh_transport(node)
259
+ end
260
+ end
261
+
262
+ def vagrant_option(node, option)
263
+ if node['normal']['provisioner_options'] &&
264
+ node['normal']['provisioner_options']['vagrant_options']
265
+ node['normal']['provisioner_options']['vagrant_options'][option]
266
+ else
267
+ nil
268
+ end
269
+ end
270
+
271
+ def vagrant_status(name)
272
+ status_output = shell_out("vagrant status #{name}", :cwd => cluster_path).stdout
273
+ if status_output =~ /^#{name}\s+([^\n]+)\s+\(([^\n]+)\)$/m
274
+ $1
275
+ else
276
+ 'not created'
277
+ end
278
+ end
279
+
280
+ def create_winrm_transport(node)
281
+ provisioner_output = node['default']['provisioner_output'] || {}
282
+ forwarded_ports = provisioner_output['forwarded_ports'] || {}
283
+
284
+ # TODO IPv6 loopback? What do we do for that?
285
+ hostname = vagrant_option(node, 'winrm.host') || '127.0.0.1'
286
+ port = vagrant_option(node, 'winrm.port') || forwarded_ports[5985] || 5985
287
+ endpoint = "http://#{hostname}:#{port}/wsman"
288
+ type = :plaintext
289
+ options = {
290
+ :user => vagrant_option(node, 'winrm.username') || 'vagrant',
291
+ :pass => vagrant_option(node, 'winrm.password') || 'vagrant',
292
+ :disable_sspi => true
293
+ }
294
+
295
+ ChefMetal::Transport::WinRM.new(endpoint, type, options)
296
+ end
297
+
298
+ def create_ssh_transport(node)
299
+ vagrant_ssh_config = vagrant_ssh_config_for(node)
300
+ hostname = vagrant_ssh_config['HostName']
301
+ username = vagrant_ssh_config['User']
302
+ ssh_options = {
303
+ :port => vagrant_ssh_config['Port'],
304
+ :auth_methods => ['publickey'],
305
+ :user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
306
+ :paranoid => yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
307
+ :keys => [ strip_quotes(vagrant_ssh_config['IdentityFile']) ],
308
+ :keys_only => yes_or_no(vagrant_ssh_config['IdentitiesOnly'])
309
+ }
310
+ ssh_options[:auth_methods] = %w(password) if yes_or_no(vagrant_ssh_config['PasswordAuthentication'])
311
+ options = {
312
+ :prefix => 'sudo '
313
+ }
314
+ ChefMetal::Transport::SSH.new(hostname, username, ssh_options, options)
315
+ end
316
+
317
+ def vagrant_ssh_config_for(node)
318
+ vagrant_ssh_config = {}
319
+ result = shell_out("vagrant ssh-config #{node['normal']['provisioner_output']['vm_name']}", :cwd => cluster_path)
320
+ result.stdout.lines.inject({}) do |result, line|
321
+ line =~ /^\s*(\S+)\s+(.+)/
322
+ vagrant_ssh_config[$1] = $2
323
+ end
324
+ vagrant_ssh_config
325
+ end
326
+
327
+ def yes_or_no(str)
328
+ case str
329
+ when 'yes'
330
+ true
331
+ else
332
+ false
333
+ end
334
+ end
335
+
336
+ def strip_quotes(str)
337
+ if str[0] == '"' && str[-1] == '"' && str.size >= 2
338
+ str[1..-2]
339
+ else
340
+ str
341
+ end
342
+ end
343
+ end
344
+ end
@@ -0,0 +1,3 @@
1
+ module ChefMetalVagrant
2
+ VERSION = '0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-metal-vagrant
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - John Keiser
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-04 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'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
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
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Provisioner for creating Vagrant instances in Chef Metal.
56
+ email: jkeiser@getchef.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files:
60
+ - README.md
61
+ - LICENSE
62
+ files:
63
+ - Rakefile
64
+ - LICENSE
65
+ - README.md
66
+ - lib/chef/provider/vagrant_box.rb
67
+ - lib/chef/provider/vagrant_cluster.rb
68
+ - lib/chef/resource/vagrant_box.rb
69
+ - lib/chef/resource/vagrant_cluster.rb
70
+ - lib/chef_metal/provisioner_init/vagrant_cluster_init.rb
71
+ - lib/chef_metal_vagrant/vagrant_provisioner.rb
72
+ - lib/chef_metal_vagrant/version.rb
73
+ - lib/chef_metal_vagrant.rb
74
+ homepage: https://github.com/opscode/chef-metal-fog
75
+ licenses: []
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.0.3
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Provisioner for creating Vagrant instances in Chef Metal.
97
+ test_files: []
98
+ has_rdoc: