bcome 1.3.4 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +5 -5
  2. data/bin/bcome +13 -8
  3. data/lib/bcome.rb +14 -11
  4. data/lib/objects/bcome/version.rb +19 -1
  5. data/lib/objects/bootup.rb +16 -7
  6. data/lib/objects/command/local.rb +2 -0
  7. data/lib/objects/config_factory.rb +3 -0
  8. data/lib/objects/driver/base.rb +52 -6
  9. data/lib/objects/driver/bucket.rb +6 -4
  10. data/lib/objects/driver/ec2.rb +45 -5
  11. data/lib/objects/driver/gcp.rb +168 -0
  12. data/lib/objects/driver/gcp/authentication/api_key.rb +6 -0
  13. data/lib/objects/driver/gcp/authentication/base.rb +36 -0
  14. data/lib/objects/driver/gcp/authentication/oauth.rb +96 -0
  15. data/lib/objects/driver/gcp/authentication/oauth_client_config.rb +22 -0
  16. data/lib/objects/driver/gcp/authentication/oauth_session_store.rb +22 -0
  17. data/lib/objects/driver/gcp/authentication/service_account.rb +62 -0
  18. data/lib/objects/driver/gcp/authentication/signet/service_account.rb +27 -0
  19. data/lib/objects/driver/gcp/authentication/utilities.rb +42 -0
  20. data/lib/objects/encryptor.rb +109 -24
  21. data/lib/objects/exception/argument_error_invoking_method_from_command_line.rb +8 -4
  22. data/lib/objects/exception/base.rb +21 -10
  23. data/lib/objects/exception/can_only_subselect_on_inventory.rb +8 -4
  24. data/lib/objects/exception/cannot_authenticate_to_gcp.rb +11 -0
  25. data/lib/objects/exception/cannot_find_internal_registry_klass.rb +8 -4
  26. data/lib/objects/exception/cannot_find_inventory.rb +11 -0
  27. data/lib/objects/exception/cannot_find_subselection_parent.rb +8 -4
  28. data/lib/objects/exception/cant_find_key_in_cloud_tags.rb +8 -4
  29. data/lib/objects/exception/cant_find_key_in_metadata.rb +8 -4
  30. data/lib/objects/exception/cant_find_proxy_host_by_identifier.rb +8 -4
  31. data/lib/objects/exception/cant_find_proxy_host_by_namespace.rb +8 -4
  32. data/lib/objects/exception/could_not_initiate_ssh_connection.rb +8 -4
  33. data/lib/objects/exception/could_not_initiate_ssh_connection_through_backend_proxy.rb +8 -4
  34. data/lib/objects/exception/could_not_retrieve_terraform_output.rb +11 -0
  35. data/lib/objects/exception/deprecation_warning.rb +9 -7
  36. data/lib/objects/exception/duplicate_command_line_argument_key.rb +8 -4
  37. data/lib/objects/exception/ec2_driver_missing_authorization_keys.rb +11 -0
  38. data/lib/objects/exception/ec2_driver_missing_provisioning_region.rb +8 -4
  39. data/lib/objects/exception/empty_namespace_tree.rb +11 -0
  40. data/lib/objects/exception/failed_to_run_local_command.rb +8 -4
  41. data/lib/objects/exception/gcp_auth_service_account_missing_credentials.rb +11 -0
  42. data/lib/objects/exception/generic.rb +11 -0
  43. data/lib/objects/exception/interactive_session_halt.rb +6 -2
  44. data/lib/objects/exception/invalid_bcome_breadcrumb.rb +8 -4
  45. data/lib/objects/exception/invalid_breadcrumb.rb +8 -4
  46. data/lib/objects/exception/invalid_context_command.rb +8 -4
  47. data/lib/objects/exception/invalid_gcp_authentication_scheme.rb +11 -0
  48. data/lib/objects/exception/invalid_identifier.rb +8 -4
  49. data/lib/objects/exception/invalid_machines_cache_config.rb +8 -4
  50. data/lib/objects/exception/invalid_matcher_query.rb +8 -4
  51. data/lib/objects/exception/invalid_meta_data_config.rb +8 -4
  52. data/lib/objects/exception/invalid_metadata_encryption_key.rb +8 -4
  53. data/lib/objects/exception/invalid_network_config.rb +8 -4
  54. data/lib/objects/exception/invalid_network_driver_type.rb +8 -4
  55. data/lib/objects/exception/invalid_port_forward_request.rb +11 -0
  56. data/lib/objects/exception/invalid_proxy_config.rb +8 -4
  57. data/lib/objects/exception/invalid_regexp_matcher_in_registry.rb +8 -4
  58. data/lib/objects/exception/invalid_registry_arguments_type.rb +8 -4
  59. data/lib/objects/exception/invalid_registry_command_name_length.rb +8 -4
  60. data/lib/objects/exception/invalid_registry_data_config.rb +8 -4
  61. data/lib/objects/exception/invalid_restriction_key_in_registry.rb +8 -4
  62. data/lib/objects/exception/invalid_ssh_config.rb +8 -4
  63. data/lib/objects/exception/inventories_cannot_have_subviews.rb +8 -4
  64. data/lib/objects/exception/malformed_command_line_arguments.rb +8 -4
  65. data/lib/objects/exception/method_invocation_requires_parameter.rb +8 -4
  66. data/lib/objects/exception/method_name_conflict_in_registry.rb +8 -4
  67. data/lib/objects/exception/missing_argument_for_registry_command.rb +8 -4
  68. data/lib/objects/exception/missing_description_on_view.rb +8 -4
  69. data/lib/objects/exception/missing_execute_on_registry_object.rb +8 -4
  70. data/lib/objects/exception/missing_gcp_authentication_scheme.rb +11 -0
  71. data/lib/objects/exception/missing_gcp_service_account_credentials_filename.rb +11 -0
  72. data/lib/objects/exception/missing_gcp_service_scopes.rb +11 -0
  73. data/lib/objects/exception/missing_identifier_on_view.rb +8 -4
  74. data/lib/objects/exception/missing_inventory_contributors.rb +11 -0
  75. data/lib/objects/exception/missing_ip_address_on_server.rb +8 -4
  76. data/lib/objects/exception/missing_network_config.rb +8 -4
  77. data/lib/objects/exception/missing_or_invalid_client_secrets.rb +11 -0
  78. data/lib/objects/exception/missing_params_for_rsync.rb +8 -4
  79. data/lib/objects/exception/missing_params_for_scp.rb +8 -4
  80. data/lib/objects/exception/missing_subselection_key.rb +8 -4
  81. data/lib/objects/exception/missing_type_on_view.rb +8 -4
  82. data/lib/objects/exception/no_node_found_for_breadcrumb.rb +8 -4
  83. data/lib/objects/exception/no_node_named_by_identifier.rb +8 -4
  84. data/lib/objects/exception/node_identifiers_must_be_unique.rb +8 -4
  85. data/lib/objects/exception/orchestration_script_does_not_exist.rb +8 -4
  86. data/lib/objects/exception/proxy_host_node_does_not_have_public_ip_address.rb +8 -4
  87. data/lib/objects/exception/unknown_dynamic_server_type.rb +11 -0
  88. data/lib/objects/exception/unknown_method_for_namespace.rb +8 -4
  89. data/lib/objects/exception/user_orchestration_error.rb +11 -0
  90. data/lib/objects/initialization/factory.rb +36 -0
  91. data/lib/objects/initialization/structure.rb +18 -0
  92. data/lib/objects/initialization/utils.rb +20 -0
  93. data/lib/objects/interactive/session.rb +4 -1
  94. data/lib/objects/interactive/session_item/base.rb +2 -0
  95. data/lib/objects/interactive/session_item/capture_input.rb +2 -0
  96. data/lib/objects/interactive/session_item/transparent_ssh.rb +29 -23
  97. data/lib/objects/loading_bar/handler.rb +80 -0
  98. data/lib/objects/loading_bar/indicator/base.rb +65 -0
  99. data/lib/objects/loading_bar/indicator/basic.rb +34 -0
  100. data/lib/objects/loading_bar/indicator/progress.rb +26 -0
  101. data/lib/objects/loading_bar/pid_bucket.rb +27 -0
  102. data/lib/objects/modules/context.rb +13 -9
  103. data/lib/objects/modules/draw.rb +49 -0
  104. data/lib/objects/modules/registry_management.rb +16 -10
  105. data/lib/objects/modules/tree.rb +157 -0
  106. data/lib/objects/modules/ui_output.rb +10 -6
  107. data/lib/objects/modules/workspace_commands.rb +131 -157
  108. data/lib/objects/modules/workspace_menu.rb +193 -129
  109. data/lib/objects/node/attributes.rb +17 -19
  110. data/lib/objects/node/base.rb +136 -74
  111. data/lib/objects/node/cache_handler.rb +3 -1
  112. data/lib/objects/node/collection.rb +10 -9
  113. data/lib/objects/node/factory.rb +47 -36
  114. data/lib/objects/node/inventory/base.rb +106 -100
  115. data/lib/objects/node/inventory/defined.rb +113 -89
  116. data/lib/objects/node/inventory/merge.rb +51 -0
  117. data/lib/objects/node/inventory/subselect.rb +66 -46
  118. data/lib/objects/node/kube/base.rb +51 -0
  119. data/lib/objects/node/kube/container.rb +9 -0
  120. data/lib/objects/node/kube/estate.rb +19 -0
  121. data/lib/objects/node/kube/namespace.rb +24 -0
  122. data/lib/objects/node/kube/pod.rb +24 -0
  123. data/lib/objects/node/kube_wrap.rb +26 -0
  124. data/lib/objects/node/meta/base.rb +8 -1
  125. data/lib/objects/node/meta/cloud.rb +2 -0
  126. data/lib/objects/node/meta/local.rb +2 -0
  127. data/lib/objects/node/meta_data_factory.rb +4 -2
  128. data/lib/objects/node/meta_data_loader.rb +28 -29
  129. data/lib/objects/node/resources/base.rb +5 -1
  130. data/lib/objects/node/resources/inventory.rb +26 -5
  131. data/lib/objects/node/resources/merged.rb +47 -0
  132. data/lib/objects/node/resources/sub_inventory.rb +12 -8
  133. data/lib/objects/node/server/base.rb +105 -70
  134. data/lib/objects/node/server/dynamic/base.rb +23 -0
  135. data/lib/objects/node/server/{dynamic.rb → dynamic/ec2.rb} +13 -13
  136. data/lib/objects/node/server/dynamic/gcp.rb +46 -0
  137. data/lib/objects/node/server/static.rb +34 -10
  138. data/lib/objects/orchestration/base.rb +17 -1
  139. data/lib/objects/orchestration/interactive_terraform.rb +59 -30
  140. data/lib/objects/orchestrator.rb +22 -0
  141. data/lib/objects/parser/bread_crumb.rb +3 -1
  142. data/lib/objects/registry/arguments/base.rb +3 -1
  143. data/lib/objects/registry/arguments/command_line.rb +6 -1
  144. data/lib/objects/registry/arguments/console.rb +4 -1
  145. data/lib/objects/registry/command/base.rb +3 -0
  146. data/lib/objects/registry/command/external.rb +9 -3
  147. data/lib/objects/registry/command/group.rb +11 -4
  148. data/lib/objects/registry/command/internal.rb +3 -1
  149. data/lib/objects/registry/command/shortcut.rb +17 -9
  150. data/lib/objects/registry/command_list.rb +2 -0
  151. data/lib/objects/registry/loader.rb +13 -10
  152. data/lib/objects/ssh/bootstrap.rb +3 -1
  153. data/lib/objects/ssh/command.rb +9 -8
  154. data/lib/objects/ssh/command_exec.rb +16 -10
  155. data/lib/objects/ssh/connection_wrangler.rb +124 -0
  156. data/lib/objects/ssh/connector.rb +108 -0
  157. data/lib/objects/ssh/driver.rb +28 -242
  158. data/lib/objects/ssh/driver_concerns/command_strings.rb +17 -0
  159. data/lib/objects/ssh/driver_concerns/connection.rb +75 -0
  160. data/lib/objects/ssh/driver_concerns/functions.rb +89 -0
  161. data/lib/objects/ssh/driver_concerns/user.rb +32 -0
  162. data/lib/objects/ssh/proxy_chain.rb +19 -0
  163. data/lib/objects/ssh/proxy_chain_link.rb +26 -0
  164. data/lib/objects/ssh/proxy_hop.rb +130 -0
  165. data/lib/objects/ssh/script_exec.rb +12 -11
  166. data/lib/objects/ssh/tunnel/local_port_forward.rb +5 -6
  167. data/lib/objects/ssh/tunnel_keeper.rb +21 -0
  168. data/lib/objects/ssh/window.rb +31 -0
  169. data/lib/objects/startup.rb +58 -0
  170. data/lib/objects/system/local.rb +3 -0
  171. data/lib/objects/terraform/output.rb +45 -0
  172. data/lib/objects/workspace.rb +13 -14
  173. data/patches/irb.rb +63 -6
  174. data/patches/string-encrypt.rb +20 -23
  175. data/patches/string.rb +18 -1
  176. data/patches/string_stylesheet.rb +2 -0
  177. metadata +157 -33
  178. data/lib/objects/driver/static.rb +0 -4
  179. data/lib/objects/progress_bar.rb +0 -30
  180. data/lib/objects/ssh/connection_handler.rb +0 -101
  181. data/lib/objects/ssh/proxy_data.rb +0 -56
  182. data/lib/objects/terraform/parser.rb +0 -23
  183. data/lib/objects/terraform/state.rb +0 -40
@@ -1,140 +1,204 @@
1
- module Bcome::WorkspaceMenu
2
- def menu
3
- print "\n\n"
4
- puts "#{mode} menu".title + "\sfor #{self.class} #{namespace}".resource_value + "\n\n"
5
- enabled_menu_items.each_with_index do |menu_item, _index|
6
- item = menu_items[menu_item]
7
- next if !::Bcome::System::Local.instance.in_console_session? && item[:console_only]
8
- puts tab_spacing + menu_item.to_s.resource_key + item_spacing(menu_item) + (menu_items[menu_item][:description]).to_s.resource_value
9
- if item[:usage] || item[:terminal_usage]
10
- usage_string = ::Bcome::System::Local.instance.in_console_session? ? item[:usage] : "bcome #{keyed_namespace.empty? ? '' : "#{keyed_namespace}:"}#{item[:terminal_usage]}"
11
- puts tab_spacing + ("\s" * menu_item_spacing_length) + 'usage: '.instructional + usage_string
1
+ # frozen_string_literal: true
2
+
3
+ module Bcome
4
+ module WorkspaceMenu
5
+ def menu
6
+ print "\n\n"
7
+ puts 'COMMAND MENU'.bc_cyan + "\sfor #{self.class} #{namespace}".resource_value
8
+
9
+ grouped_menu_items = menu_items.group_by { |m| m[1][:group] }
10
+ grouped_menu_items.each do |group_key, items|
11
+ # If we're not in a console session, we filter out console only methods
12
+ items = items.reject { |item| item[1][:console_only] } unless ::Bcome::System::Local.instance.in_console_session?
13
+
14
+ next if items.empty?
15
+
16
+ s_heading = "/ #{menu_group_names[group_key]}"
17
+ print "\n\n" + tab_spacing + s_heading.upcase.bc_cyan
18
+ print item_spacing(s_heading) + ("\s" * 110).to_s.bc_cyan.underline
19
+ print "\n\n"
20
+ print_menu_items(items)
12
21
  end
13
- puts "\n"
22
+
23
+ nil
14
24
  end
15
25
 
16
- nil
17
- end
26
+ def print_menu_items(items)
27
+ items.each_with_index do |item, _index|
28
+ key = item[0]
29
+ config = item[1]
18
30
 
19
- def mode
20
- ::Bcome::System::Local.instance.in_console_session? ? 'Console' : 'Terminal'
21
- end
31
+ next if !::Bcome::System::Local.instance.in_console_session? && config[:console_only]
22
32
 
23
- def item_spacing(item)
24
- "\s" * (menu_item_spacing_length - item.length)
25
- end
33
+ puts tab_spacing + key.to_s.resource_key + item_spacing(key) + (config[:description]).to_s.resource_value
34
+ if config[:usage] || config[:terminal_usage]
35
+ usage_string = if ::Bcome::System::Local.instance.in_console_session?
36
+ config[:usage]
37
+ else
38
+ "bcome #{keyed_namespace.empty? ? '' : "#{keyed_namespace}:"}#{config[:terminal_usage]}"
39
+ end
40
+ puts tab_spacing + ("\s" * menu_item_spacing_length) + 'usage: '.instructional + usage_string
41
+ end
42
+ puts "\n"
43
+ end
44
+ end
26
45
 
27
- def menu_item_spacing_length
28
- 16
29
- end
46
+ def mode
47
+ ::Bcome::System::Local.instance.in_console_session? ? 'Console' : 'Terminal'
48
+ end
30
49
 
31
- def tab_spacing
32
- "\s" * 3
33
- end
50
+ def item_spacing(item)
51
+ "\s" * (menu_item_spacing_length - item.length)
52
+ end
34
53
 
35
- def menu_items
36
- {
37
- ls: {
38
- description: 'list all available resources',
39
- console_only: false
40
- },
41
- lsa: {
42
- description: 'list all active resources',
43
- console_only: true
44
- },
45
- workon: {
46
- description: 'work on specific resources only, inactivating all others from this selection',
47
- usage: 'workon identifier1, identifier2 ...',
48
- console_only: true
49
- },
50
- disable: {
51
- description: 'remove a resource from this selection',
52
- usage: 'disable identifier1, identifier2 ...',
53
- console_only: true
54
- },
55
- enable: {
56
- description: 're-enable a resource within this selection',
57
- usage: 'enable identifier1, identifier2 ...',
58
- console_only: true
59
- },
60
- enable!: {
61
- description: 'enable all resources within this selection',
62
- console_only: true
63
- },
64
- disable!: {
65
- description: 'disable all resources within this selection',
66
- console_only: true
67
- },
68
- run: {
69
- description: 'execute a command to be run over ssh against all active resources',
70
- usage: "run 'command1', 'command2', ...",
71
- console_only: false,
72
- terminal_usage: "run 'command1' 'command2' ..."
73
- },
74
- interactive: {
75
- description: 'enter an interactive command session for all active resources',
76
- console_only: false
77
- },
78
- tree: {
79
- description: 'print a tree view for all resources and their sub-resources',
80
- console_only: false
81
- },
82
- ping: {
83
- description: 'ping all resources to test connectivity',
84
- console_only: false
85
- },
86
- put: {
87
- description: 'upload a file or directory using scp',
88
- usage: "put 'local/path','remote/path'",
89
- console_only: false,
90
- terminal_usage: "put 'local/path' 'remote/path'"
91
- },
92
- put_str: {
93
- description: 'Write a file /to/remote/path from a string',
94
- usage: 'put_str',
95
- console_only: false,
96
- terminal_usage: "put_str '<file contents>', 'remote/path'"
97
- },
98
- rsync: {
99
- description: 'upload a file or directory using rsync (faster)',
100
- usage: "rsync 'local/path','remote/path'",
101
- console_only: false,
102
- terminal_usage: "rsync 'local/path' 'remote/path'"
103
- },
104
- get: {
105
- description: 'download a file',
106
- usage: "get 'remote/path', 'local/path'",
107
- console_only: false,
108
- terminal_usage: "get 'remote/path' 'local/path"
109
- },
110
- cd: {
111
- description: 'enter the namespace for a resource from this selection',
112
- usage: 'cd identifier',
113
- console_only: true
114
- },
115
- save: {
116
- description: 'Save the current tree state',
117
- console_only: true
118
- },
119
- meta: {
120
- description: 'Print out all metadata related to this node'
121
- },
122
- pack_metadata: {
123
- description: 'Encrypt your metadata files',
124
- },
125
- unpack_metadata: {
126
- description: 'Decrypt and expose your encrypted metadata files',
127
- },
128
- registry: {
129
- description: 'List all user defined commands present in your registry, and available to this namespace',
130
- console_only: false
131
- },
132
- execute_script: {
133
- description: 'execute a bash script',
134
- console_only: false,
135
- usage: 'execute_script "script_name"',
136
- terminal_usage: 'execute_script script_name'
54
+ def menu_item_spacing_length
55
+ 16
56
+ end
57
+
58
+ def tab_spacing
59
+ "\s" * 3
60
+ end
61
+
62
+ def menu_group_names
63
+ {
64
+ ssh: 'Ssh',
65
+ informational: 'Informational',
66
+ selection: 'Selections',
67
+ file: 'File & Script',
68
+ navigation: 'Navigational',
69
+ miscellany: 'Miscellaneous',
70
+ command_list: 'Command lists'
137
71
  }
138
- }
72
+ end
73
+
74
+ def menu_items
75
+ {
76
+ routes: {
77
+ description: 'Print SSH routing tree',
78
+ console_only: false,
79
+ group: :informational
80
+ },
81
+ ls: {
82
+ description: 'list all available namespaces',
83
+ console_only: false,
84
+ group: :informational
85
+ },
86
+ lsa: {
87
+ description: 'list all active namespaces',
88
+ console_only: true,
89
+ group: :informational
90
+ },
91
+ workon: {
92
+ description: 'work on specific namespaces only, inactivating all others from this selection',
93
+ usage: 'workon identifier1, identifier2 ...',
94
+ console_only: true,
95
+ group: :selection
96
+ },
97
+ disable: {
98
+ description: 'remove a namespace from this selection',
99
+ usage: 'disable identifier1, identifier2 ...',
100
+ console_only: true,
101
+ group: :selection
102
+ },
103
+ enable: {
104
+ description: 're-enable a namespace within this selection',
105
+ usage: 'enable identifier1, identifier2 ...',
106
+ console_only: true,
107
+ group: :selection
108
+ },
109
+ enable!: {
110
+ description: 'enable all namespaces within this selection',
111
+ console_only: true,
112
+ group: :selection
113
+ },
114
+ disable!: {
115
+ description: 'disable all namespaces within this selection',
116
+ console_only: true,
117
+ group: :selection
118
+ },
119
+ run: {
120
+ description: 'execute a command to be run over ssh against all active namespaces',
121
+ usage: "run 'command1', 'command2', ...",
122
+ console_only: false,
123
+ terminal_usage: "run 'command1' 'command2' ...",
124
+ group: :ssh
125
+ },
126
+ interactive: {
127
+ description: 'enter an interactive command session for all active namespaces',
128
+ console_only: false,
129
+ group: :ssh
130
+ },
131
+ tree: {
132
+ description: 'print a tree view for all namespaces and their sub-namespaces',
133
+ console_only: false,
134
+ group: :informational
135
+ },
136
+ ping: {
137
+ description: 'ping all namespaces to test connectivity',
138
+ console_only: false,
139
+ group: :ssh
140
+ },
141
+ put: {
142
+ description: 'upload a file or directory using scp',
143
+ usage: "put 'local/path','remote/path'",
144
+ console_only: false,
145
+ terminal_usage: "put 'local/path' 'remote/path'",
146
+ group: :file
147
+ },
148
+ put_str: {
149
+ description: 'Write a file /to/remote/path from a string',
150
+ usage: 'put_str "string" "remote/path"',
151
+ console_only: false,
152
+ terminal_usage: "put_str '<file contents>', 'remote/path'",
153
+ group: :file
154
+ },
155
+ rsync: {
156
+ description: 'upload a file or directory using rsync (faster)',
157
+ usage: "rsync 'local/path','remote/path'",
158
+ console_only: false,
159
+ terminal_usage: "rsync 'local/path' 'remote/path'",
160
+ group: :file
161
+ },
162
+ cd: {
163
+ description: 'enter a console session for a child namespace from this selection',
164
+ usage: 'cd identifier',
165
+ console_only: true,
166
+ group: :navigation
167
+ },
168
+ quit: {
169
+ description: 'Quit out of bcome',
170
+ usage: 'quit',
171
+ console_only: true,
172
+ group: :navigation
173
+ },
174
+ back: {
175
+ description: 'Go back up a namespace, or quit',
176
+ usage: 'back',
177
+ console_only: true,
178
+ group: :navigation
179
+ },
180
+ meta: {
181
+ description: 'Print out all metadata related to this node',
182
+ group: :informational
183
+ },
184
+ registry: {
185
+ description: 'List all user defined commands present in your registry, and available to this namespace',
186
+ console_only: false,
187
+ group: :command_list
188
+ },
189
+ menu: {
190
+ description: 'List all available commands',
191
+ console_only: false,
192
+ group: :command_list
193
+ },
194
+ execute_script: {
195
+ description: 'execute a bash script',
196
+ console_only: false,
197
+ usage: 'execute_script "script_name"',
198
+ terminal_usage: 'execute_script script_name',
199
+ group: :ssh
200
+ }
201
+ }
202
+ end
139
203
  end
140
204
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bcome::Node::Attributes
2
4
  ## -- Attributes --
3
5
 
@@ -5,12 +7,8 @@ module Bcome::Node::Attributes
5
7
  @identifier
6
8
  end
7
9
 
8
- def description
9
- @description
10
- end
11
-
12
- def type
13
- @type
10
+ def ssh_driver=(alternative_ssh_driver)
11
+ @ssh_driver = alternative_ssh_driver
14
12
  end
15
13
 
16
14
  def ssh_driver
@@ -18,28 +16,28 @@ module Bcome::Node::Attributes
18
16
  end
19
17
 
20
18
  def ssh_data
21
- instance_var_name = '@ssh_settings'
22
- recurse_hash_data_for_instance_var(instance_var_name, :ssh_data)
19
+ recurse_hash_data_for_instance_key(:ssh_settings, :ssh_data)
23
20
  end
24
21
 
25
- def network_driver
26
- return nil if !network_data || (network_data.is_a?(Hash) && network_data.empty?)
27
- @network_driver ||= ::Bcome::Driver::Bucket.instance.driver_for_network_data(network_data)
28
- @network_driver
22
+ def network_data
23
+ recurse_hash_data_for_instance_key(:network, :network_data)
29
24
  end
30
25
 
26
+ # From 2.0.0 onwards, filters can be loaded from the network block only. The older key, 'ec2_filters'
27
+ #  is retained at this level for backwards compatibility.
31
28
  def filters
32
- instance_var_name = '@ec2_filters'
33
- recurse_hash_data_for_instance_var(instance_var_name, :filters)
29
+ recurse_hash_data_for_instance_key(:ec2_filters, :filters)
34
30
  end
35
31
 
36
- def network_data
37
- instance_var_name = '@network'
38
- recurse_hash_data_for_instance_var(instance_var_name, :network_data)
32
+ def network_driver
33
+ return nil if !network_data || (network_data.is_a?(Hash) && network_data.empty?)
34
+
35
+ @network_driver ||= ::Bcome::Driver::Bucket.instance.driver_for_network_data(network_data, self)
36
+ @network_driver
39
37
  end
40
38
 
41
- def recurse_hash_data_for_instance_var(instance_var_name, parent_key)
42
- instance_data = instance_variable_defined?(instance_var_name) ? instance_variable_get(instance_var_name) : {}
39
+ def recurse_hash_data_for_instance_key(instance_key, parent_key)
40
+ instance_data = respond_to?(instance_key) ? send(instance_key) : {}
43
41
  instance_data ||= {}
44
42
  instance_data = parent.send(parent_key).deep_merge(instance_data) if has_parent?
45
43
  instance_data
@@ -1,24 +1,32 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bcome::Node
2
4
  class Base
3
-
4
5
  include Bcome::Context
5
6
  include Bcome::WorkspaceCommands
6
7
  include Bcome::Node::Attributes
7
8
  include Bcome::WorkspaceMenu
8
9
  include Bcome::Node::LocalMetaDataFactory
9
10
  include Bcome::Node::RegistryManagement
10
-
11
- DEFAULT_IDENTIFIER = "bcome"
11
+ include Bcome::Tree
12
+
13
+ def inspect
14
+ "<##{self.class}: #{namespace} @network_driver=#{network_driver}>"
15
+ end
12
16
 
13
17
  def self.const_missing(constant)
14
18
  ## Hook for direct access to node level resources by constant name where
15
19
  ## cd ServerName should yield the same outcome as cd "ServerName"
16
- set_context = ::IRB.CurrentContext.workspace.main
17
- return (set_context.resource_for_identifier(constant.to_s)) ? constant.to_s : super
20
+ set_context = ::IRB.CurrentContext.workspace.main
21
+ set_context.resource_for_identifier(constant.to_s) ? constant.to_s : super
18
22
  end
19
23
 
20
24
  attr_reader :params
21
25
 
26
+ DEFAULT_IDENTIFIER = 'bcome'
27
+
28
+ include Bcome::LoadingBar::Handler
29
+
22
30
  def initialize(params)
23
31
  @params = params
24
32
  @identifier = nil
@@ -27,14 +35,34 @@ module Bcome::Node
27
35
  @parent = params[:parent]
28
36
  @type = params[:type]
29
37
  @metadata = {}
38
+ @nodes_loaded = false
30
39
 
31
40
  set_view_attributes if @views
32
41
  validate_attributes
42
+
43
+ @original_identifier = @identifier
44
+
33
45
  ::Bcome::Registry::Loader.instance.set_command_group_for_node(self)
34
46
  end
35
47
 
36
- def bootstrap?
37
- false
48
+ attr_reader :parent
49
+
50
+ attr_reader :views
51
+
52
+ def method_missing(method_sym, *arguments)
53
+ raise Bcome::Exception::Generic, "undefined method '#{method_sym}' for #{self.class}" unless method_is_available_on_node?(method_sym)
54
+
55
+ if resource_identifiers.include?(method_sym.to_s)
56
+ method_sym.to_s
57
+ elsif command = user_command_wrapper.command_for_console_command_name(method_sym)
58
+ command.execute(self, arguments)
59
+ else
60
+ raise NameError, "Missing method #{method_sym} for #{self.class}"
61
+ end
62
+ end
63
+
64
+ def ssh_connect(params = {})
65
+ ::Bcome::Ssh::Connector.connect(self, params)
38
66
  end
39
67
 
40
68
  def collection?
@@ -50,48 +78,61 @@ module Bcome::Node
50
78
  end
51
79
 
52
80
  def enabled_menu_items
53
- [:ls, :lsa, :workon, :enable, :disable, :enable!, :disable!, :run, :tree, :ping, :put, :put_str, :rsync, :cd, :meta, :pack_metadata, :unpack_metadata, :registry, :interactive, :execute_script]
81
+ %i[ls lsa workon enable disable enable! disable! run tree ping put put_str rsync cd meta registry interactive execute_script]
54
82
  end
55
83
 
56
84
  def has_proxy?
57
85
  ssh_driver.has_proxy?
58
86
  end
59
87
 
60
- def identifier=(new_identifier)
61
- @identifier = new_identifier
62
- end
88
+ attr_writer :identifier
63
89
 
64
90
  def proxy
65
91
  ssh_driver.proxy
66
92
  end
67
-
68
- # TODO - why not do these in parallel?
93
+
94
+ def proxy_chain
95
+ ssh_driver.proxy_chain
96
+ end
97
+
98
+ def proxy_chain_link
99
+ @proxy_chain_link ||= ::Bcome::Ssh::ProxyChainLink.new(self)
100
+ end
101
+
102
+ def scoped_resources
103
+ # Active & not hidden
104
+ resources.active.reject(&:hide?)
105
+ end
106
+
69
107
  def scp(local_path, remote_path)
70
- resources.active.each do |resource|
108
+ scoped_resources.each do |resource|
71
109
  resource.put(local_path, remote_path)
72
110
  end
73
- return
111
+ nil
74
112
  end
75
113
 
76
114
  def rsync(local_path, remote_path)
77
- resources.active.each do |resource|
115
+ scoped_resources.each do |resource|
78
116
  resource.rsync(local_path, remote_path)
79
- end
80
- return
117
+ end
118
+ nil
81
119
  end
82
120
 
83
- def put(local_path, remote_path)
84
- resources.active.each do |resource|
85
- resource.put(local_path, remote_path)
121
+ def put(local_path, remote_path, connect = true)
122
+ ssh_connect if connect # Initiate connect at highest namespace level
123
+
124
+ scoped_resources.each do |resource|
125
+ resource.put(local_path, remote_path, false)
86
126
  end
87
- return
127
+ nil
88
128
  end
89
129
 
90
- def put_str(string, remote_path)
91
- resources.active.each do |resource|
92
- resource.put_str(string, remote_path)
130
+ def put_str(string, remote_path, connect = true)
131
+ ssh_connect if connect # Initiate connect at highest namespace level
132
+ scoped_resources.pmap do |resource|
133
+ resource.put_str(string, remote_path, false)
93
134
  end
94
- return
135
+ nil
95
136
  end
96
137
 
97
138
  def execute_script(script_name)
@@ -103,30 +144,28 @@ module Bcome::Node
103
144
  results
104
145
  end
105
146
 
106
- def pack_metadata
107
- ::Bcome::Encryptor.instance.pack
147
+ def hide?
148
+ return true if @views.key?(:hidden) && @views[:hidden]
149
+
150
+ false
108
151
  end
109
152
 
110
- def unpack_metadata
111
- ::Bcome::Encryptor.instance.unpack
112
- end
113
-
114
153
  def validate_attributes
115
- validate_identifier
116
- raise ::Bcome::Exception::MissingDescriptionOnView.new(@views.inspect) if requires_description? && !@description
117
- raise ::Bcome::Exception::MissingTypeOnView.new(@views.inspect) if requires_type? && !@type
154
+ validate_identifier
155
+ raise ::Bcome::Exception::MissingDescriptionOnView, views.inspect if requires_description? && !defined?(:description)
156
+ raise ::Bcome::Exception::MissingTypeOnView, views.inspect if requires_type? && !defined?(:type)
118
157
  end
119
158
 
120
159
  def validate_identifier
121
- @identifier = DEFAULT_IDENTIFIER if is_top_level_node? && !@identifier && !is_a?(::Bcome::Node::Server::Base)
160
+ @identifier = DEFAULT_IDENTIFIER.dup if is_top_level_node? && !@identifier && !is_a?(::Bcome::Node::Server::Base)
122
161
 
123
- @identifier = "NO-ID_#{Time.now.to_i}" unless @identifier
162
+ @identifier ||= "NO-ID_#{Time.now.to_i}".dup
124
163
 
125
- #raise ::Bcome::Exception::MissingIdentifierOnView.new(@views.inspect) unless @identifier
126
- @identifier.gsub!(/\s/, "_") # Remove whitespace
127
- @identifier.gsub!("-", "_") # change hyphens to undescores, hyphens don't play well in var names in irb
164
+ # raise ::Bcome::Exception::MissingIdentifierOnView.new(@views.inspect) unless @identifier
165
+ @identifier.gsub!(/\s/, '_') # Remove whitespace
166
+ @identifier.gsub!('-', '_') # change hyphens to undescores, hyphens don't play well in var names in irb
128
167
 
129
- #raise ::Bcome::Exception::InvalidIdentifier.new("'#{@identifier}' contains whitespace") if @identifier =~ /\s/
168
+ # raise ::Bcome::Exception::InvalidIdentifier.new("'#{@identifier}' contains whitespace") if @identifier =~ /\s/
130
169
  end
131
170
 
132
171
  def requires_description?
@@ -142,9 +181,15 @@ module Bcome::Node
142
181
  end
143
182
 
144
183
  def nodes_loaded?
145
- resources.any?
184
+ # resources.any? # This was buggy: an inventory may validly contain no resources. This does not mean that we haven't attempted to load them
185
+ # we no explicitly set a flag for when we've loaded nodes. This will prevents uneccessary lookups over the wire
186
+ @nodes_loaded
187
+ end
188
+
189
+ def nodes_loaded!
190
+ @nodes_loaded = true
146
191
  end
147
-
192
+
148
193
  def resources
149
194
  @resources ||= ::Bcome::Node::Resources::Base.new
150
195
  end
@@ -155,16 +200,16 @@ module Bcome::Node
155
200
 
156
201
  def invoke(method_name, arguments = [])
157
202
  if method_is_available_on_node?(method_name)
158
- if respond_to?(method_name)
203
+ if respond_to?(method_name)
159
204
  # Invoke a method on node that's defined by the system
160
205
  begin
161
- if arguments && arguments.any?
206
+ if arguments&.any?
162
207
  send(method_name, *arguments)
163
208
  else
164
209
  send(method_name)
165
210
  end
166
211
  rescue ArgumentError => e
167
- raise ::Bcome::Exception::ArgumentErrorInvokingMethodFromCommmandLine.new method_name + "error message - #{e.message}"
212
+ raise ::Bcome::Exception::ArgumentErrorInvokingMethodFromCommmandLine, method_name + " error message - #{e.message}"
168
213
  end
169
214
  else
170
215
  # Invoke a user defined (registry) method
@@ -173,7 +218,7 @@ module Bcome::Node
173
218
  end
174
219
  else
175
220
  # Final crumb is neither a node level context nor an executable method on the penultimate node level context
176
- raise ::Bcome::Exception::InvalidBreadcrumb.new("Method '#{method_name}' is not available on bcome node of type #{self.class}, at namespace #{namespace}")
221
+ raise ::Bcome::Exception::InvalidBreadcrumb, "Method '#{method_name}' is not available on bcome node of type #{self.class}, at namespace #{namespace}"
177
222
  end
178
223
  end
179
224
 
@@ -183,20 +228,24 @@ module Bcome::Node
183
228
 
184
229
  def recurse_resource_for_identifier(identifier)
185
230
  resource = resource_for_identifier(identifier)
186
- return resource ? resource : (has_parent? ? parent.recurse_resource_for_identifier(identifier) : nil)
231
+ resource || (has_parent? ? parent.recurse_resource_for_identifier(identifier) : nil)
187
232
  end
188
233
 
189
234
  def prompt_breadcrumb
190
- "#{has_parent? ? "#{parent.prompt_breadcrumb}> " : "" }#{ is_current_context? ? (has_parent? ? identifier.terminal_prompt : identifier) : identifier}"
235
+ "#{has_parent? ? "#{parent.prompt_breadcrumb}> " : ''}#{if current_context?
236
+ has_parent? ? identifier.terminal_prompt : identifier
237
+ else
238
+ identifier
239
+ end}"
191
240
  end
192
241
 
193
242
  def namespace
194
- "#{ parent ? "#{parent.namespace}:" : "" }#{identifier}"
243
+ "#{parent ? "#{parent.namespace}:" : ''}#{identifier}"
195
244
  end
196
245
 
197
246
  def keyed_namespace
198
- splits = namespace.split(":") ;
199
- splits[1..splits.size].join(":")
247
+ splits = namespace.split(':')
248
+ splits[1..splits.size].join(':')
200
249
  end
201
250
 
202
251
  def has_parent?
@@ -205,10 +254,10 @@ module Bcome::Node
205
254
 
206
255
  def is_top_level_node?
207
256
  !has_parent?
208
- end
257
+ end
209
258
 
210
259
  def list_attributes
211
- {
260
+ {
212
261
  "Identifier": :identifier,
213
262
  "Description": :description,
214
263
  "Type": :type
@@ -216,58 +265,71 @@ module Bcome::Node
216
265
  end
217
266
 
218
267
  def close_ssh_connections
219
- machines.pmap do |machine|
220
- machine.close_ssh_connection
221
- end
222
- return
223
- end
224
-
225
- def open_ssh_connections
226
- machines.pmap do |machine|
227
- machine.open_ssh_connection unless machine.has_ssh_connection?
268
+ # For every loaded server, we'll close any lingering ssh connection
269
+ if @resources&.any?
270
+ resources.pmap do |resource|
271
+ if resource.is_a?(::Bcome::Node::Server::Base)
272
+ resource.close_ssh_connection
273
+ else
274
+ resource.close_ssh_connections
275
+ end
276
+ end
228
277
  end
229
- return
278
+ nil
230
279
  end
231
280
 
232
281
  def execute_local(command)
233
- puts "(local) > #{command}"
282
+ puts "(local) > #{command}" unless ::Bcome::Orchestrator.instance.command_output_silenced?
234
283
  system(command)
284
+ puts ''
235
285
  end
236
286
 
237
287
  def data_print_from_hash(data, heading)
238
288
  puts "\n#{heading.title}"
239
- puts ""
289
+ puts ''
240
290
 
241
291
  if data.keys.any?
242
292
  data.each do |key, value|
243
293
  puts "#{key.to_s.resource_key}: #{value.to_s.informational}"
244
294
  end
245
295
  else
246
- puts "No values found".warning
296
+ puts 'No values found'.warning
247
297
  end
248
- puts ""
298
+ puts ''
249
299
  end
250
300
 
251
301
  private
252
302
 
303
+ def singleton_class
304
+ class << self
305
+ self
306
+ end
307
+ end
308
+
253
309
  def set_view_attributes
254
- @views.keys.each do |view_attribute_key|
310
+ @identifier = @views[:identifier]
311
+
312
+ @views.keys.sort.each do |view_attribute_key|
255
313
  next if view_attributes_to_skip_on_setup.include?(view_attribute_key)
256
- instance_variable_set("@#{view_attribute_key}", @views[view_attribute_key])
314
+
315
+ next if view_attribute_key == :identifier
316
+
317
+ singleton_class.class_eval do
318
+ define_method(view_attribute_key) do
319
+ @views[view_attribute_key]
320
+ end
321
+ end
257
322
  end
258
323
  end
259
324
 
260
325
  def view_attributes_to_skip_on_setup
261
- [:views]
326
+ [:views]
262
327
  end
263
328
 
264
- private
265
-
266
329
  def to_ary
267
330
  # due to my method_missing implementation, the following is required.
268
331
  # with thanks to https://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html & http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
269
332
  nil
270
333
  end
271
-
272
334
  end
273
335
  end