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,72 @@
1
+ class Object
2
+
3
+ include ::Bcome::CommandHelper
4
+
5
+ def list
6
+ BOOT.send(:list) ### Our starting point in the hierarchy... maybe this is all ultimately configurable?
7
+ end
8
+ alias :ls :list
9
+
10
+ def become_identifier
11
+ ::START_PROMPT
12
+ end
13
+
14
+ def become(object)
15
+ BECOME.set(object, self)
16
+ end
17
+
18
+ def workon(identifier)
19
+ resource = resource_for_identifier(identifier)
20
+
21
+ unless resource
22
+ puts "No matching #{collection_key} for identifier '#{identifier}'. #{available_resources_options_string}".failure
23
+ else
24
+ puts "\\ \nFrom #{resource.reference_key}, working on #{identifier}\n".command
25
+ become(resource)
26
+ end
27
+ end
28
+ alias :w :workon
29
+
30
+ def menu_items
31
+ [
32
+ { :command => "list / ls", :description => "List all available resources at the current context." },
33
+ { :command => "describe", :description => "Describe the resource object at the current context." },
34
+ { :command => "workon' / w", :description => "Select a resource object, and switch to its context.", :usage => "workon 'identifier'" },
35
+ { :command => "exit", :description => "Close the current context" },
36
+ { :command => "exit!", :description => "Close all contexts, and exit Become."},
37
+ { :command => "local", :description => "Execute a shell command on your local machine.", :usage => 'local "command"'}
38
+ ]
39
+ end
40
+
41
+ def menu
42
+ ::RENDER.menu(menu_items)
43
+ end
44
+
45
+ def highlight?
46
+ false ## override in stack objects that should be highlighted within a list, e.g. instance objects at the environment level that have been selected to workon on
47
+ end
48
+
49
+ def available_resources_options_string
50
+ "Please select from one of '#{all_items.join(', ')}'"
51
+ end
52
+
53
+ def describe
54
+ if self.respond_to?(:do_describe)
55
+ message = "\nCollection Key: #{reference_key}\n\nResource: #{self.do_describe}".colorize(:green)
56
+ else
57
+ message = "\nNothing to describe. Use 'list' to see namespace options".headsup unless self.respond_to?(:do_describe)
58
+ end
59
+ puts message
60
+ end
61
+
62
+ def set_irb_prompt(conf)
63
+ conf[:PROMPT][:CUSTOM] = {
64
+ :PROMPT_N => "\e[1m:\e[m ",
65
+ :PROMPT_I => "\e[1m#{BECOME.irb_prompt} >\e[m ",
66
+ :PROMPT_C => "\e[1m#{BECOME.irb_prompt} >\e[m ",
67
+ :RETURN => ::VERBOSE ? "%s \n" : "\n"
68
+ }
69
+ conf[:PROMPT_MODE] = :CUSTOM
70
+ end
71
+
72
+ end
@@ -0,0 +1,43 @@
1
+ class String
2
+
3
+ def informational
4
+ self.colorize(:white).on_green
5
+ end
6
+
7
+ def command
8
+ self.colorize(:black).on_white
9
+ end
10
+
11
+ def failure
12
+ self.colorize(:red).on_white
13
+ end
14
+
15
+ def success
16
+ self.friendly
17
+ end
18
+
19
+ def friendly
20
+ self.colorize(:black).on_green
21
+ end
22
+
23
+ def warning
24
+ self.colorize(:orange).on_black
25
+ end
26
+
27
+ def menu
28
+ self.colorize(:yellow).on_black
29
+ end
30
+
31
+ def headsup
32
+ self.colorize(:yellow).on_black
33
+ end
34
+
35
+ def output
36
+ self
37
+ end
38
+
39
+ def bg_gray
40
+ "\033[47m#{self}\033[0m"
41
+ end
42
+
43
+ end
@@ -0,0 +1,34 @@
1
+ class ::Bcome::RenderIrb
2
+
3
+ def list_items(parent_collection, items)
4
+ if !items || !items.any?
5
+ print "\nNo resources collections found. Maybe you forgot to add any?".headsup
6
+ else
7
+ print "\n #{parent_collection}\n\n".colorize(:green) # are available:\n\n".colorize(:green)
8
+ items.each do |item|
9
+ colour = item.highlight? ? :yellow : :green
10
+ print "* #{item.do_describe}\n".colorize(colour)
11
+ end
12
+ end
13
+ print "\n"
14
+ end
15
+
16
+ def menu(menu_items)
17
+ menu_str = "\n** Commands **\n\n"
18
+ menu_str = "\n\The following commands are available: \n\n"
19
+ menu_items.each do |item|
20
+ command = item[:command]
21
+ description = item[:description]
22
+ usage = item[:usage]
23
+
24
+ menu_str += "> #{command}\n"
25
+ menu_str += "- #{description}\n"
26
+ menu_str += "e.g. #{usage}\n" if usage
27
+
28
+ menu_str += "\n"
29
+ end
30
+ menu_str += "\n\n"
31
+ print menu_str.menu
32
+ end
33
+
34
+ end
@@ -0,0 +1,29 @@
1
+ class ::Bcome::Scp
2
+
3
+ def initialize(files, remote_path, proxy, node)
4
+ @files = files
5
+ @proxy = proxy
6
+ @node = node
7
+ @remote_path = remote_path
8
+ end
9
+
10
+ def upload!
11
+ puts "scp> #{@node.identifier}".informational
12
+ ::Net::SCP.start(@node.ip_address, @node.ssh_user, { :proxy => @proxy, :keys => @node.keys, :paranoid => false }) do |scp|
13
+ @files.each do |local_path|
14
+ name_old = ""
15
+ scp.upload!(local_path, @remote_path, { :recursive => true }) do |ch, name, sent, total|
16
+ log_string = "#{name}: #{sent}/#{total}"
17
+ if name_old == name
18
+ STDOUT.write "\r#{log_string}"
19
+ else
20
+ STDOUT.write "\n\r#{log_string}"
21
+ end
22
+ name_old = name
23
+ end
24
+ end
25
+ end
26
+ puts # we don't control the output here, this moves us down a line:
27
+ end
28
+
29
+ end
@@ -0,0 +1,49 @@
1
+ class ::Bcome::Ssh
2
+
3
+ attr_reader :commands
4
+
5
+ def initialize(commands, proxy, node)
6
+ @commands = commands
7
+ @proxy = proxy
8
+ @node = node
9
+ end
10
+
11
+ def execute!
12
+ ::Net::SSH.start(@node.ip_address, @node.ssh_user, :proxy => @proxy, :keys => @node.keys, :paranoid => false) do |ssh|
13
+ @commands.each do |command|
14
+ puts "(#{@node.identifier}) #{@user}$ #{command.raw_command}".command
15
+ ssh_exec!(ssh, command)
16
+
17
+ puts command.output
18
+
19
+ print command.pretty_result
20
+ puts "\n"
21
+ end
22
+ ssh.close
23
+ end
24
+ end
25
+
26
+ def ssh_exec!(ssh, command)
27
+ ssh.open_channel do |channel|
28
+ channel.exec(command.raw_command) do |ch, success|
29
+ unless success
30
+ abort "FAILED: couldn't execute command (ssh.channel.exec)"
31
+ end
32
+ channel.on_data do |ch,data|
33
+ command.stdout +=data
34
+ end
35
+ channel.on_extended_data do |ch,type,data|
36
+ command.stderr +=data
37
+ end
38
+ channel.on_request("exit-status") do |ch,data|
39
+ command.exit_code = data.read_long
40
+ end
41
+ channel.on_request("exit-signal") do |ch, data|
42
+ command.exit_signal = data.read_long
43
+ end
44
+ end
45
+ end
46
+ ssh.loop
47
+ end
48
+
49
+ end
@@ -0,0 +1,117 @@
1
+ module ::Bcome::Stack
2
+ class Environment
3
+
4
+ include ::Bcome::BecomeObject
5
+ include ::Bcome::Selections
6
+ include ::Bcome::FogHelper
7
+ include ::Bcome::EnvironmentSSH
8
+
9
+ class << self
10
+ def collection_from_config(parent, configuration)
11
+ collection = []
12
+ configuration.each do |meta_data|
13
+ collection << new(meta_data, parent) if meta_data[:network].to_s == parent.identifier.to_s
14
+ end
15
+ return collection
16
+ end
17
+ end
18
+
19
+ attr_reader :identifier, :platform, :meta_data
20
+
21
+ def initialize(meta_data, parent)
22
+ @meta_data = meta_data
23
+ @identifier = meta_data[:environment]
24
+ @platform = parent
25
+ end
26
+
27
+ def menu_items
28
+ super + [
29
+ { :command => "add", :description => "Add a resource you wish to work on.", :usage => "add 'identifier', OR add ['array', 'of', 'identifiers']" },
30
+ { :command => "add!", :description => "Add all resources from the current context." },
31
+ { :command => "remove", :description => "Remove a resource you no longer with to work on.", :usage => "remove 'identifier', OR remove ['array', 'of','identifiers']" },
32
+ { :command => "clear!", :description => "Remove all selected resources." },
33
+ { :command => "selections", :description => "Show all added resources." },
34
+ { :command => "rsync", :description => "Rsync files to all selected resources.", :usage => "rsync 'localpath', 'remotepath'" },
35
+ { :command => "run", :description => "Execute a command on all selected resources", :usage => "run 'command'" },
36
+ { :command => "scp", :description => "SCP files up to all selected resources", :usage => "scp ['array','of', 'file', 'paths'], 'remote_path'" },
37
+ ]
38
+ end
39
+
40
+ def bastion_server
41
+ resources.select{|resource| resource.identifier == @meta_data[:ssh_mode][:jump_host_identifier] }.first
42
+ end
43
+
44
+ def ssh_mode_type
45
+ ssh_mode_type = @meta_data[:ssh_mode][:type]
46
+ raise "Invalid ssh mode type #{ssh_mode_type}. Should be one of #{valid_ssh_modes.join(", ")}".failure unless valid_ssh_modes.include?(ssh_mode_type)
47
+ return @meta_data[:ssh_mode][:type]
48
+ end
49
+
50
+ def ssh_mode_user
51
+ return @meta_data[:ssh_mode][:ssh_user]
52
+ end
53
+
54
+ def valid_ssh_modes
55
+ [::SSH_JUMP_MODE_IDENTIFIER, ::SSH_DIRECT_MODE_IDENTIFIER]
56
+ end
57
+
58
+ def bastion_ip_address
59
+ if dynamic_network_lookup?
60
+ return bastion_server.public_ip_address
61
+ else
62
+ bastion_ip_address = @meta_data[:ssh_mode][:jump_host_ip_address]
63
+ raise "No jump_host_ip_address specified in your configuration.".failure unless bastion_ip_address
64
+ return bastion_ip_address
65
+ end
66
+ end
67
+
68
+ def network_lookup
69
+ raise "Missing network lookup in networks_environment configuration".failure unless @meta_data[:network_lookup]
70
+ return @meta_data[:network_lookup]
71
+ end
72
+
73
+ def network_lookup_type
74
+ type = network_lookup[:type]
75
+ raise "Unknown network lookup type '#{type}" unless ["dynamic", "static"].include?(type)
76
+ return type
77
+ end
78
+
79
+ def dynamic_network_lookup?
80
+ return network_lookup_type == "dynamic"
81
+ end
82
+
83
+ def do_load_resources
84
+ if dynamic_network_lookup?
85
+ return collection_klass.collection_from_fog_data(self, servers)
86
+ else
87
+ path = config_path
88
+ raise "Missing instances.yml configuration for your environment. You've selected a static network lookup, and yet I cannot find #{config_path}".failure unless File.exist?(config_path)
89
+ config = YAML.load_file(path)
90
+ return collection_klass.collection_from_config(self, config)
91
+ end
92
+ end
93
+
94
+ def config_path
95
+ "#{CONFIGS_PATH}/static_instances/#{platform.identifier}_#{identifier}-instances.yml"
96
+ end
97
+
98
+ def do_describe
99
+ desc = "#{identifier}"
100
+ desc += "\t[ Net lookup: #{network_lookup_type}"
101
+ desc += "\t * SSH Mode: #{ssh_mode_type} ]"
102
+ end
103
+
104
+ def collection_klass
105
+ ::INSTANCE_STACK_KLASS
106
+ end
107
+
108
+ def reference_key
109
+ :environments
110
+ end
111
+
112
+ def collection_key
113
+ :instances
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,92 @@
1
+ module ::Bcome::Stack
2
+ class Instance
3
+
4
+ include ::Bcome::BecomeObject
5
+ include ::Bcome::InstanceSsh
6
+ include ::Bcome::InstanceCommand
7
+
8
+ class << self
9
+ def collection_from_fog_data(parent, fog_instances)
10
+ collection = []
11
+ fog_instances.each do |f_instance|
12
+
13
+ meta_data = {
14
+ :identifier => f_instance.tags["Name"],
15
+ :external_network_interface_address => f_instance.private_ip_address,
16
+ :public_ip_address => f_instance.public_ip_address,
17
+ :role => f_instance.tags["function"]
18
+ }
19
+ collection << new(meta_data, parent)
20
+ end
21
+ return collection
22
+ end
23
+
24
+ def collection_from_config(parent, configuration)
25
+ conf_for_env = configuration[:instances]
26
+ collection = []
27
+ conf_for_env.each do |meta_data|
28
+ collection << new(meta_data, parent)
29
+ end
30
+ return collection
31
+ end
32
+ end
33
+
34
+ attr_reader :environment
35
+
36
+ def initialize(meta_data, parent)
37
+ @meta_data = meta_data
38
+ @environment = parent
39
+ end
40
+
41
+ def menu_items
42
+ super + [
43
+ { :command => "rsync", :description => "Rsync files.", :usage => "rsync 'localpath', 'remotepath'" },
44
+ { :command => "run", :description => "Execute a command.", :usage => "run 'command'" },
45
+ { :command => "scp", :description => "SCP files.", :usage => "scp ['array','of', 'file', 'paths'], 'remote_path'" },
46
+ { :command => "ssh", :description => "Initiate an SSH connection." }
47
+ ]
48
+ end
49
+
50
+ def platform
51
+ @environment.platform
52
+ end
53
+
54
+ def responds_to_list?
55
+ false
56
+ end
57
+
58
+ def identifier
59
+ @meta_data[:identifier]
60
+ end
61
+
62
+ def ip_address
63
+ @environment.ssh_mode_type == ::SSH_DIRECT_MODE_IDENTIFIER ? public_ip_address : @meta_data[:external_network_interface_address]
64
+ end
65
+
66
+ def public_ip_address
67
+ @meta_data[:public_ip_address]
68
+ end
69
+
70
+ def role
71
+ @meta_data[:role]
72
+ end
73
+
74
+ def reference_key
75
+ :instances
76
+ end
77
+
78
+ def highlight?
79
+ @environment.object_in_selections?(self)
80
+ end
81
+
82
+ def do_describe
83
+ description = "#{identifier}"
84
+ description += "\n\t* Internal IP #{@meta_data[:external_network_interface_address]}"
85
+ description += "\n\t* External IP #{public_ip_address}" if public_ip_address
86
+ description += "\n\t* #{role}" if role
87
+ description += "\n"
88
+ return description
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,62 @@
1
+ module ::Bcome::Stack
2
+ class Platform
3
+
4
+ include ::Bcome::BecomeObject
5
+
6
+ class << self
7
+ def collection_from_config(parent, configuration)
8
+ return unless configuration
9
+ collection = []
10
+ configuration.each do |conf_data|
11
+ collection << new(conf_data[:name], conf_data)
12
+ end
13
+ return collection
14
+ end
15
+ end
16
+
17
+ attr_reader :identifier
18
+ attr_reader :quick_contexts
19
+
20
+ def initialize(identifier, meta_data)
21
+ @identifier = identifier
22
+ @meta_data = meta_data
23
+ @quick_contexts = @meta_data[:quick_contexts]
24
+ end
25
+
26
+ def menu_items
27
+ super
28
+ end
29
+
30
+ def do_describe
31
+ "#{identifier}\n - #{@meta_data[:description]}\n"
32
+ end
33
+
34
+ def config_path
35
+ "#{CONFIGS_PATH}/network.yml"
36
+ end
37
+
38
+ def collection_klass
39
+ ::ENV_STACK_KLASS
40
+ end
41
+
42
+ def reference_key
43
+ :platforms
44
+ end
45
+
46
+ def collection_key
47
+ :environments
48
+ end
49
+
50
+ def has_quick_contexts?
51
+ return !quick_contexts.nil?
52
+ end
53
+
54
+ def quick_context_for_reference(context_reference)
55
+ return nil unless has_quick_contexts?
56
+ matches = quick_contexts.select{|qc| qc[:ref] == context_reference }
57
+ raise "Multiple quick context matches found on platform #{@identifier} for context key #{context_reference}. Cannot load quick context - selection is ambiguous." if matches.size > 1
58
+ return matches.first
59
+ end
60
+
61
+ end
62
+ end