chef-metal-crowbar 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +3 -0
- data/Rakefile +22 -0
- data/lib/chef/provider/crowbar_key_pair.rb +217 -0
- data/lib/chef/resource/crowbar_key_pair.rb +43 -0
- data/lib/chef_metal/driver_init/crowbar.rb +17 -0
- data/lib/chef_metal_crowbar.rb +17 -0
- data/lib/chef_metal_crowbar/crowbar_driver.rb +696 -0
- data/lib/chef_metal_crowbar/providers/core.rb +50 -0
- data/lib/chef_metal_crowbar/recipe_dsl.rb +29 -0
- data/lib/chef_metal_crowbar/version.rb +17 -0
- data/lib/crowbar/core.rb +123 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0425f5008268f505bb329da8705c9ab3720d44c7
|
4
|
+
data.tar.gz: aabb37caae2cf94343380b0cdb1c6ab30292775d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8fc75b0411a5d4f785c582fba068371751448ae714f35611292ca1c96f3cedcdf4e60a7b5600e898dd937668831d1888c7440ea33c86cac6a31f63d12320fa08
|
7
|
+
data.tar.gz: 3ea6801e6e7e8d5bf91d75fa735329f79087b5b145292172960e8905ea4b6aae1b346ff790e15e10542e2b620b88101d252268230f74250d271ae05ea8828f5a
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Apache 2
|
2
|
+
|
3
|
+
This submodule of OpenCrowbar is Apache 2 licensed.
|
4
|
+
|
5
|
+
## Copyright 2014, Rob Hirschfeld
|
6
|
+
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
you may not use this file except in compliance with the License.
|
9
|
+
You may obtain a copy of the License at
|
10
|
+
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
See the License for the specific language governing permissions and
|
17
|
+
limitations under the License.
|
18
|
+
|
19
|
+
|
20
|
+
## For details
|
21
|
+
|
22
|
+
See [[doc/licenses/README.md]]
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# Copyright 2014, Rob Hirschfeld
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require 'bundler'
|
17
|
+
require 'bundler'
|
18
|
+
require 'bundler/gem_tasks'
|
19
|
+
|
20
|
+
task :spec do
|
21
|
+
require File.expand_path('spec/run')
|
22
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
# Copyright 2014, Rob Hirschfeld
|
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.
|
14
|
+
|
15
|
+
require 'chef/provider/lwrp_base'
|
16
|
+
require 'chef_metal_crowbar/crowbar_driver'
|
17
|
+
|
18
|
+
class Chef::Provider::CrowbarKeyPair < Chef::Provider::LWRPBase
|
19
|
+
|
20
|
+
use_inline_resources
|
21
|
+
|
22
|
+
def whyrun_supported?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
action :create do
|
27
|
+
create_key(:create)
|
28
|
+
end
|
29
|
+
|
30
|
+
action :delete do
|
31
|
+
if current_resource_exists?
|
32
|
+
converge_by "delete #{key_description}" do
|
33
|
+
case new_driver.compute_options[:provider]
|
34
|
+
when 'DigitalOcean'
|
35
|
+
compute.destroy_key_pair(@current_id)
|
36
|
+
when 'OpenStack', 'Rackspace'
|
37
|
+
compute.key_pairs.destroy(@current_id)
|
38
|
+
else
|
39
|
+
compute.key_pairs.delete(new_resource.name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def key_description
|
46
|
+
"#{new_resource.name} on #{new_driver.driver_url}"
|
47
|
+
end
|
48
|
+
|
49
|
+
@@use_pkcs8 = nil # For Ruby 1.9 and below, PKCS can be run
|
50
|
+
|
51
|
+
def create_key(action)
|
52
|
+
if @should_create_directory
|
53
|
+
Cheffish.inline_resource(self, action) do
|
54
|
+
directory run_context.config[:private_key_write_path]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if current_resource_exists?
|
59
|
+
# If the public keys are different, update the server public key
|
60
|
+
if !current_resource.private_key_path
|
61
|
+
if new_resource.allow_overwrite
|
62
|
+
ensure_keys(action)
|
63
|
+
else
|
64
|
+
raise "#{key_description} already exists on the server, but the private key #{new_private_key_path} does not exist!"
|
65
|
+
end
|
66
|
+
else
|
67
|
+
ensure_keys(action)
|
68
|
+
end
|
69
|
+
|
70
|
+
case new_driver.compute_options[:provider]
|
71
|
+
when 'DigitalOcean'
|
72
|
+
new_fingerprints = [Cheffish::KeyFormatter.encode(desired_key, :format => :openssh)]
|
73
|
+
when 'OpenStack', 'Rackspace'
|
74
|
+
new_fingerprints = [Cheffish::KeyFormatter.encode(desired_key, :format => :openssh)]
|
75
|
+
else
|
76
|
+
# “The nice thing about standards is that you have so many to
|
77
|
+
# choose from.” - Andrew S. Tanenbaum
|
78
|
+
#
|
79
|
+
# The AWS EC2 API uses a PKCS#1 MD5 fingerprint for keys that you
|
80
|
+
# import into EC2, but a PKCS#8 SHA1 fingerprint for keys that you
|
81
|
+
# generate using its web console. Both fingerprints are different
|
82
|
+
# from the familiar RFC4716 MD5 fingerprint that OpenSSH displays
|
83
|
+
# for host keys.
|
84
|
+
#
|
85
|
+
# So compute both possible AWS fingerprints and check if either of
|
86
|
+
# them matches.
|
87
|
+
new_fingerprints = [Cheffish::KeyFormatter.encode(desired_key, :format => :fingerprint)]
|
88
|
+
if RUBY_VERSION.to_f < 2.0
|
89
|
+
if @@use_pkcs8.nil?
|
90
|
+
begin
|
91
|
+
require 'openssl_pkcs8'
|
92
|
+
@@use_pkcs8 = true
|
93
|
+
rescue LoadError
|
94
|
+
Chef::Log.warn("The openssl_pkcs8 gem is not loaded: you may not be able to read key fingerprints created by some cloud providers. gem install openssl_pkcs8 to fix!")
|
95
|
+
@@use_pkcs8 = false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
if @@use_pkcs8
|
99
|
+
new_fingerprints << Cheffish::KeyFormatter.encode(desired_private_key,
|
100
|
+
:format => :pkcs8sha1fingerprint)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if !new_fingerprints.any? { |f| compare_public_key f }
|
106
|
+
if new_resource.allow_overwrite
|
107
|
+
converge_by "update #{key_description} to match local key at #{new_resource.private_key_path}" do
|
108
|
+
compute.key_pairs.get(new_resource.name).destroy
|
109
|
+
compute.import_key_pair(new_resource.name, Cheffish::KeyFormatter.encode(desired_key, :format => :openssh))
|
110
|
+
end
|
111
|
+
else
|
112
|
+
raise "#{key_description} with fingerprint #{@current_fingerprint} does not match local key fingerprint(s) #{new_fingerprints}, and allow_overwrite is false!"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
else
|
116
|
+
# Generate the private and/or public keys if they do not exist
|
117
|
+
ensure_keys(action)
|
118
|
+
|
119
|
+
# Create key
|
120
|
+
converge_by "create #{key_description} from local key at #{new_resource.private_key_path}" do
|
121
|
+
compute.import_key_pair(new_resource.name, Cheffish::KeyFormatter.encode(desired_key, :format => :openssh))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def new_driver
|
127
|
+
run_context.chef_metal.driver_for(new_resource.driver)
|
128
|
+
end
|
129
|
+
|
130
|
+
def ensure_keys(action)
|
131
|
+
resource = new_resource
|
132
|
+
private_key_path = new_private_key_path
|
133
|
+
Cheffish.inline_resource(self, action) do
|
134
|
+
private_key private_key_path do
|
135
|
+
public_key_path resource.public_key_path
|
136
|
+
if resource.private_key_options
|
137
|
+
resource.private_key_options.each_pair do |key,value|
|
138
|
+
send(key, value)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def desired_key
|
146
|
+
@desired_key ||= begin
|
147
|
+
if new_resource.public_key_path
|
148
|
+
public_key, format = Cheffish::KeyFormatter.decode(IO.read(new_resource.public_key_path))
|
149
|
+
public_key
|
150
|
+
else
|
151
|
+
desired_private_key.public_key
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def desired_private_key
|
157
|
+
@desired_private_key ||= begin
|
158
|
+
private_key, format = Cheffish::KeyFormatter.decode(IO.read(new_private_key_path))
|
159
|
+
private_key
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def current_resource_exists?
|
164
|
+
@current_resource.action != [ :delete ]
|
165
|
+
end
|
166
|
+
|
167
|
+
def compare_public_key(new)
|
168
|
+
c = @current_fingerprint.split[0,2].join(' ')
|
169
|
+
n = new.split[0,2].join(' ')
|
170
|
+
c == n
|
171
|
+
end
|
172
|
+
|
173
|
+
def compute
|
174
|
+
new_driver.compute
|
175
|
+
end
|
176
|
+
|
177
|
+
def current_public_key
|
178
|
+
current_resource.source_key
|
179
|
+
end
|
180
|
+
|
181
|
+
def new_private_key_path
|
182
|
+
private_key_path = new_resource.private_key_path || new_resource.name
|
183
|
+
if private_key_path.is_a?(Symbol)
|
184
|
+
private_key_path
|
185
|
+
elsif Pathname.new(private_key_path).relative? && new_driver.config[:private_key_write_path]
|
186
|
+
@should_create_directory = true
|
187
|
+
::File.join(new_driver.config[:private_key_write_path], private_key_path)
|
188
|
+
else
|
189
|
+
private_key_path
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def new_public_key_path
|
194
|
+
new_resource.public_key_path
|
195
|
+
end
|
196
|
+
|
197
|
+
def load_current_resource
|
198
|
+
if !new_driver.kind_of?(ChefMetalCrowbar::CrowbarDriver)
|
199
|
+
raise 'crowbar_key_pair only works with crowbar_driver'
|
200
|
+
end
|
201
|
+
@current_resource = Chef::Resource::CrowbarKeyPair.new(new_resource.name, run_context)
|
202
|
+
# there is only 1 provider, so there's no need to change it
|
203
|
+
current_key_pair = compute.key_pairs.get(new_resource.name)
|
204
|
+
if current_key_pair
|
205
|
+
@current_fingerprint = current_key_pair ? current_key_pair.fingerprint : nil
|
206
|
+
else
|
207
|
+
current_resource.action :delete
|
208
|
+
end
|
209
|
+
|
210
|
+
if new_private_key_path && ::File.exist?(new_private_key_path)
|
211
|
+
current_resource.private_key_path new_private_key_path
|
212
|
+
end
|
213
|
+
if new_public_key_path && ::File.exist?(new_public_key_path)
|
214
|
+
current_resource.public_key_path new_public_key_path
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright 2014, Rob Hirschfeld
|
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.
|
14
|
+
|
15
|
+
require 'chef_metal'
|
16
|
+
|
17
|
+
class Chef::Resource::CrowbarKeyPair < Chef::Resource::LWRPBase
|
18
|
+
self.resource_name = 'crowbar_key_pair'
|
19
|
+
|
20
|
+
def initialize(*args)
|
21
|
+
super
|
22
|
+
@driver = run_context.chef_metal.current_driver
|
23
|
+
end
|
24
|
+
|
25
|
+
actions :create, :delete, :nothing
|
26
|
+
default_action :create
|
27
|
+
|
28
|
+
attribute :driver
|
29
|
+
# Private key to use as input (will be generated if it does not exist)
|
30
|
+
attribute :private_key_path, :kind_of => String
|
31
|
+
# Public key to use as input (will be generated if it does not exist)
|
32
|
+
attribute :public_key_path, :kind_of => String
|
33
|
+
# List of parameters to the private_key resource used for generation of the key
|
34
|
+
attribute :private_key_options, :kind_of => Hash
|
35
|
+
|
36
|
+
# TODO what is the right default for this?
|
37
|
+
attribute :allow_overwrite, :kind_of => [TrueClass, FalseClass], :default => false
|
38
|
+
|
39
|
+
# Proc that runs after the resource completes. Called with (resource, private_key, public_key)
|
40
|
+
def after(&block)
|
41
|
+
block ? @after = block : @after
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright 2014, Rob Hirschfeld
|
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.
|
14
|
+
|
15
|
+
require 'chef_metal_crowbar/crowbar_driver'
|
16
|
+
|
17
|
+
ChefMetal.register_driver_class("crowbar", ChefMetalCrowbar::CrowbarDriver)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright 2014, Rob Hirschfeld
|
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.
|
14
|
+
|
15
|
+
require 'chef_metal'
|
16
|
+
require 'chef_metal_crowbar/crowbar_driver'
|
17
|
+
require 'chef_metal_crowbar/recipe_dsl'
|
@@ -0,0 +1,696 @@
|
|
1
|
+
# Copyright 2014, Rob Hirschfeld
|
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.
|
14
|
+
|
15
|
+
require 'chef_metal/driver'
|
16
|
+
require 'chef_metal/machine/windows_machine'
|
17
|
+
require 'chef_metal/machine/unix_machine'
|
18
|
+
require 'chef_metal/machine_spec'
|
19
|
+
require 'chef_metal/convergence_strategy/install_msi'
|
20
|
+
require 'chef_metal/convergence_strategy/install_sh'
|
21
|
+
require 'chef_metal/convergence_strategy/install_cached'
|
22
|
+
require 'chef_metal/convergence_strategy/no_converge'
|
23
|
+
require 'chef_metal/transport/ssh'
|
24
|
+
require 'chef_metal_crowbar/version'
|
25
|
+
require 'etc'
|
26
|
+
require 'time'
|
27
|
+
require 'cheffish/merged_config'
|
28
|
+
require 'chef_metal_crowbar/recipe_dsl'
|
29
|
+
require 'crowbar/core'
|
30
|
+
|
31
|
+
module ChefMetalCrowbar
|
32
|
+
|
33
|
+
class CrowbarDriver < ChefMetal::Driver
|
34
|
+
|
35
|
+
AVAILABLE_DEPLOYMENT = 'available'
|
36
|
+
RESERVED_DEPLOYMENT = 'reserved'
|
37
|
+
TARGET_NODE_ROLE = "crowbar-managed-node"
|
38
|
+
KEY_ATTRIB = "chef-server_admin_client_key"
|
39
|
+
|
40
|
+
# Passed in a driver_url, and a config in the format of Driver.config.
|
41
|
+
def self.from_url(driver_url, config)
|
42
|
+
CrowbarDriver.new(driver_url, config)
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(driver_url, config)
|
46
|
+
super(driver_url, config)
|
47
|
+
end
|
48
|
+
|
49
|
+
def crowbar_api
|
50
|
+
# relies on url & driver_config from Driver superclass
|
51
|
+
scheme, crowbar_url = url.split(':', 2)
|
52
|
+
Crowbar::Core.connect crowbar_url, driver_config
|
53
|
+
end
|
54
|
+
|
55
|
+
# Acquire a machine, generally by provisioning it. Returns a Machine
|
56
|
+
# object pointing at the machine, allowing useful actions like setup,
|
57
|
+
# converge, execute, file and directory.
|
58
|
+
def allocate_machine(action_handler, machine_spec, machine_options)
|
59
|
+
Core.connect crowbar_url
|
60
|
+
|
61
|
+
# If the server does not exist, create it
|
62
|
+
create_servers(action_handler, { machine_spec => machine_options }, Chef::ChefFS::Parallelizer.new(0))
|
63
|
+
machine_spec
|
64
|
+
end
|
65
|
+
|
66
|
+
def allocate_machine(action_handler, machine_spec, machine_options)
|
67
|
+
if !crowbar_api.node_exists?(machine_spec.location['server_id'])
|
68
|
+
# It doesn't really exist
|
69
|
+
action_handler.perform_action "Machine #{machine_spec.location['server_id']} does not really exist. Recreating ..." do
|
70
|
+
machine_spec.location = nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
if !machine_spec.location
|
74
|
+
action_handler.perform_action "Creating server #{machine_spec.name} with options #{machine_options}" do
|
75
|
+
server = crowbar_api.allocate_server(machine_spec.name, machine_options)
|
76
|
+
server_id = server["id"]
|
77
|
+
machine_spec.location = {
|
78
|
+
'driver_url' => driver_url,
|
79
|
+
'driver_version' => ChefMetalCrowbar::VERSION,
|
80
|
+
'server_id' => server_id,
|
81
|
+
'bootstrap_key' => crowbar_api.ssh_private_key(server_id)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def ready_machine(action_handler, machine_spec, machine_options)
|
88
|
+
server_id = machine_spec.location['server_id']
|
89
|
+
server = crowbar_api.node(server_id)
|
90
|
+
if server["alive"] == 'false'
|
91
|
+
action_handler.perform_action "Powering up machine #{server_id}" do
|
92
|
+
crowbar_api.power(server_id, "on")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if server["state"] != 0
|
97
|
+
action_handler.perform_action "wait for machine #{server_id}" do
|
98
|
+
crowbar_api.wait_for_machine_to_have_status(server_id, 0)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return the Machine object
|
103
|
+
machine_for(machine_spec, machine_options)
|
104
|
+
end
|
105
|
+
|
106
|
+
def machine_for(machine_spec, machine_options)
|
107
|
+
server_id = machine_spec.location['server_id']
|
108
|
+
ssh_options = {
|
109
|
+
:auth_methods => ['publickey'],
|
110
|
+
:keys => [ get_key('bootstrapkey') ],
|
111
|
+
}
|
112
|
+
transport = ChefMetal::Transport::SSHTransport.new(server_id, ssh_options, {}, config)
|
113
|
+
convergence_strategy = ChefMetal::ConvergenceStrategy::InstallCached.new(machine_options[:convergence_options])
|
114
|
+
ChefMetal::Machine::UnixMachine.new(machine_spec, transport, convergence_strategy)
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
|
120
|
+
# hit node API to see if node exists (code 200 only)
|
121
|
+
def node_exists?(name)
|
122
|
+
exists?("node", name)
|
123
|
+
end
|
124
|
+
|
125
|
+
# hit deployment API to see if deployment exists (code 200 only)
|
126
|
+
def deployment_exists?(name)
|
127
|
+
exists?("deployment", name)
|
128
|
+
end
|
129
|
+
|
130
|
+
# using the attibutes, get the key
|
131
|
+
def ssh_private_key(name)
|
132
|
+
get(driver_url + API_BASE + "nodes/#{name}/attribs/#{KEY_ATTRIB}")
|
133
|
+
end
|
134
|
+
|
135
|
+
# follow getready process to allocate nodes
|
136
|
+
def allocate_node(name, machine_options)
|
137
|
+
|
138
|
+
# get available nodes
|
139
|
+
from_deployment = AVAILABLE_DEPLOYMENT
|
140
|
+
raise "Available Pool '#{from_deployment} does not exist" unless deployment_exists?(from_deployment)
|
141
|
+
pool = get(driver_url + API_BASE + "deployments/#{from_deployment}/nodes")
|
142
|
+
raise "No available nodes in pool #{from_deployment}" if pool.size == 0
|
143
|
+
|
144
|
+
# assign node from pool
|
145
|
+
node = pool[0]
|
146
|
+
|
147
|
+
# prepare for moving by moving the deployment to proposed
|
148
|
+
to_deployment = RESERVED_DEPLOYMENT
|
149
|
+
put(driver_url + API_BASE + "deployments/#{to_deployment}/propose")
|
150
|
+
|
151
|
+
# set alias (name) and reserve
|
152
|
+
node["alias"] = name
|
153
|
+
node["deployment"] = to_deployment
|
154
|
+
put(driver_url + API_BASE + "nodes/#{node["id"]}", node)
|
155
|
+
|
156
|
+
# bind the OS NodeRole if missing (eventually set the OS property)
|
157
|
+
bind = {:node=>node["id"], :role=>TARGET_NODE_ROLE, :deployment=>to_deployment}
|
158
|
+
# blindly add node role > we need to make this smarter and skip if unneeded
|
159
|
+
post(driver_url + API_BASE + "node_roles", bind)
|
160
|
+
|
161
|
+
# commit the deployment
|
162
|
+
put(driver_url + API_BASE + "deployments/#{to_deployment}/commit")
|
163
|
+
|
164
|
+
# at this point Crowbar will bring up the node in the background
|
165
|
+
# we can return the node handle to the user
|
166
|
+
node["name"]
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
def node(name)
|
171
|
+
get(driver_url + API_BASE + "nodes/#{name}")
|
172
|
+
end
|
173
|
+
|
174
|
+
def power(name, action="on")
|
175
|
+
put(driver_url + API_BASE + "nodes/#{name}/power?poweraction=#{action}")
|
176
|
+
end
|
177
|
+
|
178
|
+
def wait_for_machine_to_have_status(name, target_state)
|
179
|
+
node(name)["state"] == target_state
|
180
|
+
end
|
181
|
+
|
182
|
+
# debug messages
|
183
|
+
def debug(msg)
|
184
|
+
Chef::Log.debug msg
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
# include Chef::Mixin::ShellOut
|
189
|
+
|
190
|
+
# DEFAULT_OPTIONS = {
|
191
|
+
# :create_timeout => 180,
|
192
|
+
# :start_timeout => 180,
|
193
|
+
# :ssh_timeout => 20
|
194
|
+
# }
|
195
|
+
|
196
|
+
# class << self
|
197
|
+
# alias :__new__ :new
|
198
|
+
|
199
|
+
# def inherited(klass)
|
200
|
+
# class << klass
|
201
|
+
# alias :new :__new__
|
202
|
+
# end
|
203
|
+
# end
|
204
|
+
# end
|
205
|
+
|
206
|
+
# @@registered_provider_classes = {}
|
207
|
+
# def self.register_provider_class(name, driver)
|
208
|
+
# @@registered_provider_classes[name] = driver
|
209
|
+
# end
|
210
|
+
|
211
|
+
# def self.provider_class_for(provider)
|
212
|
+
# require "chef_metal_crowbar/providers/#{provider.downcase}"
|
213
|
+
# @@registered_provider_classes[provider]
|
214
|
+
# end
|
215
|
+
|
216
|
+
# def self.new(driver_url, config)
|
217
|
+
# provider = driver_url.split(':')[1]
|
218
|
+
# provider_class_for(provider).new(driver_url, config)
|
219
|
+
# end
|
220
|
+
|
221
|
+
# def self.canonicalize_url(driver_url, config)
|
222
|
+
# _, provider, id = driver_url.split(':', 3)
|
223
|
+
# config, id = provider_class_for(provider).compute_options_for(provider, id, config)
|
224
|
+
# [ "crowbar:#{provider}:#{id}", config ]
|
225
|
+
# end
|
226
|
+
|
227
|
+
# # Passed in a config which is *not* merged with driver_url (because we don't
|
228
|
+
# # know what it is yet) but which has the same keys
|
229
|
+
# def self.from_provider(provider, config)
|
230
|
+
# provider ||= "core"
|
231
|
+
# # Figure out the options and merge them into the config
|
232
|
+
# config, id = provider_class_for(provider).compute_options_for(provider, nil, config)
|
233
|
+
|
234
|
+
# driver_url = "crowbar:#{provider}:#{id}"
|
235
|
+
|
236
|
+
# ChefMetal.driver_for_url(driver_url, config)
|
237
|
+
# end
|
238
|
+
|
239
|
+
# def compute_options
|
240
|
+
# driver_options[:compute_options].to_hash || {}
|
241
|
+
# end
|
242
|
+
|
243
|
+
# def provider
|
244
|
+
# compute_options[:provider]
|
245
|
+
# end
|
246
|
+
|
247
|
+
# # Acquire a machine, generally by provisioning it. Returns a Machine
|
248
|
+
# # object pointing at the machine, allowing useful actions like setup,
|
249
|
+
# # converge, execute, file and directory.
|
250
|
+
# def allocate_machine(action_handler, machine_spec, machine_options)
|
251
|
+
# # If the server does not exist, create it
|
252
|
+
# create_servers(action_handler, { machine_spec => machine_options }, Chef::ChefFS::Parallelizer.new(0))
|
253
|
+
# machine_spec
|
254
|
+
# end
|
255
|
+
|
256
|
+
# def allocate_machines(action_handler, specs_and_options, parallelizer)
|
257
|
+
# create_servers(action_handler, specs_and_options, parallelizer) do |machine_spec, server|
|
258
|
+
# yield machine_spec
|
259
|
+
# end
|
260
|
+
# specs_and_options.keys
|
261
|
+
# end
|
262
|
+
|
263
|
+
# def ready_machine(action_handler, machine_spec, machine_options)
|
264
|
+
# server = server_for(machine_spec)
|
265
|
+
# if server.nil?
|
266
|
+
# raise "Machine #{machine_spec.name} does not have a server associated with it, or server does not exist."
|
267
|
+
# end
|
268
|
+
|
269
|
+
# # Attach floating IPs if necessary
|
270
|
+
# attach_floating_ips(action_handler, machine_spec, machine_options, server)
|
271
|
+
|
272
|
+
# # Start the server if needed, and wait for it to start
|
273
|
+
# start_server(action_handler, machine_spec, server)
|
274
|
+
# wait_until_ready(action_handler, machine_spec, machine_options, server)
|
275
|
+
# begin
|
276
|
+
# wait_for_transport(action_handler, machine_spec, machine_options, server)
|
277
|
+
# rescue Crowbar::Errors::TimeoutError
|
278
|
+
# # Only ever reboot once, and only if it's been less than 10 minutes since we stopped waiting
|
279
|
+
# if machine_spec.location['started_at'] || remaining_wait_time(machine_spec, machine_options) < -(10*60)
|
280
|
+
# raise
|
281
|
+
# else
|
282
|
+
# # Sometimes (on EC2) the machine comes up but gets stuck or has
|
283
|
+
# # some other problem. If this is the case, we restart the server
|
284
|
+
# # to unstick it. Reboot covers a multitude of sins.
|
285
|
+
# Chef::Log.warn "Machine #{machine_spec.name} (#{server.id} on #{driver_url}) was started but SSH did not come up. Rebooting machine in an attempt to unstick it ..."
|
286
|
+
# restart_server(action_handler, machine_spec, server)
|
287
|
+
# wait_until_ready(action_handler, machine_spec, machine_options, server)
|
288
|
+
# wait_for_transport(action_handler, machine_spec, machine_options, server)
|
289
|
+
# end
|
290
|
+
# end
|
291
|
+
|
292
|
+
# machine_for(machine_spec, machine_options, server)
|
293
|
+
# end
|
294
|
+
|
295
|
+
# # Connect to machine without acquiring it
|
296
|
+
# def connect_to_machine(machine_spec, machine_options)
|
297
|
+
# machine_for(machine_spec, machine_options)
|
298
|
+
# end
|
299
|
+
|
300
|
+
# def destroy_machine(action_handler, machine_spec, machine_options)
|
301
|
+
# server = server_for(machine_spec)
|
302
|
+
# if server
|
303
|
+
# action_handler.perform_action "destroy machine #{machine_spec.name} (#{machine_spec.location['server_id']} at #{driver_url})" do
|
304
|
+
# server.destroy
|
305
|
+
# machine_spec.location = nil
|
306
|
+
# end
|
307
|
+
# end
|
308
|
+
# strategy = convergence_strategy_for(machine_spec, machine_options)
|
309
|
+
# strategy.cleanup_convergence(action_handler, machine_spec)
|
310
|
+
# end
|
311
|
+
|
312
|
+
# def stop_machine(action_handler, machine_spec, machine_options)
|
313
|
+
# server = server_for(machine_spec)
|
314
|
+
# if server
|
315
|
+
# action_handler.perform_action "stop machine #{machine_spec.name} (#{server.id} at #{driver_url})" do
|
316
|
+
# server.stop
|
317
|
+
# end
|
318
|
+
# end
|
319
|
+
# end
|
320
|
+
|
321
|
+
# def compute
|
322
|
+
# @compute ||= Crowbar::Compute.new(compute_options)
|
323
|
+
# end
|
324
|
+
|
325
|
+
# # Not meant to be part of public interface
|
326
|
+
# def transport_for(machine_spec, machine_options, server)
|
327
|
+
# # TODO winrm
|
328
|
+
# create_ssh_transport(machine_spec, machine_options, server)
|
329
|
+
# end
|
330
|
+
|
331
|
+
# protected
|
332
|
+
|
333
|
+
# def option_for(machine_options, key)
|
334
|
+
# machine_options[key] || DEFAULT_OPTIONS[key]
|
335
|
+
# end
|
336
|
+
|
337
|
+
# def creator
|
338
|
+
# raise "unsupported provider #{provider} (please implement #creator)"
|
339
|
+
# end
|
340
|
+
|
341
|
+
# def create_servers(action_handler, specs_and_options, parallelizer, &block)
|
342
|
+
# specs_and_servers = servers_for(specs_and_options.keys)
|
343
|
+
|
344
|
+
# # Get the list of servers which exist, segmented by their bootstrap options
|
345
|
+
# # (we will try to create a set of servers for each set of bootstrap options
|
346
|
+
# # with create_many)
|
347
|
+
# by_bootstrap_options = {}
|
348
|
+
# specs_and_options.each do |machine_spec, machine_options|
|
349
|
+
# server = specs_and_servers[machine_spec]
|
350
|
+
# if server
|
351
|
+
# if %w(terminated archive).include?(server.state) # Can't come back from that
|
352
|
+
# Chef::Log.warn "Machine #{machine_spec.name} (#{server.id} on #{driver_url}) is terminated. Recreating ..."
|
353
|
+
# else
|
354
|
+
# yield machine_spec, server if block_given?
|
355
|
+
# next
|
356
|
+
# end
|
357
|
+
# elsif machine_spec.location
|
358
|
+
# Chef::Log.warn "Machine #{machine_spec.name} (#{machine_spec.location['server_id']} on #{driver_url}) no longer exists. Recreating ..."
|
359
|
+
# end
|
360
|
+
|
361
|
+
# bootstrap_options = bootstrap_options_for(action_handler, machine_spec, machine_options)
|
362
|
+
# by_bootstrap_options[bootstrap_options] ||= []
|
363
|
+
# by_bootstrap_options[bootstrap_options] << machine_spec
|
364
|
+
# end
|
365
|
+
|
366
|
+
# # Create the servers in parallel
|
367
|
+
# parallelizer.parallelize(by_bootstrap_options) do |bootstrap_options, machine_specs|
|
368
|
+
# machine_description = if machine_specs.size == 1
|
369
|
+
# "machine #{machine_specs.first.name}"
|
370
|
+
# else
|
371
|
+
# "machines #{machine_specs.map { |s| s.name }.join(", ")}"
|
372
|
+
# end
|
373
|
+
# description = [ "creating #{machine_description} on #{driver_url}" ]
|
374
|
+
# bootstrap_options.each_pair { |key,value| description << " #{key}: #{value.inspect}" }
|
375
|
+
# action_handler.report_progress description
|
376
|
+
|
377
|
+
# if action_handler.should_perform_actions
|
378
|
+
# # Actually create the servers
|
379
|
+
# create_many_servers(machine_specs.size, bootstrap_options, parallelizer) do |server|
|
380
|
+
|
381
|
+
# # Assign each one to a machine spec
|
382
|
+
# machine_spec = machine_specs.pop
|
383
|
+
# machine_options = specs_and_options[machine_spec]
|
384
|
+
# machine_spec.location = {
|
385
|
+
# 'driver_url' => driver_url,
|
386
|
+
# 'driver_version' => ChefMetalCrowbar::VERSION,
|
387
|
+
# 'server_id' => server.id,
|
388
|
+
# 'creator' => creator,
|
389
|
+
# 'allocated_at' => Time.now.to_i
|
390
|
+
# }
|
391
|
+
# machine_spec.location['key_name'] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
|
392
|
+
# %w(is_windows ssh_username sudo use_private_ip_for_ssh ssh_gateway).each do |key|
|
393
|
+
# machine_spec.location[key] = machine_options[key.to_sym] if machine_options[key.to_sym]
|
394
|
+
# end
|
395
|
+
# action_handler.performed_action "machine #{machine_spec.name} created as #{server.id} on #{driver_url}"
|
396
|
+
|
397
|
+
# yield machine_spec, server if block_given?
|
398
|
+
# end
|
399
|
+
|
400
|
+
# if machine_specs.size > 0
|
401
|
+
# raise "Not all machines were created by create_many_machines!"
|
402
|
+
# end
|
403
|
+
# end
|
404
|
+
# end.to_a
|
405
|
+
# end
|
406
|
+
|
407
|
+
# def create_many_servers(num_servers, bootstrap_options, parallelizer)
|
408
|
+
# parallelizer.parallelize(1.upto(num_servers)) do |i|
|
409
|
+
# server = compute.servers.create(bootstrap_options)
|
410
|
+
# yield server if block_given?
|
411
|
+
# server
|
412
|
+
# end.to_a
|
413
|
+
# end
|
414
|
+
|
415
|
+
# def start_server(action_handler, machine_spec, server)
|
416
|
+
# # If it is stopping, wait for it to get out of "stopping" transition state before starting
|
417
|
+
# if server.state == 'stopping'
|
418
|
+
# action_handler.report_progress "wait for #{machine_spec.name} (#{server.id} on #{driver_url}) to finish stopping ..."
|
419
|
+
# server.wait_for { server.state != 'stopping' }
|
420
|
+
# action_handler.report_progress "#{machine_spec.name} is now stopped"
|
421
|
+
# end
|
422
|
+
|
423
|
+
# if server.state == 'stopped'
|
424
|
+
# action_handler.perform_action "start machine #{machine_spec.name} (#{server.id} on #{driver_url})" do
|
425
|
+
# server.start
|
426
|
+
# machine_spec.location['started_at'] = Time.now.to_i
|
427
|
+
# end
|
428
|
+
# machine_spec.save(action_handler)
|
429
|
+
# end
|
430
|
+
# end
|
431
|
+
|
432
|
+
# def restart_server(action_handler, machine_spec, server)
|
433
|
+
# action_handler.perform_action "restart machine #{machine_spec.name} (#{server.id} on #{driver_url})" do
|
434
|
+
# server.reboot
|
435
|
+
# machine_spec.location['started_at'] = Time.now.to_i
|
436
|
+
# end
|
437
|
+
# machine_spec.save(action_handler)
|
438
|
+
# end
|
439
|
+
|
440
|
+
# def remaining_wait_time(machine_spec, machine_options)
|
441
|
+
# if machine_spec.location['started_at']
|
442
|
+
# timeout = option_for(machine_options, :start_timeout) - (Time.now.utc - parse_time(machine_spec.location['started_at']))
|
443
|
+
# else
|
444
|
+
# timeout = option_for(machine_options, :create_timeout) - (Time.now.utc - parse_time(machine_spec.location['allocated_at']))
|
445
|
+
# end
|
446
|
+
# timeout > 0 ? timeout : 0.01
|
447
|
+
# end
|
448
|
+
|
449
|
+
# def parse_time(value)
|
450
|
+
# if value.is_a?(String)
|
451
|
+
# Time.parse(value)
|
452
|
+
# else
|
453
|
+
# Time.at(value)
|
454
|
+
# end
|
455
|
+
# end
|
456
|
+
|
457
|
+
# def wait_until_ready(action_handler, machine_spec, machine_options, server)
|
458
|
+
# if !server.ready?
|
459
|
+
# if action_handler.should_perform_actions
|
460
|
+
# action_handler.report_progress "waiting for #{machine_spec.name} (#{server.id} on #{driver_url}) to be ready ..."
|
461
|
+
# server.wait_for(remaining_wait_time(machine_spec, machine_options)) { ready? }
|
462
|
+
# action_handler.report_progress "#{machine_spec.name} is now ready"
|
463
|
+
# end
|
464
|
+
# end
|
465
|
+
# end
|
466
|
+
|
467
|
+
# def wait_for_transport(action_handler, machine_spec, machine_options, server)
|
468
|
+
# transport = transport_for(machine_spec, machine_options, server)
|
469
|
+
# if !transport.available?
|
470
|
+
# if action_handler.should_perform_actions
|
471
|
+
# action_handler.report_progress "waiting for #{machine_spec.name} (#{server.id} on #{driver_url}) to be connectable (transport up and running) ..."
|
472
|
+
|
473
|
+
# _self = self
|
474
|
+
|
475
|
+
# server.wait_for(remaining_wait_time(machine_spec, machine_options)) do
|
476
|
+
# transport.available?
|
477
|
+
# end
|
478
|
+
# action_handler.report_progress "#{machine_spec.name} is now connectable"
|
479
|
+
# end
|
480
|
+
# end
|
481
|
+
# end
|
482
|
+
|
483
|
+
# def attach_floating_ips(action_handler, machine_spec, machine_options, server)
|
484
|
+
# # TODO this is not particularly idempotent. OK, it is not idempotent AT ALL. Fix.
|
485
|
+
# if option_for(machine_options, :floating_ip_pool)
|
486
|
+
# Chef::Log.info 'Attaching IP from pool'
|
487
|
+
# action_handler.perform_action "attach floating IP from #{option_for(machine_options, :floating_ip_pool)} pool" do
|
488
|
+
# attach_ip_from_pool(server, option_for(machine_options, :floating_ip_pool))
|
489
|
+
# end
|
490
|
+
# elsif option_for(machine_options, :floating_ip)
|
491
|
+
# Chef::Log.info 'Attaching given IP'
|
492
|
+
# action_handler.perform_action "attach floating IP #{option_for(machine_options, :floating_ip)}" do
|
493
|
+
# attach_ip(server, option_for(machine_options, :allocation_id), option_for(machine_options, :floating_ip))
|
494
|
+
# end
|
495
|
+
# end
|
496
|
+
# end
|
497
|
+
|
498
|
+
# # Attach IP to machine from IP pool
|
499
|
+
# # Code taken from kitchen-openstack driver
|
500
|
+
# # https://github.com/test-kitchen/kitchen-openstack/blob/master/lib/kitchen/driver/openstack.rb#L196-L207
|
501
|
+
# def attach_ip_from_pool(server, pool)
|
502
|
+
# @ip_pool_lock ||= Mutex.new
|
503
|
+
# @ip_pool_lock.synchronize do
|
504
|
+
# Chef::Log.info "Attaching floating IP from <#{pool}> pool"
|
505
|
+
# free_addrs = compute.addresses.collect do |i|
|
506
|
+
# i.ip if i.fixed_ip.nil? and i.instance_id.nil? and i.pool == pool
|
507
|
+
# end.compact
|
508
|
+
# if free_addrs.empty?
|
509
|
+
# raise ActionFailed, "No available IPs in pool <#{pool}>"
|
510
|
+
# end
|
511
|
+
# attach_ip(server, free_addrs[0])
|
512
|
+
# end
|
513
|
+
# end
|
514
|
+
|
515
|
+
# # Attach given IP to machine
|
516
|
+
# # Code taken from kitchen-openstack driver
|
517
|
+
# # https://github.com/test-kitchen/kitchen-openstack/blob/master/lib/kitchen/driver/openstack.rb#L209-L213
|
518
|
+
# def attach_ip(server, allocation_id, ip)
|
519
|
+
# Chef::Log.info "Attaching floating IP <#{ip}>"
|
520
|
+
# compute.associate_address(:instance_id => server.id,
|
521
|
+
# :allocation_id => allocation_id,
|
522
|
+
# :public_ip => ip)
|
523
|
+
# end
|
524
|
+
|
525
|
+
# def symbolize_keys(options)
|
526
|
+
# options.inject({}) do |result,(key,value)|
|
527
|
+
# result[key.to_sym] = value
|
528
|
+
# result
|
529
|
+
# end
|
530
|
+
# end
|
531
|
+
|
532
|
+
# def server_for(machine_spec)
|
533
|
+
# if machine_spec.location
|
534
|
+
# compute.servers.get(machine_spec.location['server_id'])
|
535
|
+
# else
|
536
|
+
# nil
|
537
|
+
# end
|
538
|
+
# end
|
539
|
+
|
540
|
+
# def servers_for(machine_specs)
|
541
|
+
# result = {}
|
542
|
+
# machine_specs.each do |machine_spec|
|
543
|
+
# if machine_spec.location
|
544
|
+
# if machine_spec.location['driver_url'] != driver_url
|
545
|
+
# raise "Switching a machine's driver from #{machine_spec.location['driver_url']} to #{driver_url} for is not currently supported! Use machine :destroy and then re-create the machine on the new driver."
|
546
|
+
# end
|
547
|
+
# result[machine_spec] = compute.servers.get(machine_spec.location['server_id'])
|
548
|
+
# else
|
549
|
+
# result[machine_spec] = nil
|
550
|
+
# end
|
551
|
+
# end
|
552
|
+
# result
|
553
|
+
# end
|
554
|
+
|
555
|
+
# @@metal_default_lock = Mutex.new
|
556
|
+
|
557
|
+
# def overwrite_default_key_willy_nilly(action_handler)
|
558
|
+
# driver = self
|
559
|
+
# updated = @@metal_default_lock.synchronize do
|
560
|
+
# ChefMetal.inline_resource(action_handler) do
|
561
|
+
# crowbar_key_pair 'metal_default' do
|
562
|
+
# driver driver
|
563
|
+
# allow_overwrite true
|
564
|
+
# end
|
565
|
+
# end
|
566
|
+
# end
|
567
|
+
# if updated
|
568
|
+
# # Only warn the first time
|
569
|
+
# Chef::Log.warn("Using metal_default key, which is not shared between machines! It is recommended to create an AWS key pair with the crowbar_key_pair resource, and set :bootstrap_options => { :key_name => <key name> }")
|
570
|
+
# end
|
571
|
+
# 'metal_default'
|
572
|
+
# end
|
573
|
+
|
574
|
+
# def bootstrap_options_for(action_handler, machine_spec, machine_options)
|
575
|
+
# bootstrap_options = symbolize_keys(machine_options[:bootstrap_options] || {})
|
576
|
+
|
577
|
+
# bootstrap_options[:tags] = default_tags(machine_spec, bootstrap_options[:tags] || {})
|
578
|
+
|
579
|
+
# bootstrap_options[:name] ||= machine_spec.name
|
580
|
+
|
581
|
+
# bootstrap_options
|
582
|
+
# end
|
583
|
+
|
584
|
+
# def default_tags(machine_spec, bootstrap_tags = {})
|
585
|
+
# tags = {
|
586
|
+
# 'Name' => machine_spec.name,
|
587
|
+
# 'BootstrapId' => machine_spec.id,
|
588
|
+
# 'BootstrapHost' => Socket.gethostname,
|
589
|
+
# 'BootstrapUser' => Etc.getlogin
|
590
|
+
# }
|
591
|
+
# # User-defined tags override the ones we set
|
592
|
+
# tags.merge(bootstrap_tags)
|
593
|
+
# end
|
594
|
+
|
595
|
+
# def machine_for(machine_spec, machine_options, server = nil)
|
596
|
+
# server ||= server_for(machine_spec)
|
597
|
+
# if !server
|
598
|
+
# raise "Server for node #{machine_spec.name} has not been created!"
|
599
|
+
# end
|
600
|
+
|
601
|
+
# if machine_spec.location['is_windows']
|
602
|
+
# ChefMetal::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec, machine_options, server), convergence_strategy_for(machine_spec, machine_options))
|
603
|
+
# else
|
604
|
+
# ChefMetal::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec, machine_options, server), convergence_strategy_for(machine_spec, machine_options))
|
605
|
+
# end
|
606
|
+
# end
|
607
|
+
|
608
|
+
# def convergence_strategy_for(machine_spec, machine_options)
|
609
|
+
# # Defaults
|
610
|
+
# if !machine_spec.location
|
611
|
+
# return ChefMetal::ConvergenceStrategy::NoConverge.new(machine_options[:convergence_options], config)
|
612
|
+
# end
|
613
|
+
|
614
|
+
# if machine_spec.location['is_windows']
|
615
|
+
# ChefMetal::ConvergenceStrategy::InstallMsi.new(machine_options[:convergence_options], config)
|
616
|
+
# elsif machine_options[:cached_installer] == true
|
617
|
+
# ChefMetal::ConvergenceStrategy::InstallCached.new(machine_options[:convergence_options], config)
|
618
|
+
# else
|
619
|
+
# ChefMetal::ConvergenceStrategy::InstallSh.new(machine_options[:convergence_options], config)
|
620
|
+
# end
|
621
|
+
# end
|
622
|
+
|
623
|
+
# def ssh_options_for(machine_spec, machine_options, server)
|
624
|
+
# result = {
|
625
|
+
# # TODO create a user known hosts file
|
626
|
+
# # :user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
|
627
|
+
# # :paranoid => true,
|
628
|
+
# :auth_methods => [ 'publickey' ],
|
629
|
+
# :keys_only => true,
|
630
|
+
# :host_key_alias => "#{server.id}.#{provider}"
|
631
|
+
# }.merge(machine_options[:ssh_options] || {})
|
632
|
+
# if server.respond_to?(:private_key) && server.private_key
|
633
|
+
# result[:key_data] = [ server.private_key ]
|
634
|
+
# elsif server.respond_to?(:key_name) && server.key_name
|
635
|
+
# key = get_private_key(server.key_name)
|
636
|
+
# if !key
|
637
|
+
# raise "Server has key name '#{server.key_name}', but the corresponding private key was not found locally. Check if the key is in Chef::Config.private_key_paths: #{Chef::Config.private_key_paths.join(', ')}"
|
638
|
+
# end
|
639
|
+
# result[:key_data] = [ key ]
|
640
|
+
# elsif machine_spec.location['key_name']
|
641
|
+
# key = get_private_key(machine_spec.location['key_name'])
|
642
|
+
# if !key
|
643
|
+
# raise "Server was created with key name '#{machine_spec.location['key_name']}', but the corresponding private key was not found locally. Check if the key is in Chef::Config.private_key_paths: #{Chef::Config.private_key_paths.join(', ')}"
|
644
|
+
# end
|
645
|
+
# result[:key_data] = [ key ]
|
646
|
+
# elsif machine_options[:bootstrap_options][:key_path]
|
647
|
+
# result[:key_data] = [ IO.read(machine_options[:bootstrap_options][:key_path]) ]
|
648
|
+
# elsif machine_options[:bootstrap_options][:key_name]
|
649
|
+
# result[:key_data] = [ get_private_key(machine_options[:bootstrap_options][:key_name]) ]
|
650
|
+
# else
|
651
|
+
# # TODO make a way to suggest other keys to try ...
|
652
|
+
# raise "No key found to connect to #{machine_spec.name} (#{machine_spec.location.inspect})!"
|
653
|
+
# end
|
654
|
+
# result
|
655
|
+
# end
|
656
|
+
|
657
|
+
# def default_ssh_username
|
658
|
+
# 'root'
|
659
|
+
# end
|
660
|
+
|
661
|
+
# def create_ssh_transport(machine_spec, machine_options, server)
|
662
|
+
# ssh_options = ssh_options_for(machine_spec, machine_options, server)
|
663
|
+
# username = machine_spec.location['ssh_username'] || default_ssh_username
|
664
|
+
# if machine_options.has_key?(:ssh_username) && machine_options[:ssh_username] != machine_spec.location['ssh_username']
|
665
|
+
# Chef::Log.warn("Server #{machine_spec.name} was created with SSH username #{machine_spec.location['ssh_username']} and machine_options specifies username #{machine_options[:ssh_username]}. Using #{machine_spec.location['ssh_username']}. Please edit the node and change the metal.location.ssh_username attribute if you want to change it.")
|
666
|
+
# end
|
667
|
+
# options = {}
|
668
|
+
# if machine_spec.location[:sudo] || (!machine_spec.location.has_key?(:sudo) && username != 'root')
|
669
|
+
# options[:prefix] = 'sudo '
|
670
|
+
# end
|
671
|
+
|
672
|
+
# remote_host = nil
|
673
|
+
# if machine_spec.location['use_private_ip_for_ssh']
|
674
|
+
# remote_host = server.private_ip_address
|
675
|
+
# elsif !server.public_ip_address
|
676
|
+
# Chef::Log.warn("Server #{machine_spec.name} has no public ip address. Using private ip '#{server.private_ip_address}'. Set driver option 'use_private_ip_for_ssh' => true if this will always be the case ...")
|
677
|
+
# remote_host = server.private_ip_address
|
678
|
+
# elsif server.public_ip_address
|
679
|
+
# remote_host = server.public_ip_address
|
680
|
+
# else
|
681
|
+
# raise "Server #{server.id} has no private or public IP address!"
|
682
|
+
# end
|
683
|
+
|
684
|
+
# #Enable pty by default
|
685
|
+
# options[:ssh_pty_enable] = true
|
686
|
+
# options[:ssh_gateway] = machine_spec.location['ssh_gateway'] if machine_spec.location.has_key?('ssh_gateway')
|
687
|
+
|
688
|
+
# ChefMetal::Transport::SSH.new(remote_host, username, ssh_options, options, config)
|
689
|
+
# end
|
690
|
+
|
691
|
+
# def self.compute_options_for(provider, id, config)
|
692
|
+
# raise "unsupported crowbar provider #{provider}"
|
693
|
+
# end
|
694
|
+
|
695
|
+
end
|
696
|
+
end
|