bcome 1.4.0 → 2.0.0

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bcome.rb +7 -0
  3. data/lib/objects/bcome/version.rb +3 -3
  4. data/lib/objects/bootup.rb +4 -3
  5. data/lib/objects/driver/base.rb +16 -2
  6. data/lib/objects/driver/ec2.rb +11 -2
  7. data/lib/objects/driver/gcp.rb +49 -5
  8. data/lib/objects/driver/gcp/authentication/base.rb +36 -0
  9. data/lib/objects/driver/gcp/authentication/oauth.rb +24 -29
  10. data/lib/objects/driver/gcp/authentication/oauth_client_config.rb +22 -0
  11. data/lib/objects/driver/gcp/authentication/oauth_session_store.rb +22 -0
  12. data/lib/objects/driver/gcp/authentication/service_account.rb +57 -2
  13. data/lib/objects/driver/gcp/authentication/signet/service_account.rb +27 -0
  14. data/lib/objects/driver/gcp/authentication/utilities.rb +42 -0
  15. data/lib/objects/encryptor.rb +83 -0
  16. data/lib/objects/exception/base.rb +10 -3
  17. data/lib/objects/exception/ec2_driver_missing_authorization_keys.rb +11 -0
  18. data/lib/objects/exception/empty_namespace_tree.rb +11 -0
  19. data/lib/objects/exception/gcp_auth_service_account_missing_credentials.rb +11 -0
  20. data/lib/objects/exception/invalid_metadata_encryption_key.rb +1 -1
  21. data/lib/objects/exception/missing_gcp_service_account_credentials_filename.rb +11 -0
  22. data/lib/objects/exception/user_orchestration_error.rb +11 -0
  23. data/lib/objects/initialization/factory.rb +36 -0
  24. data/lib/objects/initialization/structure.rb +18 -0
  25. data/lib/objects/initialization/utils.rb +20 -0
  26. data/lib/objects/loading_bar/handler.rb +1 -1
  27. data/lib/objects/loading_bar/indicator/base.rb +1 -0
  28. data/lib/objects/modules/draw.rb +49 -0
  29. data/lib/objects/modules/tree.rb +157 -0
  30. data/lib/objects/modules/workspace_commands.rb +2 -32
  31. data/lib/objects/modules/workspace_menu.rb +113 -48
  32. data/lib/objects/node/attributes.rb +6 -0
  33. data/lib/objects/node/base.rb +27 -7
  34. data/lib/objects/node/cache_handler.rb +1 -1
  35. data/lib/objects/node/factory.rb +15 -11
  36. data/lib/objects/node/inventory/base.rb +9 -3
  37. data/lib/objects/node/inventory/defined.rb +18 -15
  38. data/lib/objects/node/inventory/merge.rb +9 -1
  39. data/lib/objects/node/inventory/subselect.rb +6 -4
  40. data/lib/objects/node/meta_data_factory.rb +1 -1
  41. data/lib/objects/node/meta_data_loader.rb +2 -2
  42. data/lib/objects/node/resources/inventory.rb +19 -0
  43. data/lib/objects/node/resources/merged.rb +23 -14
  44. data/lib/objects/node/resources/sub_inventory.rb +6 -5
  45. data/lib/objects/node/server/base.rb +35 -22
  46. data/lib/objects/node/server/dynamic/ec2.rb +0 -1
  47. data/lib/objects/node/server/dynamic/gcp.rb +0 -1
  48. data/lib/objects/node/server/static.rb +22 -9
  49. data/lib/objects/orchestration/base.rb +7 -1
  50. data/lib/objects/orchestration/interactive_terraform.rb +10 -16
  51. data/lib/objects/registry/command/external.rb +6 -2
  52. data/lib/objects/registry/command/group.rb +5 -1
  53. data/lib/objects/registry/loader.rb +3 -0
  54. data/lib/objects/ssh/command.rb +4 -8
  55. data/lib/objects/ssh/command_exec.rb +3 -1
  56. data/lib/objects/ssh/connection_wrangler.rb +34 -17
  57. data/lib/objects/ssh/connector.rb +17 -9
  58. data/lib/objects/ssh/driver.rb +7 -18
  59. data/lib/objects/ssh/driver_concerns/connection.rb +3 -11
  60. data/lib/objects/ssh/driver_concerns/functions.rb +7 -7
  61. data/lib/objects/ssh/proxy_chain.rb +19 -0
  62. data/lib/objects/ssh/proxy_chain_link.rb +26 -0
  63. data/lib/objects/ssh/proxy_hop.rb +47 -18
  64. data/lib/objects/ssh/script_exec.rb +9 -11
  65. data/lib/objects/startup.rb +7 -1
  66. data/lib/objects/terraform/output.rb +5 -1
  67. data/lib/objects/workspace.rb +10 -0
  68. data/patches/irb.rb +35 -1
  69. data/patches/string.rb +13 -0
  70. metadata +71 -25
  71. data/lib/objects/driver/static.rb +0 -6
@@ -30,7 +30,7 @@ module Bcome
30
30
  end
31
31
 
32
32
  def enabled_menu_items
33
- super + %i[ssh]
33
+ super + %i[ssh tags]
34
34
  end
35
35
 
36
36
  def menu_items
@@ -38,14 +38,20 @@ module Bcome
38
38
  base_items[:ssh] = {
39
39
  description: 'ssh directly into a resource',
40
40
  usage: 'ssh identifier',
41
- console_only: true
41
+ console_only: true,
42
+ group: :ssh
43
+ }
44
+
45
+ base_items[:tags] = {
46
+ description: 'print out server tags/labels',
47
+ group: :informational
42
48
  }
43
49
 
44
50
  base_items
45
51
  end
46
52
 
47
53
  def resources
48
- @resources ||= ::Bcome::Node::Resources::Inventory.new
54
+ @resources ||= ::Bcome::Node::Resources::Inventory.new(self)
49
55
  end
50
56
 
51
57
  def ssh(identifier = nil)
@@ -6,7 +6,7 @@ module Bcome
6
6
  class Defined < ::Bcome::Node::Inventory::Base
7
7
  include ::Bcome::LoadingBar::Handler
8
8
 
9
- MACHINES_CACHE_PATH = 'machines-cache.yml'
9
+ MACHINES_CACHE_PATH = 'static-cache.yml'
10
10
 
11
11
  attr_reader :dynamic_nodes_loaded
12
12
 
@@ -17,15 +17,22 @@ module Bcome
17
17
  end
18
18
 
19
19
  def enabled_menu_items
20
- super + %i[save reload]
20
+ super + %i[cache reload]
21
21
  end
22
22
 
23
23
  def menu_items
24
24
  base_items = super.dup
25
25
 
26
+ base_items[:cache] = {
27
+ description: 'Cache the current tree state',
28
+ console_only: false,
29
+ group: :miscellany
30
+ }
31
+
26
32
  base_items[:reload] = {
27
- description: "Restock this inventory from remote (hit 'save' after to persist)",
28
- console_only: true
33
+ description: "Restock this inventory from remote (hit 'cache' after to persist)",
34
+ console_only: true,
35
+ group: :miscellany
29
36
  }
30
37
  base_items
31
38
  end
@@ -40,7 +47,9 @@ module Bcome
40
47
  cached_machines = raw_static_machines_from_cache
41
48
 
42
49
  if cached_machines&.any?
43
- wrap_indicator type: :basic, title: 'Loading' + "\sCACHE".bc_blue.bold + "\s" + namespace.to_s.underline, completed_title: '' do
50
+ print "\n"
51
+ title = 'Loading' + "\sCACHE".bc_orange.bold + "\s" + namespace.to_s.underline
52
+ wrap_indicator type: :basic, title: title, completed_title: '' do
44
53
  cached_machines.each do |server_config|
45
54
  resources << ::Bcome::Node::Server::Static.new(views: server_config, parent: self)
46
55
  end
@@ -57,13 +66,7 @@ module Bcome
57
66
  "#{::Bcome::Node::Factory::CONFIG_PATH}/#{MACHINES_CACHE_PATH}"
58
67
  end
59
68
 
60
- def mark_as_cached!
61
- data = ::Bcome::Node::Factory.instance.load_estate_config
62
- data[namespace.to_sym][:load_machines_from_cache] = true
63
- ::Bcome::Node::Factory.instance.rewrite_estate_config(data)
64
- end
65
-
66
- def save
69
+ def cache
67
70
  @answer = ::Bcome::Interactive::Session.run(self,
68
71
  :capture_input, terminal_prompt: 'Are you sure you want to cache these machines (saving will overwrite any previous selections) [Y|N] ? ')
69
72
 
@@ -79,8 +82,7 @@ module Bcome
79
82
  File.open(machines_cache_path, 'w') do |file|
80
83
  file.write data.to_yaml
81
84
  end
82
- mark_as_cached!
83
- puts "\nMachines have been cached for node #{namespace}".informational
85
+ puts "\nMachines have been cached to #{machines_cache_path} for node #{namespace}".informational
84
86
  else
85
87
  puts 'Nothing saved'.warning
86
88
  end
@@ -106,7 +108,8 @@ module Bcome
106
108
 
107
109
  def load_nodes
108
110
  set_static_servers
109
- load_dynamic_nodes unless @load_machines_from_cache
111
+ load_dynamic_nodes unless resources.any?
112
+ nodes_loaded!
110
113
  end
111
114
 
112
115
  def load_dynamic_nodes
@@ -12,7 +12,15 @@ module Bcome
12
12
  end
13
13
 
14
14
  def nodes_loaded?
15
- true
15
+ !contributing_inventories.collect(&:nodes_loaded?).include?(false)
16
+ end
17
+
18
+ def load_nodes
19
+ contributing_inventories.each do |inventory|
20
+ inventory.load_nodes unless inventory.nodes_loaded?
21
+ end
22
+
23
+ resources
16
24
  end
17
25
 
18
26
  def contributing_inventories
@@ -20,7 +20,8 @@ module Bcome
20
20
 
21
21
  base_items[:reload] = {
22
22
  description: 'Restock this inventory from remote',
23
- console_only: true
23
+ console_only: true,
24
+ group: :miscellany
24
25
  }
25
26
  base_items
26
27
  end
@@ -34,7 +35,7 @@ module Bcome
34
35
  end
35
36
 
36
37
  def do_set_resources
37
- ::Bcome::Node::Resources::SubselectInventory.new(parent_inventory: parent_inventory, filters: filters)
38
+ ::Bcome::Node::Resources::SubselectInventory.new(origin_inventory: self, parent_inventory: parent_inventory, filters: filters)
38
39
  end
39
40
 
40
41
  def nodes_loaded?
@@ -42,8 +43,7 @@ module Bcome
42
43
  end
43
44
 
44
45
  def filters
45
- # Flex point for filters, as obviously we need to support more than just ec2 filtering eventually
46
- @views[:filters] || {}
46
+ @views[:sub_filter] || @views[:filters] || {}
47
47
  end
48
48
 
49
49
  def reload
@@ -65,6 +65,8 @@ module Bcome
65
65
  end
66
66
 
67
67
  def load_parent_inventory
68
+ raise ::Bcome::Exception::Generic, "Missing 'subselect_from' attribute on inventory-subselect with config #{@views}" unless @views[:subselect_from]
69
+
68
70
  parent_crumb = @views[:subselect_from]
69
71
  parent = ::Bcome::Node::Factory.instance.bucket[parent_crumb]
70
72
  raise Bcome::Exception::CannotFindSubselectionParent, "for key '#{parent_crumb}'" unless parent
@@ -16,7 +16,7 @@ module Bcome::Node::LocalMetaDataFactory
16
16
  end
17
17
 
18
18
  def raw_metadata
19
- has_parent? ? parent.raw_metadata.merge(metadata_for_namespace) : metadata_for_namespace
19
+ has_parent? ? parent.raw_metadata.deep_merge(metadata_for_namespace) : metadata_for_namespace
20
20
  end
21
21
 
22
22
  def metadata_for_namespace
@@ -30,7 +30,7 @@ module Bcome::Node
30
30
  end
31
31
 
32
32
  def prompt_for_decryption_key
33
- decryption_key_prompt = 'Enter your metadata decryption key: '.informational
33
+ decryption_key_prompt = 'Enter your Metadata key: '.informational
34
34
 
35
35
  print "\n#{decryption_key_prompt}"
36
36
  @decryption_key = STDIN.noecho(&:gets).chomp
@@ -62,7 +62,7 @@ module Bcome::Node
62
62
 
63
63
  begin
64
64
  filedata = load_file_data_for(filepath)
65
- all_meta_data.deep_merge!(filedata)
65
+ all_meta_data.deep_merge!(filedata) if filedata.is_a?(Hash)
66
66
  rescue Psych::SyntaxError => e
67
67
  raise Bcome::Exception::InvalidMetaDataConfig, "Error: #{e.message}"
68
68
  end
@@ -2,7 +2,26 @@
2
2
 
3
3
  module Bcome::Node::Resources
4
4
  class Inventory < Bcome::Node::Resources::Base
5
+ def initialize(inventory)
6
+ @inventory = inventory
7
+ super
8
+ end
9
+
10
+ def set_overrides(inventory, node)
11
+ override_server_identifier(inventory, node)
12
+ node.set_network_configuration_overrides
13
+ end
14
+
15
+ def override_server_identifier(inventory, node)
16
+ if inventory.override_server_identifier?
17
+ node.identifier =~ /#{inventory.override_identifier}/
18
+ node.update_identifier(Regexp.last_match(1)) if Regexp.last_match(1)
19
+ end
20
+ end
21
+
5
22
  def <<(node)
23
+ set_overrides(@inventory, node)
24
+
6
25
  if existing_node = for_identifier(node.identifier)
7
26
  if existing_node.static_server? && node.dynamic_server?
8
27
  # We've got a duplicate, but we'll treat the remote node as authoritative
@@ -5,34 +5,43 @@ module Bcome::Node::Resources
5
5
  def initialize(config)
6
6
  super
7
7
  @inventory = config[:inventory]
8
+ @nodes = []
8
9
  run_select
9
10
  end
10
11
 
11
12
  def run_select
12
- @inventory.contributing_inventories.each { |inventory| inventory.load_nodes unless inventory.nodes_loaded? }
13
- @nodes = @inventory.contributing_inventories.collect { |inv| inv.resources.nodes }.flatten.collect(&:clone)
13
+ @inventory.contributing_inventories.each do |inventory|
14
+ raise ::Bcome::Exception::Generic, "#{inventory.namespace} is not an inventory, and cannot be merged." unless inventory.is_a?(::Bcome::Node::Inventory::Base)
14
15
 
15
- @nodes.map do |node|
16
- node.add_list_attributes(origin: :origin_namespace)
16
+ inventory.load_nodes unless inventory.nodes_loaded?
17
17
  end
18
18
 
19
+ contributing_nodes = @inventory.contributing_inventories.collect { |inv| inv.resources.nodes }.flatten
20
+ dup_nodes(contributing_nodes)
21
+
19
22
  @nodes
20
23
  end
21
24
 
22
- def update_nodes
23
- new_set = []
25
+ def dup_nodes(contributing_nodes)
26
+ contributing_nodes.each do |original_node|
27
+ # Duplicate the node, setting its origin inventory to this one, and
28
+ # resetting its ssh_driver to the original node's driver.
29
+ new_node = original_node.dup_with_new_parent(@inventory)
30
+ set_overrides(@inventory, new_node)
31
+ new_node.ssh_driver = original_node.ssh_driver
32
+
33
+ # Rename the node as contributing inventories may provide duplicate node names
34
+ rename_node_for_merged_inventory(original_node, new_node)
24
35
 
25
- @nodes.collect do |node|
26
- new_node = node.dup_with_new_parent(@inventory)
27
- if @inventory.override_server_identifier?
28
- new_node.identifier =~ /#{@inventory.override_identifier}/
29
- new_node.update_identifier(Regexp.last_match(1)) if Regexp.last_match(1)
30
- end
31
36
  # Register the new node with the registry
32
37
  ::Bcome::Registry::Loader.instance.set_command_group_for_node(new_node)
33
- new_set << new_node
38
+
39
+ @nodes << new_node
34
40
  end
35
- @nodes = new_set
41
+ end
42
+
43
+ def rename_node_for_merged_inventory(original_node, new_node)
44
+ new_node.identifier = original_node.namespace.gsub(':', '_')
36
45
  end
37
46
  end
38
47
  end
@@ -24,10 +24,7 @@ module Bcome::Node::Resources
24
24
 
25
25
  @nodes.collect do |node|
26
26
  new_node = node.dup_with_new_parent(inventory)
27
- if inventory.override_server_identifier?
28
- new_node.identifier =~ /#{inventory.override_identifier}/
29
- new_node.update_identifier(Regexp.last_match(1)) if Regexp.last_match(1)
30
- end
27
+ set_overrides(inventory, new_node)
31
28
 
32
29
  # Register the new node with the registry
33
30
  ::Bcome::Registry::Loader.instance.set_command_group_for_node(new_node)
@@ -53,11 +50,15 @@ module Bcome::Node::Resources
53
50
  end
54
51
 
55
52
  def tag_filters
56
- filters[:by_tag] || filters[:by_label] || {}
53
+ filters[:by_tag] || filters[:by_label] || filters
57
54
  end
58
55
 
59
56
  def parent_inventory
60
57
  @config[:parent_inventory]
61
58
  end
59
+
60
+ def origin_inventory
61
+ @config[:origin_inventory]
62
+ end
62
63
  end
63
64
  end
@@ -1,7 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
- require 'awesome_print'
4
-
5
2
  module Bcome::Node::Server
6
3
  class Base < Bcome::Node::Base
7
4
  attr_reader :origin_object_id
@@ -12,14 +9,21 @@ module Bcome::Node::Server
12
9
  @origin_object_id = object_id
13
10
  end
14
11
 
12
+ def is_same_machine?(other)
13
+ origin_object_id == other.origin_object_id
14
+ end
15
+
15
16
  def host
16
17
  raise 'Should be overidden'
17
18
  end
18
19
 
19
- # override a server namespace's parameters. This enables features such as specific SSH parameters for a specific server, e.g. my use case was a
20
- # single debian box within an ubuntu network, where I needed to access the machine with the 'admin' rather 'ubuntu' username.
21
20
  def set_view_attributes
22
21
  super
22
+ end
23
+
24
+ # Override a server's namespace parameters. Enabled features such as specific SSH config for a particular server, i.e. overidding that of it's parent
25
+ #  inventory namespace.
26
+ def set_network_configuration_overrides
23
27
  overridden_attributes = ::Bcome::Node::Factory.instance.machines_data_for_namespace(namespace.to_sym)
24
28
  overridden_attributes.each do |override_key, override_value|
25
29
  singleton_class.class_eval do
@@ -30,6 +34,10 @@ module Bcome::Node::Server
30
34
  end
31
35
  end
32
36
 
37
+ def local_network?
38
+ defined?(local_network) && local_network
39
+ end
40
+
33
41
  def dup_with_new_parent(new_parent)
34
42
  new_node = clone
35
43
  new_node.update_parent(new_parent)
@@ -44,6 +52,10 @@ module Bcome::Node::Server
44
52
  data_print_from_hash(cloud_tags.data, 'Tags')
45
53
  end
46
54
 
55
+ def tags_h
56
+ cloud_tags.data
57
+ end
58
+
47
59
  def cloud_tags
48
60
  @generated_tags ||= do_generate_cloud_tags
49
61
  end
@@ -82,34 +94,39 @@ module Bcome::Node::Server
82
94
  end
83
95
 
84
96
  def enabled_menu_items
85
- (super + %i[get ssh tags pseudo_tty tunnel]) - %i[enable disable enable! disable!]
97
+ (super + %i[get ssh tags pseudo_tty tunnel]) - %i[workon enable disable enable! disable!]
86
98
  end
87
99
 
88
100
  def menu_items
89
101
  base_items = super.dup
90
102
  base_items[:tags] = {
91
- description: 'print out remote EC2 tags'
103
+ description: 'print out server tags/labels',
104
+ group: :informational
92
105
  }
93
106
  base_items[:ssh] = {
94
- description: 'initiate an ssh connection to this server'
107
+ description: 'initiate an ssh connection to this server',
108
+ group: :ssh
95
109
  }
96
110
  base_items[:get] = {
97
111
  description: 'Download a file or directory',
98
112
  console_only: false,
99
113
  usage: 'get "/remote/path", "/local/path"',
100
- terminal_usage: 'get "/remote/path" "/local/path"'
114
+ terminal_usage: 'get "/remote/path" "/local/path"',
115
+ group: :file
101
116
  }
102
117
  base_items[:pseudo_tty] = {
103
118
  description: 'Invoke a pseudo-tty session',
104
119
  console_only: false,
105
120
  usage: 'pseudo_tty "your command"',
106
- terminal_usage: 'pseudo_tty "your command"'
121
+ terminal_usage: 'pseudo_tty "your command"',
122
+ group: :ssh
107
123
  }
108
124
  base_items[:tunnel] = {
109
125
  description: 'Create a Tunnel over SSH',
110
126
  console_only: false,
111
127
  usage: 'tunnel(local_port, destination_port)',
112
- terminal_usage: 'tunnel local_port destination_port'
128
+ terminal_usage: 'tunnel local_port destination_port',
129
+ group: :ssh
113
130
  }
114
131
 
115
132
  base_items
@@ -186,17 +203,13 @@ module Bcome::Node::Server
186
203
  end
187
204
 
188
205
  def print_ping_result(ping_result = { success: true })
189
- result = {
190
- namespace => {
191
- 'connection' => ping_result[:success] ? 'success' : 'failed',
192
- 'ssh_config' => ssh_driver.pretty_ssh_config
193
- }
194
- }
195
-
196
- result[namespace]['error'] = ping_result[:error].message if !ping_result[:success] && ping_result[:error]
197
- colour = ping_result[:success] ? :green : :red
206
+ result_string = ping_result[:success] ? 'success'.success : 'failed'.error
198
207
 
199
- ap(result, indent: -2, color: { hash: colour, symbol: colour, string: colour, keyword: colour, variable: colour, array: 'cyan' })
208
+ pretty_ping_result = "\n#{namespace.bc_cyan}:\s#{result_string.bold}\n"
209
+ pretty_ping_result += "Error:\s".bc_cyan + "#{ping_result[:error].message.bc_red}\n" if !ping_result[:success] && ping_result[:error]
210
+ # pretty_ping_result += ping_result[:backtrace] if ping_result[:backtrace]
211
+ pretty_ping_result += "config:\s".bc_cyan + JSON.pretty_generate(ssh_driver.pretty_ssh_config)
212
+ puts pretty_ping_result
200
213
  end
201
214
 
202
215
  def add_list_attributes(attrs)
@@ -223,7 +236,7 @@ module Bcome::Node::Server
223
236
  end
224
237
 
225
238
  def cache_data
226
- d = { identifier: identifier }
239
+ d = { identifier: @original_identifier }
227
240
  d[:internal_ip_address] = internal_ip_address if internal_ip_address
228
241
  d[:public_ip_address] = public_ip_address if public_ip_address
229
242
  d[:description] = description if description
@@ -9,7 +9,6 @@ module Bcome::Node::Server::Dynamic
9
9
 
10
10
  def new_from_fog_instance(fog_instance, parent)
11
11
  identifier = fog_instance.tags['Name']
12
- identifier = override_identifier(parent, identifier)
13
12
 
14
13
  params = {
15
14
  identifier: identifier,
@@ -9,7 +9,6 @@ module Bcome::Node::Server::Dynamic
9
9
 
10
10
  def new_from_gcp_instance(gcp_instance, parent)
11
11
  identifier = gcp_instance.name
12
- identifier = override_identifier(parent, identifier)
13
12
 
14
13
  ## For now we support only the first network interface
15
14
  first_interface = gcp_instance.network_interfaces.first