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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a78ef0e9b999c6dc1c605b73791e5d853be3e5fa
4
- data.tar.gz: 5585b40b46f2f0b962893161aaa566a698d8ddfd
3
+ metadata.gz: d0cd22d6c8adce1637dfdc5471fef725d49029c6
4
+ data.tar.gz: c700c7444533f603a03fb4c013093fc960c94a37
5
5
  SHA512:
6
- metadata.gz: 4dc3a5c6bfeb933f670a63865cd57f5f697373c6793328a74d46d17c453bbabaf023379da4a16fbb97b1116747c769af5c800aad817f0a37770715fc207970fe
7
- data.tar.gz: 0315e88f28e40b7d65fe161b4d982df9a404bb8f674a52c1e003beebc83d75e8df870b7f649a89d5b8108d8ec54c6cd8098f249ff991cc267334e9b346182b7e
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
@@ -1,3 +1,3 @@
1
1
  module Bcome
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
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 :raw_command, :host_node, :stdout, :stderr, :exit_code, :exit_signal
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 == 0
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)
@@ -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 || !context_cmd_functions.any?
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
- return Net::SSH::Proxy::Command.new(proxy_command)
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 execute_command(commands, instance)
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, proxy, instance)
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
- @environment.execute_command(commands, self)
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
- rsync_command = "rsync #{rsync_is_sudo}-av #{local_path} #{self.ssh_user}@#{self.ip_address}:#{remote_path}"
23
+ ## DIRECT MODE
24
+ rsync_command = bootstrap ? direct_rsync_bootstrap(local_path, remote_path) : direct_rsync(local_path, remote_path)
16
25
  else
17
- rsync_command = "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}"
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
@@ -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
- raise "Unable to execute result" unless result # TODO node has not been bootstrapped most likely. Implement a fallback
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
@@ -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
@@ -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, proxy, instance)
5
+ def initialize(commands)
6
6
  @commands = commands
7
- @proxy = proxy
8
- @instance = instance
9
7
  end
10
8
 
11
9
  def execute!
12
- ::Net::SSH.start(@instance.ip_address, @instance.ssh_user, :proxy => @proxy, :keys => @instance.keys, :paranoid => false) do |ssh|
13
- @commands.each do |command|
14
- puts "(#{@instance.identifier}) #{@user}$ #{command.raw_command}".command
15
- ssh_exec!(ssh, command)
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
- puts "\n"
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.raw_command) do |ch, success|
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.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-02 00:00:00.000000000 Z
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