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 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