bcome 0.1.1 → 0.1.2
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/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
|