knife-clc 0.0.1 → 0.0.2.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +62 -40
- data/lib/chef/knife/clc_ip_create.rb +1 -0
- data/lib/chef/knife/clc_server_create.rb +25 -528
- data/lib/clc/client.rb +1 -1
- data/lib/knife-clc/async/config_options.rb +18 -0
- data/lib/knife-clc/async.rb +11 -0
- data/lib/knife-clc/base/config_options.rb +26 -0
- data/lib/knife-clc/base.rb +56 -0
- data/lib/knife-clc/bootstrap/bootstrapper.rb +92 -0
- data/lib/knife-clc/bootstrap/config_options.rb +66 -0
- data/lib/knife-clc/bootstrap/connectivity_helper.rb +39 -0
- data/lib/knife-clc/bootstrap/methods/async_linux_package.rb +41 -0
- data/lib/knife-clc/bootstrap/methods/async_windows_package.rb +69 -0
- data/lib/knife-clc/bootstrap/methods/sync_linux_ssh.rb +67 -0
- data/lib/knife-clc/bootstrap/methods/sync_windows_winrm.rb +61 -0
- data/lib/knife-clc/bootstrap/subcommand_loader.rb +18 -0
- data/lib/knife-clc/bootstrap/validator.rb +149 -0
- data/lib/knife-clc/bootstrap.rb +20 -0
- data/lib/knife-clc/cloud_extensions/cloud_adapter.rb +35 -0
- data/lib/knife-clc/cloud_extensions.rb +11 -0
- data/lib/knife-clc/ip_assignment/config_options.rb +29 -0
- data/lib/knife-clc/ip_assignment/ip_assigner.rb +41 -0
- data/lib/knife-clc/ip_assignment/mapper.rb +20 -0
- data/lib/knife-clc/ip_assignment/validator.rb +74 -0
- data/lib/knife-clc/ip_assignment.rb +20 -0
- data/lib/knife-clc/server_launch/config_options.rb +145 -0
- data/lib/knife-clc/server_launch/mapper.rb +40 -0
- data/lib/knife-clc/server_launch/server_launcher.rb +40 -0
- data/lib/knife-clc/server_launch/validator.rb +100 -0
- data/lib/knife-clc/server_launch.rb +21 -0
- data/lib/knife-clc/version.rb +1 -1
- metadata +44 -4
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative 'base/config_options'
|
2
|
+
|
3
|
+
require 'hirb'
|
4
|
+
require 'clc'
|
5
|
+
require 'knife-clc/version'
|
6
|
+
|
7
|
+
module Knife
|
8
|
+
module Clc
|
9
|
+
module Base
|
10
|
+
def self.included(command_class)
|
11
|
+
ConfigOptions.attach(command_class)
|
12
|
+
end
|
13
|
+
|
14
|
+
def connection
|
15
|
+
@connection ||= ::Clc::Client.new(
|
16
|
+
:username => config[:clc_username],
|
17
|
+
:password => config[:clc_password],
|
18
|
+
:endpoint => config[:clc_endpoint],
|
19
|
+
:verbosity => config[:verbosity]
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def context
|
24
|
+
@context ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
$stdout.sync = true
|
29
|
+
|
30
|
+
parse_and_validate_parameters
|
31
|
+
|
32
|
+
if errors.any?
|
33
|
+
show_errors
|
34
|
+
show_usage
|
35
|
+
exit 1
|
36
|
+
else
|
37
|
+
execute
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_and_validate_parameters
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute
|
45
|
+
end
|
46
|
+
|
47
|
+
def errors
|
48
|
+
@errors ||= []
|
49
|
+
end
|
50
|
+
|
51
|
+
def show_errors
|
52
|
+
errors.each { |message| ui.error message }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative 'validator'
|
2
|
+
require_relative 'connectivity_helper'
|
3
|
+
require_relative 'subcommand_loader'
|
4
|
+
|
5
|
+
require_relative 'methods/async_linux_package'
|
6
|
+
require_relative 'methods/async_windows_package'
|
7
|
+
require_relative 'methods/sync_linux_ssh'
|
8
|
+
require_relative 'methods/sync_windows_winrm'
|
9
|
+
|
10
|
+
module Knife
|
11
|
+
module Clc
|
12
|
+
module Bootstrap
|
13
|
+
class Bootstrapper
|
14
|
+
attr_reader :cloud_adapter, :config, :errors
|
15
|
+
|
16
|
+
def initialize(params)
|
17
|
+
@cloud_adapter = params.fetch(:cloud_adapter)
|
18
|
+
@config = params.fetch(:config)
|
19
|
+
@errors = params.fetch(:errors)
|
20
|
+
end
|
21
|
+
|
22
|
+
def sync_bootstrap(server)
|
23
|
+
sync_bootstrap_method.execute(server)
|
24
|
+
end
|
25
|
+
|
26
|
+
def async_bootstrap(launch_parameters)
|
27
|
+
async_bootstrap_method.execute(launch_parameters)
|
28
|
+
end
|
29
|
+
|
30
|
+
def prepare
|
31
|
+
validator.validate
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def validator
|
37
|
+
@validator ||= Validator.new(
|
38
|
+
:connection => cloud_adapter.connection,
|
39
|
+
:config => config,
|
40
|
+
:errors => errors
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def connectivity_helper
|
45
|
+
@connectivity_helper ||= ConnectivityHelper.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def subcommand_loader
|
49
|
+
@subcommand_loader ||= SubcommandLoader.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def sync_bootstrap_method
|
53
|
+
case config[:clc_bootstrap_platform]
|
54
|
+
when 'linux'
|
55
|
+
Methods::SyncLinuxSsh.new(
|
56
|
+
:cloud_adapter => cloud_adapter,
|
57
|
+
:config => config,
|
58
|
+
:connectivity_helper => connectivity_helper,
|
59
|
+
:subcommand_loader => subcommand_loader
|
60
|
+
)
|
61
|
+
when 'windows'
|
62
|
+
Methods::SyncWindowsWinrm.new(
|
63
|
+
:cloud_adapter => cloud_adapter,
|
64
|
+
:config => config,
|
65
|
+
:connectivity_helper => connectivity_helper,
|
66
|
+
:subcommand_loader => subcommand_loader
|
67
|
+
)
|
68
|
+
else
|
69
|
+
raise 'No suitable bootstrap method found'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def async_bootstrap_method
|
74
|
+
case config[:clc_bootstrap_platform]
|
75
|
+
when 'linux'
|
76
|
+
Methods::AsyncLinuxPackage.new(
|
77
|
+
:config => config,
|
78
|
+
:subcommand_loader => subcommand_loader
|
79
|
+
)
|
80
|
+
when 'windows'
|
81
|
+
Methods::AsyncWindowsPackage.new(
|
82
|
+
:config => config,
|
83
|
+
:subcommand_loader => subcommand_loader
|
84
|
+
)
|
85
|
+
else
|
86
|
+
raise 'No suitable bootstrap method found'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'chef/knife/bootstrap'
|
2
|
+
require 'chef/knife/bootstrap_windows_winrm'
|
3
|
+
|
4
|
+
module Knife
|
5
|
+
module Clc
|
6
|
+
module Bootstrap
|
7
|
+
class ConfigOptions
|
8
|
+
def self.attach(command_class)
|
9
|
+
command_class.class_eval do
|
10
|
+
option :clc_bootstrap,
|
11
|
+
:long => '--bootstrap',
|
12
|
+
:description => '[Bootstrap] Bootstrap launched server',
|
13
|
+
:boolean => true,
|
14
|
+
:default => false,
|
15
|
+
:on => :head
|
16
|
+
|
17
|
+
option :clc_bootstrap_private,
|
18
|
+
:long => '--bootstrap-private',
|
19
|
+
:description => '[Bootstrap] Bootstrap from private network. Requires client or SSH gateway to have an access to private network of the server',
|
20
|
+
:boolean => true,
|
21
|
+
:default => false,
|
22
|
+
:on => :head
|
23
|
+
|
24
|
+
option :clc_bootstrap_platform,
|
25
|
+
:long => '--bootstrap-platform PLATFORM',
|
26
|
+
:description => '[Bootstrap] Sets bootstrapping server platform as windows or linux. Derived automatically by default',
|
27
|
+
:on => :head
|
28
|
+
end
|
29
|
+
|
30
|
+
attach_platform_specific_options(command_class)
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO AS: Once bootstrapper has separate platform modules - rework this
|
34
|
+
def self.attach_platform_specific_options(command_class)
|
35
|
+
linux_options = Chef::Knife::Bootstrap.options.dup
|
36
|
+
windows_options = Chef::Knife::BootstrapWindowsWinrm.options.dup
|
37
|
+
|
38
|
+
windows_specific_option_keys = windows_options.keys - linux_options.keys
|
39
|
+
linux_specific_option_keys = linux_options.keys - windows_options.keys
|
40
|
+
|
41
|
+
linux_specific_option_keys.each do |linux_key|
|
42
|
+
description = linux_options[linux_key][:description]
|
43
|
+
linux_options[linux_key][:description] = '(Linux Only) ' + description
|
44
|
+
end
|
45
|
+
|
46
|
+
windows_specific_option_keys.each do |windows_key|
|
47
|
+
description = windows_options[windows_key][:description]
|
48
|
+
windows_options[windows_key][:description] = '(Windows Only) ' + description
|
49
|
+
end
|
50
|
+
|
51
|
+
windows_options.each do |name, settings|
|
52
|
+
settings[:description] = '[Bootstrap] ' + settings[:description]
|
53
|
+
end
|
54
|
+
|
55
|
+
linux_options.each do |name, settings|
|
56
|
+
settings[:description] = '[Bootstrap] ' + settings[:description]
|
57
|
+
end
|
58
|
+
|
59
|
+
command_class.options
|
60
|
+
.merge!(linux_options)
|
61
|
+
.merge!(windows_options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
class ConnectivityHelper
|
5
|
+
def test_tcp(params)
|
6
|
+
host = params.fetch(:host)
|
7
|
+
port = params.fetch(:port)
|
8
|
+
|
9
|
+
socket = TCPSocket.new(host, port)
|
10
|
+
if readable = IO.select([socket], [socket], nil, 5)
|
11
|
+
yield if block_given?
|
12
|
+
true
|
13
|
+
else
|
14
|
+
false
|
15
|
+
end
|
16
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError, Errno::EPERM, Errno::ETIMEDOUT
|
17
|
+
false
|
18
|
+
ensure
|
19
|
+
socket && socket.close
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_ssh_tunnel(params)
|
23
|
+
host = params.fetch(:host)
|
24
|
+
port = params.fetch(:port)
|
25
|
+
gateway = params.fetch(:gateway)
|
26
|
+
|
27
|
+
gateway_user, gateway_host = gateway.split('@')
|
28
|
+
gateway_host, gateway_port = gateway_host.split(':')
|
29
|
+
|
30
|
+
gateway = Net::SSH::Gateway.new(gateway_host, gateway_user, :port => gateway_port || 22)
|
31
|
+
status = false
|
32
|
+
gateway.open(host, port) do |local_tunnel_port|
|
33
|
+
status = test_tcp(:host => 'localhost', :port => local_tunnel_port)
|
34
|
+
end
|
35
|
+
status
|
36
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError, Errno::EPERM, Errno::ETIMEDOUT
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Knife
|
2
|
+
module Clc
|
3
|
+
module Bootstrap
|
4
|
+
module Methods
|
5
|
+
class AsyncLinuxPackage
|
6
|
+
attr_reader :config, :subcommand_loader
|
7
|
+
|
8
|
+
def initialize(params)
|
9
|
+
@config = params.fetch(:config)
|
10
|
+
@subcommand_loader = params.fetch(:subcommand_loader)
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(launch_parameters)
|
14
|
+
launch_parameters['packages'] ||= []
|
15
|
+
launch_parameters['packages'].concat(packages_for_async_bootstrap)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def packages_for_async_bootstrap
|
21
|
+
[{
|
22
|
+
'packageId' => 'a5d9d04369df4276a4f98f2ca7f7872b',
|
23
|
+
'parameters' => {
|
24
|
+
'Mode' => 'Ssh',
|
25
|
+
'Script' => bootstrap_script
|
26
|
+
}
|
27
|
+
}]
|
28
|
+
end
|
29
|
+
|
30
|
+
def bootstrap_script
|
31
|
+
bootstrap_command.render_template
|
32
|
+
end
|
33
|
+
|
34
|
+
def bootstrap_command
|
35
|
+
subcommand_loader.load(:class => Chef::Knife::Bootstrap, :config => config)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'chef/knife/bootstrap_windows_winrm'
|
2
|
+
|
3
|
+
module Knife
|
4
|
+
module Clc
|
5
|
+
module Bootstrap
|
6
|
+
module Methods
|
7
|
+
class AsyncWindowsPackage
|
8
|
+
COMBINED_SCRIPT_PATH = 'C:/bootstrap.bat'
|
9
|
+
LINES_PER_PARTIAL_SCRIPT = 100
|
10
|
+
|
11
|
+
attr_reader :config, :subcommand_loader
|
12
|
+
|
13
|
+
def initialize(params)
|
14
|
+
@config = params.fetch(:config)
|
15
|
+
@subcommand_loader = params.fetch(:subcommand_loader)
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute(launch_parameters)
|
19
|
+
launch_parameters['packages'] ||= []
|
20
|
+
launch_parameters['packages'].concat(packages_for_async_bootstrap)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def packages_for_async_bootstrap
|
26
|
+
split_script(bootstrap_script).map do |partial_script|
|
27
|
+
{
|
28
|
+
'packageId' => 'a5d9d04369df4276a4f98f2ca7f7872b',
|
29
|
+
'parameters' => {
|
30
|
+
'Mode' => 'PowerShell',
|
31
|
+
'Script' => partial_script
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def split_script(script)
|
38
|
+
partial_scripts = script.lines.each_slice(LINES_PER_PARTIAL_SCRIPT).map do |lines|
|
39
|
+
appending_script(lines.join).tap { |script| ensure_windows_newlines(script) }
|
40
|
+
end
|
41
|
+
|
42
|
+
partial_scripts.push(COMBINED_SCRIPT_PATH)
|
43
|
+
end
|
44
|
+
|
45
|
+
def appending_script(script_to_append)
|
46
|
+
"$script = @'\n" +
|
47
|
+
script_to_append +
|
48
|
+
"'@\n" +
|
49
|
+
"$script | out-file #{COMBINED_SCRIPT_PATH} -Append -Encoding ASCII\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
def ensure_windows_newlines(script)
|
53
|
+
script.gsub!("\r\n", "\n")
|
54
|
+
script.gsub!("\n", "\r\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
def bootstrap_command
|
58
|
+
subcommand_loader.load(:class => Chef::Knife::BootstrapWindowsWinrm, :config => config)
|
59
|
+
end
|
60
|
+
|
61
|
+
def bootstrap_script
|
62
|
+
command = bootstrap_command
|
63
|
+
command.render_template(command.load_template(config[:bootstrap_template]))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Knife
|
2
|
+
module Clc
|
3
|
+
module Bootstrap
|
4
|
+
module Methods
|
5
|
+
class SyncLinuxSsh
|
6
|
+
attr_reader :cloud_adapter, :config, :connectivity_helper, :subcommand_loader
|
7
|
+
|
8
|
+
def initialize(params)
|
9
|
+
@cloud_adapter = params.fetch(:cloud_adapter)
|
10
|
+
@config = params.fetch(:config)
|
11
|
+
@connectivity_helper = params.fetch(:connectivity_helper)
|
12
|
+
@subcommand_loader = params.fetch(:subcommand_loader)
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(server)
|
16
|
+
cloud_adapter.ensure_server_powered_on(server)
|
17
|
+
|
18
|
+
fqdn = get_server_fqdn(server)
|
19
|
+
wait_for_sshd(fqdn)
|
20
|
+
|
21
|
+
command = subcommand_loader.load(:class => Chef::Knife::Bootstrap, :config => config)
|
22
|
+
|
23
|
+
username, password = config.values_at(:ssh_user, :ssh_password)
|
24
|
+
unless username && password
|
25
|
+
creds = cloud_adapter.get_server_credentials(server)
|
26
|
+
command.config.merge!(:ssh_user => creds['userName'], :ssh_password => creds['password'])
|
27
|
+
end
|
28
|
+
|
29
|
+
command.name_args = [fqdn]
|
30
|
+
command.config[:chef_node_name] ||= server['name']
|
31
|
+
|
32
|
+
command.run
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def wait_for_sshd(hostname)
|
38
|
+
expire_at = Time.now + 30
|
39
|
+
port = config[:ssh_port] || 22
|
40
|
+
|
41
|
+
if gateway = config[:ssh_gateway]
|
42
|
+
until connectivity_helper.test_ssh_tunnel(:host => hostname, :port => port, :gateway => gateway)
|
43
|
+
raise 'Could not establish tunneled SSH connection with the server' if Time.now > expire_at
|
44
|
+
end
|
45
|
+
else
|
46
|
+
until connectivity_helper.test_tcp(:host => hostname, :port => port)
|
47
|
+
raise 'Could not establish SSH connection with the server' if Time.now > expire_at
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_server_fqdn(server)
|
53
|
+
if indirect_bootstrap?
|
54
|
+
cloud_adapter.get_private_ip(server)
|
55
|
+
else
|
56
|
+
cloud_adapter.get_public_ip(server)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def indirect_bootstrap?
|
61
|
+
config[:clc_bootstrap_private] || config[:ssh_gateway]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Knife
|
2
|
+
module Clc
|
3
|
+
module Bootstrap
|
4
|
+
module Methods
|
5
|
+
class SyncWindowsWinrm
|
6
|
+
attr_reader :cloud_adapter, :config, :connectivity_helper, :subcommand_loader
|
7
|
+
|
8
|
+
def initialize(params)
|
9
|
+
@cloud_adapter = params.fetch(:cloud_adapter)
|
10
|
+
@config = params.fetch(:config)
|
11
|
+
@connectivity_helper = params.fetch(:connectivity_helper)
|
12
|
+
@subcommand_loader = params.fetch(:subcommand_loader)
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(server)
|
16
|
+
cloud_adapter.ensure_server_powered_on(server)
|
17
|
+
|
18
|
+
fqdn = get_server_fqdn(server)
|
19
|
+
wait_for_winrm(fqdn)
|
20
|
+
|
21
|
+
command = subcommand_loader.load(:class => Chef::Knife::BootstrapWindowsWinrm, :config => config)
|
22
|
+
|
23
|
+
username, password = config.values_at(:winrm_user, :winrm_password)
|
24
|
+
unless username && password
|
25
|
+
creds = cloud_adapter.get_server_credentials(server)
|
26
|
+
command.config.merge!(:winrm_user => creds['userName'], :winrm_password => creds['password'])
|
27
|
+
end
|
28
|
+
|
29
|
+
command.name_args = [fqdn]
|
30
|
+
command.config[:chef_node_name] ||= server['name']
|
31
|
+
|
32
|
+
command.run
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def wait_for_winrm(hostname)
|
38
|
+
expire_at = Time.now + 3600
|
39
|
+
port = config[:winrm_port] || 5985
|
40
|
+
|
41
|
+
until connectivity_helper.test_tcp(:host => hostname, :port => port)
|
42
|
+
raise 'Could not establish WinRM connection with the server' if Time.now > expire_at
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_server_fqdn(server)
|
47
|
+
if indirect_bootstrap?
|
48
|
+
cloud_adapter.get_private_ip(server)
|
49
|
+
else
|
50
|
+
cloud_adapter.get_public_ip(server)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def indirect_bootstrap?
|
55
|
+
config[:clc_bootstrap_private]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Knife
|
2
|
+
module Clc
|
3
|
+
module Bootstrap
|
4
|
+
class SubcommandLoader
|
5
|
+
def load(params)
|
6
|
+
klass = params.fetch(:class)
|
7
|
+
config = params.fetch(:config)
|
8
|
+
|
9
|
+
klass.load_deps
|
10
|
+
command = klass.new
|
11
|
+
command.config.merge!(config)
|
12
|
+
command.configure_chef
|
13
|
+
command
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'chef/node'
|
2
|
+
|
3
|
+
module Knife
|
4
|
+
module Clc
|
5
|
+
module Bootstrap
|
6
|
+
class Validator
|
7
|
+
attr_reader :connection, :config, :errors
|
8
|
+
|
9
|
+
def initialize(params)
|
10
|
+
@connection = params.fetch(:connection)
|
11
|
+
@config = params.fetch(:config)
|
12
|
+
@errors = params.fetch(:errors)
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate
|
16
|
+
return unless config[:clc_bootstrap]
|
17
|
+
|
18
|
+
check_chef_server_connectivity
|
19
|
+
|
20
|
+
if config[:clc_bootstrap_platform]
|
21
|
+
validate_bootstrap_platform
|
22
|
+
else
|
23
|
+
check_server_platform
|
24
|
+
end
|
25
|
+
|
26
|
+
check_server_platform
|
27
|
+
if config[:clc_wait]
|
28
|
+
check_bootstrap_connectivity_params
|
29
|
+
else
|
30
|
+
check_bootstrap_node_connectivity_params
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def indirect_bootstrap?
|
37
|
+
config[:clc_bootstrap_private] || config[:ssh_gateway]
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_chef_server_connectivity
|
41
|
+
Chef::Node.list
|
42
|
+
rescue Exception => e
|
43
|
+
errors << 'Could not connect to Chef Server: ' + e.message
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_bootstrap_node_connectivity_params
|
47
|
+
unless Chef::Config[:validation_key]
|
48
|
+
errors << "Validatorless async bootstrap is not supported. Validation key #{Chef::Config[:validation_key]} not found"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_bootstrap_platform
|
53
|
+
unless %w(linux windows).include? config[:clc_bootstrap_platform]
|
54
|
+
errors << "Unsupported bootstrap platform: #{config[:clc_bootstrap_platform]}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_bootstrap_connectivity_params
|
59
|
+
return if indirect_bootstrap?
|
60
|
+
|
61
|
+
if public_ip_requested?
|
62
|
+
check_connectivity_errors
|
63
|
+
else
|
64
|
+
errors << 'Bootstrapping requires public IP access to the server. Ignore this check with --bootstrap-private'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def check_connectivity_errors
|
69
|
+
if config[:clc_bootstrap_platform] == 'windows'
|
70
|
+
errors << "Bootstrapping requires WinRM access to the server" unless winrm_access_requested?
|
71
|
+
else
|
72
|
+
errors << "Bootstrapping requires SSH access to the server" unless ssh_access_requested?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def check_server_platform
|
77
|
+
return unless config[:clc_group] && config[:clc_source_server]
|
78
|
+
|
79
|
+
if template = find_source_template
|
80
|
+
windows_platform = template['osType'] =~ /windows/
|
81
|
+
elsif server = find_source_server
|
82
|
+
windows_platform = server['os'] =~ /windows/
|
83
|
+
end
|
84
|
+
|
85
|
+
if windows_platform
|
86
|
+
config[:clc_bootstrap_platform] = 'windows'
|
87
|
+
else
|
88
|
+
config[:clc_bootstrap_platform] = 'linux'
|
89
|
+
end
|
90
|
+
rescue Clc::CloudExceptions::Error => e
|
91
|
+
errors << "Could not derive server bootstrap platform: #{e.message}. Please set it manually via --bootstrap-platform"
|
92
|
+
end
|
93
|
+
|
94
|
+
def find_source_template
|
95
|
+
group = connection.show_group(config[:clc_group])
|
96
|
+
datacenter_id = group['locationId']
|
97
|
+
connection.list_templates(datacenter_id).find do |template|
|
98
|
+
template['name'] == config[:clc_source_server]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_source_server
|
103
|
+
connection.show_server(config[:clc_source_server])
|
104
|
+
end
|
105
|
+
|
106
|
+
def public_ip_requested?
|
107
|
+
config[:clc_allowed_protocols] && config[:clc_allowed_protocols].any?
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def winrm_access_requested?
|
112
|
+
winrm_port = requested_winrm_port
|
113
|
+
|
114
|
+
config[:clc_allowed_protocols].find do |permission|
|
115
|
+
protocol, from, to = permission.values_at('protocol', 'port', 'portTo')
|
116
|
+
next unless protocol == 'tcp'
|
117
|
+
next unless from
|
118
|
+
|
119
|
+
to ||= from
|
120
|
+
|
121
|
+
Range.new(from, to).include? winrm_port
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def requested_winrm_port
|
126
|
+
(config[:winrm_port] && Integer(config[:winrm_port])) || 5985
|
127
|
+
end
|
128
|
+
|
129
|
+
def ssh_access_requested?
|
130
|
+
ssh_port = requested_ssh_port
|
131
|
+
|
132
|
+
config[:clc_allowed_protocols].find do |permission|
|
133
|
+
protocol, from, to = permission.values_at('protocol', 'port', 'portTo')
|
134
|
+
next unless protocol == 'tcp'
|
135
|
+
next unless from
|
136
|
+
|
137
|
+
to ||= from
|
138
|
+
|
139
|
+
Range.new(from, to).include? ssh_port
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def requested_ssh_port
|
144
|
+
(config[:ssh_port] && Integer(config[:ssh_port])) || 22
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'bootstrap/config_options'
|
2
|
+
require_relative 'bootstrap/bootstrapper'
|
3
|
+
|
4
|
+
module Knife
|
5
|
+
module Clc
|
6
|
+
module Bootstrap
|
7
|
+
def self.included(command_class)
|
8
|
+
ConfigOptions.attach(command_class)
|
9
|
+
end
|
10
|
+
|
11
|
+
def bootstrapper
|
12
|
+
@bootstrapper = Bootstrapper.new(
|
13
|
+
:cloud_adapter => cloud_adapter,
|
14
|
+
:config => config,
|
15
|
+
:errors => errors
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|