bcome 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/boot.rb +6 -0
- data/lib/bcome/version.rb +1 -1
- data/lib/command.rb +28 -5
- data/lib/context_functions.rb +3 -1
- data/lib/helpers/environment_ssh.rb +21 -8
- data/lib/helpers/instance_command.rb +33 -7
- data/lib/helpers/instance_ssh.rb +19 -5
- data/lib/helpers/selections.rb +4 -4
- data/lib/nodes/instance.rb +16 -0
- data/lib/orchestrator/command_group/base.rb +56 -0
- data/lib/orchestrator/command_group/custom.rb +13 -0
- data/lib/orchestrator/command_group/direct.rb +53 -0
- data/lib/orchestrator/direct_command/group.rb +21 -0
- data/lib/orchestrator/direct_command/instance.rb +19 -0
- data/lib/orchestrator/factory.rb +38 -0
- data/lib/orchestrator/loader.rb +43 -0
- data/lib/orchestrator/node_target/all.rb +14 -0
- data/lib/orchestrator/node_target/all_excluding_roles.rb +14 -0
- data/lib/orchestrator/node_target/all_with_roles.rb +14 -0
- data/lib/orchestrator/node_target/base.rb +43 -0
- data/lib/orchestrator/node_target/single.rb +13 -0
- data/lib/orchestrator/recipe.rb +51 -0
- data/lib/orchestrator/validate_and_set.rb +10 -0
- data/lib/render_irb.rb +3 -0
- data/lib/ssh.rb +12 -11
- data/lib/stack/base.rb +4 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0cd22d6c8adce1637dfdc5471fef725d49029c6
|
4
|
+
data.tar.gz: c700c7444533f603a03fb4c013093fc960c94a37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be8260d1736e4fd6a774084a5658bd4206c808d754df523eeb8e14fb29c6f2d804c21d0b360cf566eab58bcb0370fbf989a6c280c2a0a42b48d0ead1dcfa5d96
|
7
|
+
data.tar.gz: 91b19768fc4c7e8548d34dea211a7d153d6ece8d39f2f0430ee0f84166a3620c65bef1b649a7f7fe2596257b0539bf4d345259ff181e6e12e28f2dc74488eab7
|
data/bin/boot.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
|
2
|
+
|
3
|
+
extensions_init_path = "#{Dir.pwd}/bcome/extensions_initializer.rb"
|
4
|
+
load extensions_init_path if File.exist?(extensions_init_path)
|
5
|
+
|
2
6
|
### Boot up bcome - used by the shell & exterior bcome processes
|
3
7
|
|
4
8
|
unless @no_shell
|
@@ -110,7 +114,9 @@ if quick_contexts.any?
|
|
110
114
|
end
|
111
115
|
|
112
116
|
B_PIN = @context_object
|
117
|
+
RECIPES = ::Bcome::Orchestrator::Loader.new(@context_object.machines)
|
113
118
|
B_PIN.init
|
114
119
|
else
|
120
|
+
RECIPES = ::Bcome::Orchestrator::Loader.new(ESTATE.machines)
|
115
121
|
ESTATE.init
|
116
122
|
end
|
data/lib/bcome/version.rb
CHANGED
data/lib/command.rb
CHANGED
@@ -2,11 +2,14 @@ class ::Bcome::Command
|
|
2
2
|
|
3
3
|
# Wraps a command. Convenient flex-point for logging etc
|
4
4
|
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :command, :stdout, :stderr, :exit_code, :exit_signal, :instance, :bootstrap, :success_exit_codes
|
6
|
+
|
7
|
+
def initialize(params)
|
8
|
+
@command = params[:command]
|
9
|
+
@instance = params[:instance]
|
10
|
+
@bootstrap = params[:bootstrap] # whether to run this in bootstrap mode or not
|
11
|
+
@success_exit_codes = params[:success_exit_codes]
|
6
12
|
|
7
|
-
def initialize(raw_command, host_node)
|
8
|
-
@raw_command = raw_command
|
9
|
-
@host_node = host_node
|
10
13
|
@stdout = ""
|
11
14
|
@stderr = ""
|
12
15
|
@exit_code = nil
|
@@ -17,13 +20,33 @@ class ::Bcome::Command
|
|
17
20
|
return is_success? ? "Success".success : "Failed".failure
|
18
21
|
end
|
19
22
|
|
23
|
+
def ssh_user
|
24
|
+
@bootstrap ? instance.bootstrap_settings[:ssh_user] : instance.ssh_user
|
25
|
+
end
|
26
|
+
|
27
|
+
def bootstrap=(bootstrap_value)
|
28
|
+
@bootstrap = bootstrap_value
|
29
|
+
end
|
30
|
+
|
31
|
+
def ssh_keys
|
32
|
+
@bootstrap ? [instance.bootstrap_settings[:key]] : instance.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
def proxy
|
36
|
+
@bootstrap ? instance.proxy(true) : instance.proxy
|
37
|
+
end
|
38
|
+
|
20
39
|
def output
|
21
40
|
command_output = is_success? ? @stdout : "Exit code: #{@exit_code}\n\nSTDERR: #{@stderr}"
|
22
41
|
return "\n#{command_output}\n"
|
23
42
|
end
|
24
43
|
|
25
44
|
def is_success?
|
26
|
-
exit_code
|
45
|
+
success_exit_codes.include?(exit_code.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
def success_exit_codes
|
49
|
+
return @success_exit_codes ? @success_exit_codes : ["0"]
|
27
50
|
end
|
28
51
|
|
29
52
|
def stdout=(data)
|
data/lib/context_functions.rb
CHANGED
@@ -8,7 +8,9 @@ module Bcome::ContextFunctions
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def command_for_context(command)
|
11
|
-
return false unless context_cmd_functions
|
11
|
+
return false unless context_cmd_functions
|
12
|
+
return false if !context_cmd_functions.any?
|
13
|
+
|
12
14
|
ccfs = context_cmd_functions.select{|ccf| ccf[:command].to_s == command.to_s }
|
13
15
|
raise "Multiple context commands found at context level #{context_breadcrumb} for command key #{command}" if ccfs.size > 1
|
14
16
|
return ccfs.first
|
@@ -1,17 +1,26 @@
|
|
1
1
|
module ::Bcome::EnvironmentSSH
|
2
2
|
|
3
|
-
def proxy
|
3
|
+
def proxy(bootstrap = false)
|
4
4
|
return nil unless ssh_mode_type == ::SSH_JUMP_MODE_IDENTIFIER
|
5
|
-
|
5
|
+
|
6
|
+
unless bootstrap
|
7
|
+
return Net::SSH::Proxy::Command.new(proxy_command)
|
8
|
+
else
|
9
|
+
return Net::SSH::Proxy::Command.new(bootstrap_proxy_command)
|
10
|
+
end
|
6
11
|
end
|
7
12
|
|
8
13
|
def proxy_command
|
9
14
|
return "ssh -W %h:%p #{bastion_ip_address}"
|
10
15
|
end
|
11
16
|
|
12
|
-
def
|
17
|
+
def bootstrap_proxy_command
|
18
|
+
return "ssh -i #{bootstrap_settings[:key]} -o StrictHostKeyChecking=no -W %h:%p #{bootstrap_settings[:ssh_user]}@#{bastion_ip_address}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute_command(commands, instance, bootstrap = false)
|
13
22
|
begin
|
14
|
-
return execute_cmd(commands, instance, proxy)
|
23
|
+
return execute_cmd(commands, instance, proxy(bootstrap), bootstrap)
|
15
24
|
rescue Net::SSH::AuthenticationFailed
|
16
25
|
raise "Could not authenticate connection to #{instance.identifier}".failure
|
17
26
|
rescue Net::SSH::Disconnect
|
@@ -19,14 +28,14 @@ module ::Bcome::EnvironmentSSH
|
|
19
28
|
end
|
20
29
|
end
|
21
30
|
|
22
|
-
def execute_cmd(raw_commands, instance, proxy)
|
31
|
+
def execute_cmd(raw_commands, instance, proxy, bootstrap = false)
|
23
32
|
commands = raw_commands.collect{|raw_command|
|
24
|
-
raw_command.is_a?(::Bcome::Command) ? raw_command : ::Bcome::Command.new(raw_command, instance)
|
33
|
+
raw_command.is_a?(::Bcome::Command) ? raw_command : ::Bcome::Command.new(:command => raw_command, :instance => instance, :bootstrap => bootstrap)
|
25
34
|
}
|
26
35
|
|
27
|
-
ssh = ::Bcome::Ssh.new(commands
|
36
|
+
ssh = ::Bcome::Ssh.new(commands)
|
28
37
|
ssh.execute!
|
29
|
-
return
|
38
|
+
return commands
|
30
39
|
end
|
31
40
|
|
32
41
|
def execute_scp_upload(files, remote_path, instance)
|
@@ -82,6 +91,10 @@ module ::Bcome::EnvironmentSSH
|
|
82
91
|
end
|
83
92
|
|
84
93
|
def bastion_ip_address
|
94
|
+
unless bastion_server
|
95
|
+
raise "Unable to fine the jump host. No instance found with identifier '#{@ssh_mode[:jump_host_identifier]}'. Check your network.yml config for this stack, and ensure that you've got the correct value for key 'jump_host_identifier'"
|
96
|
+
end
|
97
|
+
|
85
98
|
if dynamic_network_lookup?
|
86
99
|
return bastion_server.public_ip_address
|
87
100
|
else
|
@@ -1,24 +1,50 @@
|
|
1
1
|
module ::Bcome::InstanceCommand
|
2
2
|
|
3
|
-
def run(raw_commands)
|
3
|
+
def run(raw_commands, bootstrap = false)
|
4
4
|
commands = raw_commands.is_a?(String) ? [raw_commands] : raw_commands
|
5
|
-
execute_command(commands)
|
5
|
+
return execute_command(commands, bootstrap)
|
6
6
|
end
|
7
7
|
|
8
|
-
def execute_command(commands)
|
9
|
-
|
8
|
+
def execute_command(commands, bootstrap = false)
|
9
|
+
begin
|
10
|
+
return @environment.execute_command(commands, self, bootstrap)
|
11
|
+
rescue
|
12
|
+
unless bootstrap && bootstrap_settings ### Try bootstrap fallback
|
13
|
+
puts "\n BOOTSTRAP MODE".informational
|
14
|
+
bootstrap = true
|
15
|
+
return @environment.execute_command(commands, self, bootstrap)
|
16
|
+
end
|
17
|
+
end
|
10
18
|
end
|
11
19
|
|
12
|
-
def put(local_path, remote_path)
|
20
|
+
def put(local_path, remote_path, bootstrap = false)
|
13
21
|
puts "rsync> #{self.identifier}".informational
|
14
22
|
if @environment.ssh_mode_type == ::SSH_DIRECT_MODE_IDENTIFIER
|
15
|
-
|
23
|
+
## DIRECT MODE
|
24
|
+
rsync_command = bootstrap ? direct_rsync_bootstrap(local_path, remote_path) : direct_rsync(local_path, remote_path)
|
16
25
|
else
|
17
|
-
|
26
|
+
## JUMP HOST MODE
|
27
|
+
rsync_command = bootstrap ? jump_host_rsync_bootstrap(local_path, remote_path) : jump_host_rsync(local_path, remote_path)
|
18
28
|
end
|
19
29
|
run_local(rsync_command)
|
20
30
|
end
|
21
31
|
|
32
|
+
def direct_rsync(local_path, remote_path)
|
33
|
+
"rsync #{rsync_is_sudo}-av #{local_path} #{self.ssh_user}@#{self.ip_address}:#{remote_path}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def jump_host_rsync(local_path, remote_path)
|
37
|
+
"rsync -av -e \"ssh -A #{self.nat_user}@#{@environment.bastion_ip_address} ssh -o StrictHostKeyChecking=no\" #{rsync_is_sudo}#{local_path} #{self.ssh_user}@#{self.ip_address}:#{remote_path}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def direct_rsync_bootstrap(local_path, remote_path)
|
41
|
+
"rsync -i #{bootstrap_settings[:key]} #{rsync_is_sudo}-av #{local_path} #{bootstrap_settings[:ssh_user]}@#{self.ip_address}:#{remote_path}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def jump_host_rsync_bootstrap(local_path, remote_path)
|
45
|
+
"rsync -av -e \"ssh -i #{bootstrap_settings[:key]} -A #{bootstrap_settings[:ssh_user]}@#{@environment.bastion_ip_address} ssh -o StrictHostKeyChecking=no\" #{rsync_is_sudo}#{local_path} #{bootstrap_settings[:ssh_user]}@#{self.ip_address}:#{remote_path}"
|
46
|
+
end
|
47
|
+
|
22
48
|
def get(remote_path, local_path = local_download_path)
|
23
49
|
raise "No local path specified" unless local_path
|
24
50
|
raise "No remote_path specified" unless remote_path
|
data/lib/helpers/instance_ssh.rb
CHANGED
@@ -8,13 +8,14 @@ module ::Bcome::InstanceSsh
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def ssh
|
12
|
-
execute_task(:do_ssh)
|
13
|
-
end
|
14
|
-
|
15
11
|
def do_ssh(command)
|
16
12
|
result = execute_task(:run_local, command)
|
17
|
-
|
13
|
+
unless result
|
14
|
+
if bootstrap_settings
|
15
|
+
puts "\n BOOTSTRAP MODE".informational
|
16
|
+
execute_task(:run_local, non_bootstrapped_ssh_command)
|
17
|
+
end
|
18
|
+
end
|
18
19
|
return result
|
19
20
|
end
|
20
21
|
|
@@ -63,6 +64,19 @@ module ::Bcome::InstanceSsh
|
|
63
64
|
is_jump_ssh? ? (is_ssh_jump_host? ? do_ssh(ssh_jump_command_to_bastion) : do_ssh(ssh_jump_command_to_internal_node)) : do_ssh(direct_ssh_command)
|
64
65
|
end
|
65
66
|
|
67
|
+
def non_bootstrapped_ssh_command
|
68
|
+
command = is_jump_ssh? ? (
|
69
|
+
is_ssh_jump_host? ? (
|
70
|
+
"ssh -i #{bootstrap_settings[:key]} #{bootstrap_settings[:ssh_user]}@#{ip_address}"
|
71
|
+
) : (
|
72
|
+
"ssh -i #{bootstrap_settings[:key]} -t #{bootstrap_settings[:ssh_user]}@#{bastion_ip_address} ssh -t #{ip_address}"
|
73
|
+
)
|
74
|
+
) : (
|
75
|
+
"ssh -i #{bootstrap_settings[:key]} #{bootstrap_settings[:ssh_user]}@#{ip_address}"
|
76
|
+
)
|
77
|
+
return command
|
78
|
+
end
|
79
|
+
|
66
80
|
def is_ssh_jump_host?
|
67
81
|
@role == ::SSH_JUMP_HOST_ROLE_IDENTIFIER
|
68
82
|
end
|
data/lib/helpers/selections.rb
CHANGED
@@ -14,10 +14,10 @@ module ::Bcome::Selections
|
|
14
14
|
end
|
15
15
|
|
16
16
|
## Runs commands over *every* object in the selection
|
17
|
-
def run(raw_commands)
|
17
|
+
def run(raw_commands, bootstrap = false)
|
18
18
|
return unless @objects
|
19
19
|
@objects.each do |object|
|
20
|
-
object.run(raw_commands)
|
20
|
+
object.run(raw_commands, bootstrap)
|
21
21
|
end
|
22
22
|
return
|
23
23
|
end
|
@@ -26,14 +26,14 @@ module ::Bcome::Selections
|
|
26
26
|
!@objects || @objects.empty?
|
27
27
|
end
|
28
28
|
|
29
|
-
def put(local_path, remote_path)
|
29
|
+
def put(local_path, remote_path, bootstrap = false)
|
30
30
|
if no_selections?
|
31
31
|
no_selections_error
|
32
32
|
return
|
33
33
|
end
|
34
34
|
|
35
35
|
@objects.each do |object|
|
36
|
-
object.put(local_path, remote_path)
|
36
|
+
object.put(local_path, remote_path, bootstrap)
|
37
37
|
end
|
38
38
|
return
|
39
39
|
end
|
data/lib/nodes/instance.rb
CHANGED
@@ -16,10 +16,26 @@ module ::Bcome::Node
|
|
16
16
|
@environment.ssh_mode_type == ::SSH_DIRECT_MODE_IDENTIFIER ? public_ip_address : @external_network_interface_address
|
17
17
|
end
|
18
18
|
|
19
|
+
def bootstrap_settings
|
20
|
+
@environment.bootstrap_settings
|
21
|
+
end
|
22
|
+
|
23
|
+
def proxy(bootstrap = false)
|
24
|
+
@environment.proxy(bootstrap)
|
25
|
+
end
|
26
|
+
|
19
27
|
def public_ip_address
|
20
28
|
@public_ip_address
|
21
29
|
end
|
22
30
|
|
31
|
+
def hostname_map
|
32
|
+
return "ip-#{ip_address.to_s.gsub(".","-")}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def ec2_hostname
|
36
|
+
return "#{hostname_map}.ec2.internal"
|
37
|
+
end
|
38
|
+
|
23
39
|
def role
|
24
40
|
@role
|
25
41
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module ::Bcome::Orchestrator::CommandGroup
|
2
|
+
class Base
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def new_of_type_from(json, node_targets, all_machines, direct_command_groups)
|
6
|
+
raise "Missing command type in #{json.inspect}" unless json.has_key?(:type)
|
7
|
+
raise "Invalid command of type #{json[:type]} in #{json.inspect}" unless klass_for_type.keys.include?(json[:type])
|
8
|
+
|
9
|
+
klass = klass_for_type[json[:type]]
|
10
|
+
|
11
|
+
command_group = klass.new(json)
|
12
|
+
command_group.set_machines(node_targets)
|
13
|
+
|
14
|
+
if klass == ::Bcome::Orchestrator::CommandGroup::Direct
|
15
|
+
command_group.direct_command_groups = direct_command_groups
|
16
|
+
end
|
17
|
+
|
18
|
+
return command_group
|
19
|
+
end
|
20
|
+
|
21
|
+
def klass_for_type
|
22
|
+
{
|
23
|
+
"DIRECT" => ::Bcome::Orchestrator::CommandGroup::Direct,
|
24
|
+
"CUSTOM" => ::Bcome::Orchestrator::CommandGroup::Custom
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :machines
|
30
|
+
|
31
|
+
include ::Bcome::Orchestrator::ValidateAndSet
|
32
|
+
|
33
|
+
def initialize(json)
|
34
|
+
validate_and_set(json)
|
35
|
+
end
|
36
|
+
|
37
|
+
def json_attributes
|
38
|
+
raise "json_attributes should be overidden on derived Orchestrator::Command classes"
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_machines(node_targets)
|
42
|
+
@machines ||= node_targets.select{ |nt|
|
43
|
+
nt.target_key == @target_nodes_key
|
44
|
+
}.collect(&:node_selections).flatten!
|
45
|
+
end
|
46
|
+
|
47
|
+
def json_attributes
|
48
|
+
[:target_nodes_key]
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute!(*params)
|
52
|
+
raise "Should be overidden"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ::Bcome::Orchestrator::CommandGroup
|
2
|
+
class Custom < ::Bcome::Orchestrator::CommandGroup::Base
|
3
|
+
|
4
|
+
def json_attributes
|
5
|
+
super + [:bcome_command]
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute!(current_context_machines)
|
9
|
+
::Bcome::Orchestrator::CustomCommand.send(@bcome_command, current_context_machines)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ::Bcome::Orchestrator::CommandGroup
|
2
|
+
class Direct < ::Bcome::Orchestrator::CommandGroup::Base
|
3
|
+
|
4
|
+
attr_reader :commands, :direct_command_groups
|
5
|
+
|
6
|
+
def initialize(*params)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def json_attributes
|
11
|
+
super + [:direct_command_set_key]
|
12
|
+
end
|
13
|
+
|
14
|
+
def direct_command_groups=(groups)
|
15
|
+
@direct_command_groups = groups.select{|dcg| dcg.direct_command_set_identifier == @direct_command_set_key}
|
16
|
+
end
|
17
|
+
|
18
|
+
def commands
|
19
|
+
@commands ||= set_commands
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute!(current_context_machines)
|
23
|
+
# Per command group
|
24
|
+
@direct_command_groups.each do |direct_command_group|
|
25
|
+
# Per unique machine instance
|
26
|
+
current_context_machines.each do |machine|
|
27
|
+
kommands = []
|
28
|
+
direct_command_group.command_instances.flatten.each do |command_instance|
|
29
|
+
kommands << ::Bcome::Command.new(:command => command_instance.cmd, :instance => machine, :bootstrap => command_instance.bootstrap, :success_exit_codes => command_instance.success_exit_codes)
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
execute_commands(kommands)
|
34
|
+
rescue Net::SSH::AuthenticationFailed => e
|
35
|
+
if machine.bootstrap_settings
|
36
|
+
## Fallback to execute commands without bootstrap set
|
37
|
+
kommands.each {|kommand| kommand.bootstrap = true }
|
38
|
+
execute_commands(kommands)
|
39
|
+
else
|
40
|
+
raise e
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute_commands(commands)
|
48
|
+
ssh = ::Bcome::Ssh.new(commands)
|
49
|
+
ssh.execute!
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ::Bcome::Orchestrator::DirectCommand
|
2
|
+
class Group
|
3
|
+
|
4
|
+
include ::Bcome::Orchestrator::ValidateAndSet
|
5
|
+
|
6
|
+
attr_reader :commands, :direct_command_set_identifier, :description
|
7
|
+
|
8
|
+
def initialize(json)
|
9
|
+
validate_and_set(json)
|
10
|
+
end
|
11
|
+
|
12
|
+
def command_instances
|
13
|
+
@command_instances ||= commands.collect{|raw_command| ::Bcome::Orchestrator::DirectCommand::Instance.new(raw_command)}
|
14
|
+
end
|
15
|
+
|
16
|
+
def json_attributes
|
17
|
+
[:direct_command_set_identifier, :commands, :description]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ::Bcome::Orchestrator::DirectCommand
|
2
|
+
class Instance
|
3
|
+
|
4
|
+
include ::Bcome::Orchestrator::ValidateAndSet
|
5
|
+
|
6
|
+
attr_reader :cmd, :bootstrap, :success_exit_codes
|
7
|
+
|
8
|
+
def initialize(json)
|
9
|
+
validate_and_set(json)
|
10
|
+
@bootstrap = json[:bootstrap] ? json[:bootstrap] : false
|
11
|
+
@success_exit_codes = json[:success_exit_codes]
|
12
|
+
end
|
13
|
+
|
14
|
+
def json_attributes
|
15
|
+
[:cmd]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Bcome::Orchestrator
|
2
|
+
module Factory
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def create_recipes_from_json(json, node_targets, all_machines, direct_commands)
|
6
|
+
validate_json_collection(json)
|
7
|
+
return json.collect{|instance_json| ::Bcome::Orchestrator::Recipe.new(instance_json, node_targets, all_machines, direct_commands) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_command_groups_from_json(json, node_targets, all_machines, direct_commands)
|
11
|
+
validate_json_collection(json)
|
12
|
+
return json.collect{|instance_json| ::Bcome::Orchestrator::CommandGroup::Base.new_of_type_from(instance_json, node_targets, all_machines, direct_commands) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_node_targets_from_json(json, machines)
|
16
|
+
validate_json_collection(json)
|
17
|
+
user_defined_targets = json.collect{|instance_json| ::Bcome::Orchestrator::NodeTarget::Base.new_of_type_from(instance_json, machines) }
|
18
|
+
return user_defined_targets + system_node_targets(machines)
|
19
|
+
end
|
20
|
+
|
21
|
+
def system_node_targets(machines)
|
22
|
+
[
|
23
|
+
::Bcome::Orchestrator::NodeTarget::All.new(:node_selections => machines, :target_key => "all")
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_direct_commands_from_json(json)
|
28
|
+
validate_json_collection(json)
|
29
|
+
return json.collect{|instance_json| ::Bcome::Orchestrator::DirectCommand::Group.new(instance_json) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_json_collection(json)
|
33
|
+
raise "Cannot create Orchestrator recipes - expecting a JSON array" unless json.is_a?(Array)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Bcome::Orchestrator
|
2
|
+
class Loader
|
3
|
+
|
4
|
+
def initialize(machines)
|
5
|
+
config_path = "#{Dir.pwd}/bcome/config/orchestration"
|
6
|
+
@recipes_path = "#{config_path}/command_recipes.yml"
|
7
|
+
@machine_targets_path = "#{config_path}/node_target_selectors.yml"
|
8
|
+
@direct_commands_path = "#{config_path}/direct_command_sets.yml"
|
9
|
+
@machines = machines
|
10
|
+
end
|
11
|
+
|
12
|
+
def recipes
|
13
|
+
@recipes ||= ::Bcome::Orchestrator::Factory.create_recipes_from_json(do_load_recipes, node_targets, @machines, direct_commands)
|
14
|
+
end
|
15
|
+
|
16
|
+
def node_targets
|
17
|
+
@node_targets ||= ::Bcome::Orchestrator::Factory.create_node_targets_from_json(do_load_machine_targets, @machines)
|
18
|
+
end
|
19
|
+
|
20
|
+
def direct_commands
|
21
|
+
@direct_commands ||= ::Bcome::Orchestrator::Factory.create_direct_commands_from_json(do_load_direct_commands)
|
22
|
+
end
|
23
|
+
|
24
|
+
def do_load_recipes
|
25
|
+
return json_from_file(@recipes_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def do_load_direct_commands
|
29
|
+
return json_from_file(@direct_commands_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def do_load_machine_targets
|
33
|
+
return json_from_file(@machine_targets_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def json_from_file(file_path)
|
37
|
+
raise "Cannot load file #{file_path}" unless File.exist?(file_path)
|
38
|
+
file_contents = File.read(file_path)
|
39
|
+
return YAML.load(file_contents)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ::Bcome::Orchestrator::NodeTarget
|
2
|
+
class All < ::Bcome::Orchestrator::NodeTarget::Base
|
3
|
+
|
4
|
+
def initialize(params)
|
5
|
+
@node_selections = params[:node_selections]
|
6
|
+
@target_key = params[:target_key]
|
7
|
+
end
|
8
|
+
|
9
|
+
def override_validate_and_set?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ::Bcome::Orchestrator::NodeTarget
|
2
|
+
class AllExcludingRoles < ::Bcome::Orchestrator::NodeTarget::Base
|
3
|
+
|
4
|
+
def set_machines(machines)
|
5
|
+
exclusion_roles = @selectors[:exclusion_roles]
|
6
|
+
raise "No exclusion roles set for node target of type all_excluding_roles" unless exclusion_roles
|
7
|
+
|
8
|
+
@node_selections = machines.select{|machine|
|
9
|
+
!exclusion_roles.include?(machine.role)
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ::Bcome::Orchestrator::NodeTarget
|
2
|
+
class AllWithRoles < ::Bcome::Orchestrator::NodeTarget::Base
|
3
|
+
|
4
|
+
def set_machines(machines)
|
5
|
+
inclusion_roles = @selectors[:inclusion_roles]
|
6
|
+
raise "No exclusion roles set for node target of type all_with_roles" unless inclusion_roles
|
7
|
+
|
8
|
+
@node_selections = machines.select{|machine|
|
9
|
+
inclusion_roles.include?(machine.role)
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ::Bcome::Orchestrator::NodeTarget
|
2
|
+
class Base
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def new_of_type_from(json, machines)
|
6
|
+
raise "Missing node target type in #{json.inspect}" unless json.has_key?(:type)
|
7
|
+
raise "Invalid node target of type #{json[:type]} in #{json.inspect}" unless klass_for_type.keys.include?(json[:type])
|
8
|
+
node_target = klass_for_type[json[:type]].new(json)
|
9
|
+
node_target.set_machines(machines)
|
10
|
+
return node_target
|
11
|
+
end
|
12
|
+
|
13
|
+
def klass_for_type
|
14
|
+
{
|
15
|
+
"single" => ::Bcome::Orchestrator::NodeTarget::Single,
|
16
|
+
"all_excluding_roles" => ::Bcome::Orchestrator::NodeTarget::AllExcludingRoles,
|
17
|
+
"all_with_roles" => ::Bcome::Orchestrator::NodeTarget::AllWithRoles
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
include ::Bcome::Orchestrator::ValidateAndSet
|
23
|
+
|
24
|
+
attr_reader :node_selections, :target_key
|
25
|
+
|
26
|
+
def initialize(json)
|
27
|
+
validate_and_set(json) unless override_validate_and_set?
|
28
|
+
end
|
29
|
+
|
30
|
+
def override_validate_and_set?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def json_attributes
|
35
|
+
[:target_key, :selectors]
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_machines(*params)
|
39
|
+
raise "Override set_machines in derived types - each type has its own distinct selector, and is passed an array of machines available at the current bcome context."
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ::Bcome::Orchestrator::NodeTarget
|
2
|
+
class Single < ::Bcome::Orchestrator::NodeTarget::Base
|
3
|
+
|
4
|
+
def set_machines(machines)
|
5
|
+
machine_role = @selectors[:role]
|
6
|
+
raise "No machine role set for node target of type single" unless machine_role
|
7
|
+
@node_selections = machines.select{|machine| machine.role == machine_role }
|
8
|
+
raise "Multiple machines found with role #{machine_role}. Selection is ambiguous. Maybe you meant to use the 'all_with_roles' node_target type?" if @node_selections.size > 1
|
9
|
+
raise "No machines found with role #{machine_role} for node_target type single." unless @node_selections.any?
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ::Bcome::Orchestrator
|
2
|
+
class Recipe
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def execute!(params)
|
6
|
+
raise "No recipe name provided" unless params && params.is_a?(Hash) && params[:name]
|
7
|
+
recipe = by_identifier(params[:name])
|
8
|
+
raise "No recipe found matching #{params[:name]}" unless recipe
|
9
|
+
recipe.execute!
|
10
|
+
end
|
11
|
+
|
12
|
+
def by_identifier(identifier)
|
13
|
+
all_recipes = RECIPES.recipes
|
14
|
+
return all_recipes.select{|recipe| recipe.recipe_identifier == identifier }.first
|
15
|
+
end
|
16
|
+
|
17
|
+
def as_menu_items
|
18
|
+
::RECIPES.recipes.collect {|recipe| { :command => recipe.recipe_identifier, :description => "(Command recipe): #{recipe.description}" } }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :command_groups, :recipe_identifier, :description
|
24
|
+
|
25
|
+
include ::Bcome::Orchestrator::ValidateAndSet
|
26
|
+
|
27
|
+
def initialize(json, node_targets, all_machines, direct_commands)
|
28
|
+
validate_and_set(json)
|
29
|
+
set_command_groups(json, node_targets, all_machines, direct_commands)
|
30
|
+
end
|
31
|
+
|
32
|
+
def json_attributes
|
33
|
+
[:recipe_identifier, :description]
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute!(current_context_machines)
|
37
|
+
@command_groups.each do |command_group|
|
38
|
+
puts "\nExecuting command group".informational
|
39
|
+
command_group.execute!(current_context_machines)
|
40
|
+
puts "\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_command_groups(json, node_targets, all_machines, direct_commands)
|
45
|
+
commands_json = json[:commands]
|
46
|
+
raise "Expecting commands within an Orchestrator recipe as an Array" unless commands_json.is_a?(Array)
|
47
|
+
@command_groups = ::Bcome::Orchestrator::Factory.create_command_groups_from_json(commands_json, node_targets, all_machines, direct_commands)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module ::Bcome::Orchestrator::ValidateAndSet
|
2
|
+
|
3
|
+
def validate_and_set(json)
|
4
|
+
json_attributes.each{|attribute|
|
5
|
+
raise "Missing attribute #{attribute} in #{json.inspect}" unless json.has_key?(attribute)
|
6
|
+
instance_variable_set("@#{attribute}".to_sym, json[attribute])
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
data/lib/render_irb.rb
CHANGED
@@ -14,6 +14,9 @@ class ::Bcome::RenderIrb
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def menu(menu_items)
|
17
|
+
recipe_menu_items = ::Bcome::Orchestrator::Recipe.as_menu_items
|
18
|
+
menu_items += recipe_menu_items if recipe_menu_items.any?
|
19
|
+
|
17
20
|
menu_str = "\n** Commands **\n\n"
|
18
21
|
menu_str = "\n\The following commands are available: \n\n"
|
19
22
|
menu_items.each do |item|
|
data/lib/ssh.rb
CHANGED
@@ -2,30 +2,31 @@ class ::Bcome::Ssh
|
|
2
2
|
|
3
3
|
attr_reader :commands
|
4
4
|
|
5
|
-
def initialize(commands
|
5
|
+
def initialize(commands)
|
6
6
|
@commands = commands
|
7
|
-
@proxy = proxy
|
8
|
-
@instance = instance
|
9
7
|
end
|
10
8
|
|
11
9
|
def execute!
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
@commands.each do |command|
|
11
|
+
instance = command.instance
|
12
|
+
ssh_user = command.ssh_user
|
13
|
+
ssh_keys = command.ssh_keys
|
14
|
+
proxy = command.proxy
|
16
15
|
|
16
|
+
::Net::SSH.start(instance.ip_address, ssh_user, :proxy => proxy, :keys => ssh_keys, :paranoid => false) do |ssh|
|
17
|
+
puts "(#{instance.identifier})$ #{command.command}".command
|
18
|
+
ssh_exec!(ssh, command)
|
17
19
|
puts command.output
|
18
|
-
|
19
20
|
print command.pretty_result
|
20
|
-
|
21
|
+
print "\n"
|
22
|
+
ssh.close
|
21
23
|
end
|
22
|
-
ssh.close
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
27
|
def ssh_exec!(ssh, command)
|
27
28
|
ssh.open_channel do |channel|
|
28
|
-
channel.exec(command.
|
29
|
+
channel.exec(command.command) do |ch, success|
|
29
30
|
unless success
|
30
31
|
abort "FAILED: couldn't execute command (ssh.channel.exec)"
|
31
32
|
end
|
data/lib/stack/base.rb
CHANGED
@@ -19,6 +19,10 @@ module ::Bcome::Stack
|
|
19
19
|
def method_missing(method_sym, *arguments, &block)
|
20
20
|
if command_for_context = command_for_context(method_sym)
|
21
21
|
execute_command_for_context(command_for_context)
|
22
|
+
elsif recipe = ::Bcome::Orchestrator::Recipe.by_identifier(method_sym.to_s)
|
23
|
+
puts "Executing Command Recipe #{recipe.recipe_identifier}".informational
|
24
|
+
puts "Description: #{recipe.description}".command
|
25
|
+
recipe.execute!(machines)
|
22
26
|
else
|
23
27
|
super
|
24
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bcome
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Guillaume Roderick (Webzakimbo)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -145,6 +145,20 @@ files:
|
|
145
145
|
- lib/nodes/instance.rb
|
146
146
|
- lib/nodes/platform.rb
|
147
147
|
- lib/object.rb
|
148
|
+
- lib/orchestrator/command_group/base.rb
|
149
|
+
- lib/orchestrator/command_group/custom.rb
|
150
|
+
- lib/orchestrator/command_group/direct.rb
|
151
|
+
- lib/orchestrator/direct_command/group.rb
|
152
|
+
- lib/orchestrator/direct_command/instance.rb
|
153
|
+
- lib/orchestrator/factory.rb
|
154
|
+
- lib/orchestrator/loader.rb
|
155
|
+
- lib/orchestrator/node_target/all.rb
|
156
|
+
- lib/orchestrator/node_target/all_excluding_roles.rb
|
157
|
+
- lib/orchestrator/node_target/all_with_roles.rb
|
158
|
+
- lib/orchestrator/node_target/base.rb
|
159
|
+
- lib/orchestrator/node_target/single.rb
|
160
|
+
- lib/orchestrator/recipe.rb
|
161
|
+
- lib/orchestrator/validate_and_set.rb
|
148
162
|
- lib/patches/string.rb
|
149
163
|
- lib/render_irb.rb
|
150
164
|
- lib/scp.rb
|