bcome 0.0.3

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.
@@ -0,0 +1,12 @@
1
+ module ::Bcome::Filter
2
+ class Ec2Filter < ::Bcome::Filter::Base
3
+
4
+ def by_group_on_environment
5
+ env_string = @environment.meta_data[:environment]
6
+ return @resources.select{|resource|
7
+ resource.groups.first == env_string
8
+ }
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ require 'irb'
2
+ require "irb/completion"
3
+ require 'colorize'
4
+ require 'aws-sdk'
5
+ require 'net/scp'
6
+ require 'net/ssh/proxy/command'
7
+ require 'fog'
8
+
9
+ Dir[File.dirname(__FILE__) + "/**/*.rb"].each {|file| require file }
@@ -0,0 +1,3 @@
1
+ module Bcome
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,69 @@
1
+ module ::Bcome::BecomeObject
2
+
3
+ require 'yaml'
4
+
5
+ CONFIGS_PATH = "bcome/config"
6
+
7
+ def identifier
8
+ raise "Missing method 'identifier' - this should uniquely identify your workspace context object."
9
+ end
10
+
11
+ def become_identifier
12
+ "#{previous_workspace_object.send(:become_identifier)}> #{identifier}"
13
+ end
14
+
15
+ def previous_workspace_object=(object)
16
+ @previous_workspace_object = object
17
+ end
18
+
19
+ def previous_workspace_object
20
+ @previous_workspace_object
21
+ end
22
+
23
+ def main_context=(main_context)
24
+ @main_context = main_context
25
+ end
26
+
27
+ def main_context
28
+ @main_context
29
+ end
30
+
31
+ def has_main_context?
32
+ !@main_context.nil?
33
+ end
34
+
35
+ def resources
36
+ @resources ||= do_load_resources
37
+ end
38
+
39
+ def do_load_resources
40
+ config = YAML.load_file(config_path)
41
+ return collection_klass.collection_from_config(self, config)
42
+ end
43
+
44
+ def resource_for_identifier(identifier)
45
+ return nil unless resources
46
+ matches = resources.select{|resource| resource.identifier.to_sym == identifier.to_sym }
47
+ raise "Retrieved more than one match for #{collection_key} '#{identifier}'. Selection is ambiguous" if matches.size > 1
48
+ return matches.first
49
+ end
50
+
51
+ def all_items
52
+ return [] unless resources
53
+ resources.collect(&:identifier)
54
+ end
55
+
56
+ def responds_to_list?
57
+ true
58
+ end
59
+
60
+ def list
61
+ if responds_to_list?
62
+ ::RENDER.list_items(collection_key, resources)
63
+ else
64
+ puts "\nNo list function at this level".headsup
65
+ end
66
+ end
67
+ alias :ls :list
68
+
69
+ end
@@ -0,0 +1,25 @@
1
+ class ::Bcome::Boot
2
+
3
+ include ::Bcome::BecomeObject
4
+
5
+ def config_path
6
+ "#{CONFIGS_PATH}/platform.yml"
7
+ end
8
+
9
+ def collection_klass
10
+ ::PLATFORM_STACK_KLASS
11
+ end
12
+
13
+ def collection_key
14
+ :platforms
15
+ end
16
+
17
+ def to_s
18
+ "Bootup"
19
+ end
20
+
21
+ def become_identifier
22
+ ::START_PROMPT
23
+ end
24
+
25
+ end
@@ -0,0 +1,45 @@
1
+ class ::Bcome::Command
2
+
3
+ # Wraps a command. Convenient flex-point for logging etc
4
+
5
+ attr_reader :raw_command, :host_node, :stdout, :stderr, :exit_code, :exit_signal
6
+
7
+ def initialize(raw_command, host_node)
8
+ @raw_command = raw_command
9
+ @host_node = host_node
10
+ @stdout = ""
11
+ @stderr = ""
12
+ @exit_code = nil
13
+ @exit_signal = nil
14
+ end
15
+
16
+ def pretty_result
17
+ return is_success? ? "Success".success : "Failed".failure
18
+ end
19
+
20
+ def output
21
+ command_output = is_success? ? @stdout : "Exit code: #{@exit_code}\n\nSTDERR: #{@stderr}"
22
+ return "\n#{command_output}\n"
23
+ end
24
+
25
+ def is_success?
26
+ exit_code == 0
27
+ end
28
+
29
+ def stdout=(data)
30
+ @stdout = data #data.output
31
+ end
32
+
33
+ def stderr=(data)
34
+ @stderr = data #data.output
35
+ end
36
+
37
+ def exit_code=(data)
38
+ @exit_code = data
39
+ end
40
+
41
+ def exit_signal(data)
42
+ @exit_signal = data
43
+ end
44
+
45
+ end
@@ -0,0 +1,10 @@
1
+ module ::Bcome::Filter
2
+ class Base
3
+
4
+ def initialize(resources, environment)
5
+ @resources = resources
6
+ @environment = environment
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module Bcome::Filter
2
+ class Ec2Filter < Bcome::Filter::Base
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ module ::Bcome::CommandHelper
2
+
3
+ def run_local(command)
4
+ puts "(local) > #{command}" if ::VERBOSE
5
+ system(command)
6
+ end
7
+ alias :local :run_local
8
+
9
+ end
@@ -0,0 +1,44 @@
1
+ module ::Bcome::EnvironmentSSH
2
+
3
+ def proxy
4
+ return nil unless ssh_mode_type == ::SSH_JUMP_MODE_IDENTIFIER
5
+ return Net::SSH::Proxy::Command.new(proxy_command)
6
+ end
7
+
8
+ def proxy_command
9
+ return "ssh -W %h:%p #{bastion_ip_address}"
10
+ end
11
+
12
+ def execute_command(commands, node)
13
+ begin
14
+ return execute_cmd(commands, node, proxy)
15
+ rescue Net::SSH::AuthenticationFailed
16
+ raise "Could not authenticate connection to #{node.identifier}".failure
17
+ rescue Net::SSH::Disconnect
18
+ raise "SSH connection to #{node.identifier} was disconnected".failure
19
+ end
20
+ end
21
+
22
+ def execute_cmd(raw_commands, node, proxy)
23
+ commands = raw_commands.collect{|raw_command|
24
+ raw_command.is_a?(::Bcome::Command) ? raw_command : ::Bcome::Command.new(raw_command, node)
25
+ }
26
+
27
+ ssh = ::Bcome::Ssh.new(commands, proxy, node)
28
+ ssh.execute!
29
+ return
30
+ end
31
+
32
+ def execute_scp_upload(files, remote_path, node)
33
+ begin
34
+ scp = ::Bcome::Scp.new(files, remote_path, proxy, node)
35
+ scp.upload!
36
+ rescue Net::SSH::AuthenticationFailed
37
+ raise "Could not authenticate connection to #{node.identifier}".failure
38
+ rescue Net::SSH::Disconnect
39
+ raise "SSH connection to #{node.identifier} was disconnected".failure
40
+ end
41
+ end
42
+
43
+
44
+ end
@@ -0,0 +1,54 @@
1
+ module ::Bcome::FogHelper
2
+
3
+ def credentials_key
4
+ network_lookup[:credentials_key]
5
+ end
6
+
7
+ def fog_client
8
+ @fog_client ||= get_fog_client
9
+ end
10
+
11
+ def unfiltered_servers
12
+ @unfiltered_servers ||= fog_client.servers.all(network_lookup[:ec2_server_lookup_filters])
13
+ end
14
+
15
+ def ec2_tags
16
+ network_lookup[:ec2_tag_filter]
17
+ end
18
+
19
+ def servers
20
+ all = unfiltered_servers
21
+ filtered_by_tags = ec2_tags ? filter_by_tags(all) : all
22
+
23
+ custom_filter_method = @meta_data[:network_lookup][:custom_post_process_filter_method]
24
+
25
+ if custom_filter_method
26
+ filter = ::Bcome::Filter::Ec2Filter.new(filtered_by_tags, self)
27
+ raise "Custome filter method #{custom_filter_method} is missing. Make sure you've added it".failure unless filter.respond_to?(custom_filter_method.to_sym)
28
+ return filter.send(custom_filter_method.to_sym)
29
+ else
30
+ return filtered_by_tags
31
+ end
32
+ end
33
+
34
+ def filter_by_tags(instances)
35
+ return instances.select{|instance| instance_matches_tags?(instance)}
36
+ end
37
+
38
+ def instance_matches_tags?(instance)
39
+ tags = instance.tags
40
+ return ec2_tags.select{|key, value| tags[key.to_s] == value }.size == ec2_tags.keys.size
41
+ end
42
+
43
+ private
44
+
45
+ def get_fog_client
46
+ ::Fog.credential = credentials_key
47
+ client = ::Fog::Compute.new(
48
+ :provider => "AWS",
49
+ :region => network_lookup[:provisioning_region]
50
+ )
51
+ return client
52
+ end
53
+
54
+ end
@@ -0,0 +1,27 @@
1
+ module ::Bcome::InstanceCommand
2
+
3
+ def run(raw_commands)
4
+ commands = raw_commands.is_a?(String) ? [raw_commands] : raw_commands
5
+ execute_command(commands)
6
+ end
7
+
8
+ def execute_command(commands)
9
+ @environment.execute_command(commands, self)
10
+ end
11
+
12
+ def rsync(local_path, remote_path)
13
+ puts "rsync> #{self.identifier}".informational
14
+ if @environment.ssh_mode_type == ::SSH_DIRECT_MODE_IDENTIFIER
15
+ rsync_command = "rsync -av #{local_path} #{self.ssh_user}@#{self.ip_address}:#{remote_path}"
16
+ else
17
+ rsync_command = "rsync -av -e \"ssh -A #{self.ssh_user}@#{@environment.bastion_ip_address} ssh -o StrictHostKeyChecking=no\" #{local_path} #{self.ssh_user}@#{self.ip_address}:#{remote_path}"
18
+ end
19
+ run_local(rsync_command)
20
+ end
21
+
22
+ def scp(files, remote_path)
23
+ @environment.execute_scp_upload(files, remote_path, self)
24
+ return
25
+ end
26
+
27
+ end
@@ -0,0 +1,66 @@
1
+ module ::Bcome::InstanceSsh
2
+
3
+ def execute_task(task_name, *args)
4
+ begin
5
+ public_send(task_name, *args)
6
+ rescue ::Seahorse::Client::NetworkingError
7
+ puts "Could not execute '#{task_name}'. Cannot initiate connection - check your network connection and try again.".failure
8
+ end
9
+ end
10
+
11
+ def ssh
12
+ execute_task(:do_ssh)
13
+ end
14
+
15
+ def do_ssh(command)
16
+ 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
18
+ return result
19
+ end
20
+
21
+ def ssh_jump_command_to_bastion
22
+ command = "ssh -o UserKnownHostsFile=/dev/null -i #{ssh_key_path} #{ssh_user}@#{bastion_ip_address}"
23
+ end
24
+
25
+ def ssh_jump_command_to_internal_node
26
+ command = "ssh -o UserKnownHostsFile=/dev/null -i #{ssh_key_path} -o \"ProxyCommand ssh -W %h:%p -i #{ssh_key_path} #{ssh_user}@#{bastion_ip_address}\" #{ssh_user}@#{ip_address}"
27
+ end
28
+
29
+ def direct_ssh_command
30
+ command = "ssh #{environment.ssh_mode_user}@#{public_ip_address}"
31
+ end
32
+
33
+ def ssh_user
34
+ # defined by the environment or we fall back to the local user
35
+ return @environment.ssh_mode_user ? @environment.ssh_mode_user : `whoami`.gsub("\n","")
36
+ end
37
+
38
+ def ssh_key_path
39
+ "~/.ssh/id_rsa"
40
+ end
41
+
42
+ def keys
43
+ [ssh_key_path]
44
+ end
45
+
46
+ def bastion_ip_address
47
+ return @environment.bastion_ip_address
48
+ end
49
+
50
+ def is_direct_ssh?
51
+ environment.ssh_mode_type == ::SSH_DIRECT_MODE_IDENTIFIER
52
+ end
53
+
54
+ def is_jump_ssh?
55
+ environment.ssh_mode_type == ::SSH_JUMP_MODE_IDENTIFIER
56
+ end
57
+
58
+ def ssh
59
+ 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)
60
+ end
61
+
62
+ def is_ssh_jump_host?
63
+ @meta_data["role"] == ::SSH_JUMP_HOST_ROLE_IDENTIFIER
64
+ end
65
+
66
+ end
@@ -0,0 +1,120 @@
1
+ module ::Bcome::Selections
2
+
3
+ def manage_object(method, resource_identifier = nil)
4
+ unless resource_identifier && resource_identifier.is_a?(String)
5
+ print "You must select a resource identified by its name. #{available_resources_options_string}".headsup
6
+ else
7
+ object = find_resource_by_identifier(resource_identifier)
8
+ unless object
9
+ print "Cannot find object with identifier #{resource_identifier} within the resources for this collection. #{available_resources_options_string}".failure unless object
10
+ else
11
+ send(method, object)
12
+ end
13
+ end
14
+ end
15
+
16
+ ## Runs commands over *every* object in the selection
17
+ def run(raw_commands)
18
+ return unless @objects
19
+ @objects.each do |object|
20
+ object.run(raw_commands)
21
+ end
22
+ return
23
+ end
24
+
25
+ def rsync(local_path, remote_path)
26
+ if !@objects || @objects.empty?
27
+ no_selections_error
28
+ return
29
+ end
30
+ @objects.each do |object|
31
+ object.rsync(local_path, remote_path)
32
+ end
33
+ return
34
+ end
35
+
36
+ def scp(files, remote_path)
37
+ if !@objects || @objects.empty?
38
+ no_selections_error
39
+ return
40
+ end
41
+
42
+ return unless @objects
43
+ @objects.each do |object|
44
+ object.scp(files, remote_path)
45
+ end
46
+ return
47
+ end
48
+
49
+ def add(resource_identifier = nil)
50
+ if resource_identifier.is_a?(Array)
51
+ resource_identifier.each do |ri|
52
+ manage_object(:add_object, ri)
53
+ end
54
+ else
55
+ manage_object(:add_object, resource_identifier)
56
+ end
57
+ end
58
+
59
+ def remove(resource_identifier = nil)
60
+ if resource_identifier.is_a?(Array)
61
+ resource_identifier.each do |ri|
62
+ manage_object(:remove_object, ri)
63
+ end
64
+ else
65
+ manage_object(:remove_object, resource_identifier)
66
+ end
67
+ end
68
+
69
+ def add!
70
+ @objects = resources
71
+ puts "All nodes added".informational
72
+ end
73
+
74
+ def clear!
75
+ @objects = []
76
+ selections
77
+ end
78
+
79
+ def find_resource_by_identifier(resource_identifier)
80
+ resources.select{|r| r.identifier == resource_identifier }.first
81
+ end
82
+
83
+ def in_resources?(object)
84
+ resources.include?(object)
85
+ end
86
+
87
+ def add_object(object)
88
+ return if object_in_selections?(object)
89
+ @objects = @objects ? (@objects << object) : [object]
90
+ return
91
+ end
92
+
93
+ def remove_object(object)
94
+ return if !@objects || @objects.empty?
95
+ unless object_in_selections?(object)
96
+ print "\n#{object.identifier} is not within selections\n".headsup
97
+ else
98
+ @objects = @objects - [object]
99
+ end
100
+ return
101
+ end
102
+
103
+ def object_in_selections?(object)
104
+ return false unless @objects
105
+ return @objects.include?(object)
106
+ end
107
+
108
+ def selections
109
+ if !@objects || @objects.empty?
110
+ print "\nNo added nodes.\n".headsup
111
+ else
112
+ print "\nAdded nodes: #{@objects.collect(&:identifier).join(", ")}\n".headsup
113
+ end
114
+ end
115
+
116
+ def no_selections_error
117
+ puts "No nodes selected".informational
118
+ end
119
+
120
+ end