unimatrix-cli 2.2.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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/README.md +12 -0
  6. data/Rakefile +6 -0
  7. data/VERSION +1 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/exe/unimatrix +11 -0
  11. data/lib/unimatrix_cli.rb +74 -0
  12. data/lib/unimatrix_cli/alchemist/rendition/list_command.rb +72 -0
  13. data/lib/unimatrix_cli/alchemist/rendition_profile/assign_command.rb +59 -0
  14. data/lib/unimatrix_cli/alchemist/rendition_profile/list_command.rb +36 -0
  15. data/lib/unimatrix_cli/alchemist/video/create_command.rb +40 -0
  16. data/lib/unimatrix_cli/alchemist/video/describe_command.rb +40 -0
  17. data/lib/unimatrix_cli/alchemist/video_encoder/create_command.rb +37 -0
  18. data/lib/unimatrix_cli/alchemist/video_encoder/describe_command.rb +39 -0
  19. data/lib/unimatrix_cli/alchemist/video_encoder/encode_command.rb +102 -0
  20. data/lib/unimatrix_cli/alchemist/video_encoder/list_command.rb +35 -0
  21. data/lib/unimatrix_cli/archivist/blueprint/create_command.rb +94 -0
  22. data/lib/unimatrix_cli/citadel/app/build_command.rb +28 -0
  23. data/lib/unimatrix_cli/citadel/app/console_command.rb +17 -0
  24. data/lib/unimatrix_cli/citadel/app/db_setup_command.rb +17 -0
  25. data/lib/unimatrix_cli/citadel/app/environment_command.rb +30 -0
  26. data/lib/unimatrix_cli/citadel/app/logs_command.rb +17 -0
  27. data/lib/unimatrix_cli/citadel/app/migrate_command.rb +17 -0
  28. data/lib/unimatrix_cli/citadel/app/migrate_status_command.rb +17 -0
  29. data/lib/unimatrix_cli/citadel/app/rake_command.rb +18 -0
  30. data/lib/unimatrix_cli/citadel/app/routes_command.rb +17 -0
  31. data/lib/unimatrix_cli/citadel/citadel_command.rb +285 -0
  32. data/lib/unimatrix_cli/citadel/instance/details_command.rb +18 -0
  33. data/lib/unimatrix_cli/citadel/instance/scp_command.rb +48 -0
  34. data/lib/unimatrix_cli/citadel/instance/ssh_command.rb +29 -0
  35. data/lib/unimatrix_cli/citadel/instance/status_command.rb +17 -0
  36. data/lib/unimatrix_cli/citadel/instance/user_data_command.rb +17 -0
  37. data/lib/unimatrix_cli/citadel/instance/user_data_logs_command.rb +17 -0
  38. data/lib/unimatrix_cli/cli.rb +30 -0
  39. data/lib/unimatrix_cli/command.rb +138 -0
  40. data/lib/unimatrix_cli/config/acceptance.yml +23 -0
  41. data/lib/unimatrix_cli/config/configuration.rb +66 -0
  42. data/lib/unimatrix_cli/config/production.yml +15 -0
  43. data/lib/unimatrix_cli/config/staging.yml +15 -0
  44. data/lib/unimatrix_cli/config/unimatrix.rb +4 -0
  45. data/lib/unimatrix_cli/iris/stream/create_command.rb +43 -0
  46. data/lib/unimatrix_cli/iris/stream/describe_command.rb +35 -0
  47. data/lib/unimatrix_cli/iris/stream_encoder/create_command.rb +43 -0
  48. data/lib/unimatrix_cli/iris/stream_encoder/describe_command.rb +37 -0
  49. data/lib/unimatrix_cli/iris/stream_input/create_command.rb +43 -0
  50. data/lib/unimatrix_cli/iris/stream_input/describe_command.rb +37 -0
  51. data/lib/unimatrix_cli/iris/stream_output/create_command.rb +55 -0
  52. data/lib/unimatrix_cli/iris/stream_output/describe_command.rb +37 -0
  53. data/lib/unimatrix_cli/iris/stream_recorder/create_command.rb +49 -0
  54. data/lib/unimatrix_cli/iris/stream_recorder/describe_command.rb +37 -0
  55. data/lib/unimatrix_cli/iris/stream_transcriber/create_command.rb +54 -0
  56. data/lib/unimatrix_cli/iris/stream_transcriber/describe_command.rb +37 -0
  57. data/lib/unimatrix_cli/iris/stream_transmutator/create_command.rb +65 -0
  58. data/lib/unimatrix_cli/iris/stream_transmutator/describe_command.rb +38 -0
  59. data/lib/unimatrix_cli/login_command.rb +91 -0
  60. data/lib/unimatrix_cli/logout_command.rb +21 -0
  61. data/lib/unimatrix_cli/version.rb +3 -0
  62. data/lib/unimatrix_cli/zephyrus/input/create_command.rb +40 -0
  63. data/lib/unimatrix_cli/zephyrus/input/describe_command.rb +35 -0
  64. data/lib/unimatrix_cli/zephyrus/input/list_command.rb +38 -0
  65. data/lib/unimatrix_cli/zephyrus/output/list_command.rb +44 -0
  66. data/lib/unimatrix_cli/zephyrus/recording/configuration_command.rb +43 -0
  67. data/lib/unimatrix_cli/zephyrus/rendition/list_command.rb +40 -0
  68. data/lib/unimatrix_cli/zephyrus/routing/configuration_command.rb +43 -0
  69. data/lib/unimatrix_cli/zephyrus/transcoding/configuration_command.rb +55 -0
  70. data/lib/unimatrix_cli/zephyrus/transcribing/configuration_command.rb +55 -0
  71. data/lib/unimatrix_cli/zephyrus/transmutation/configuration_command.rb +55 -0
  72. data/lib/unimatrix_parser.rb +796 -0
  73. data/unimatrix-cli.gemspec +30 -0
  74. metadata +247 -0
@@ -0,0 +1,17 @@
1
+ module UnimatrixCLI
2
+ module Citadel
3
+ module App
4
+ class RoutesCommand < CitadelCommand
5
+
6
+ option :app, "Keyword for the name of the app", type: :string, required: true
7
+
8
+ synopsis "Display rails routes on a Rails-based API instance"
9
+
10
+ def execute
11
+ super
12
+ execute_command( 'routes' )
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,285 @@
1
+ require 'tty-prompt'
2
+ require 'aws-sdk'
3
+ require 'net/ssh'
4
+
5
+ module UnimatrixCLI
6
+ module Citadel
7
+ class CitadelCommand < Command
8
+
9
+ APIS = %w( activist alchemist analyst anemoi archivist
10
+ cartographer curator distributor dealer fabricator iris
11
+ keymaker merchant organizer portal regent universal zephyrus )
12
+
13
+ ROOT = %w( status bastion efsm environment user_data_logs )
14
+
15
+ PRIVATE_SUBNETS = %w( subnet-6788a72c subnet-761b7a59 subnet-71340a38
16
+ subnet-db541df6 subnet-699ce444 subnet-dbeeddf6
17
+ subnet-cf507986 subnet-7cedde51 subnet-5e505816 )
18
+
19
+ def execute
20
+ if load_environment && instance_list
21
+ write_instance_details( instance_details[ :instance_id ] )
22
+ end
23
+ end
24
+
25
+ def load_environment
26
+ Dotenv.load if defined?( Dotenv )
27
+ if ENV[ 'AWS_REGION' ] &&
28
+ ENV[ 'server_key' ] &&
29
+ ENV[ 'AWS_ACCESS_KEY_ID' ] &&
30
+ ENV[ 'AWS_SECRET_ACCESS_KEY' ]
31
+
32
+ Aws.config.update({
33
+ region: ENV[ 'AWS_REGION' ],
34
+ credentials: Aws::Credentials.new(
35
+ ENV[ 'AWS_ACCESS_KEY_ID' ],
36
+ ENV[ 'AWS_SECRET_ACCESS_KEY' ]
37
+ )
38
+ })
39
+ else
40
+ write( message: 'AWS credentials not found', error: true )
41
+ false
42
+ end
43
+ end
44
+
45
+ #----------------------------------------------------------------------------
46
+ # Memoized Variables
47
+
48
+ def instance_list
49
+ @instance_list ||= begin
50
+ app_keyword = @options[ :builder ] || @options[ :app ] || @options[ :instance ]
51
+ instance_list = instances( app_keyword )
52
+
53
+ if instance_list
54
+ instance_list
55
+ else
56
+ write( message: 'Instances not found', error: true )
57
+ false
58
+ end
59
+ end
60
+ end
61
+
62
+ def aws_client
63
+ @aws_client ||= Aws::EC2::Client.new
64
+ end
65
+
66
+ def instance_details
67
+ @instance_details ||= begin
68
+ names = instance_list.map { | instance | instance.keys }.flatten
69
+ chosen_instance = names.length == 1 ? names.first : menu( names )
70
+ collect_instance_details( chosen_instance )
71
+ end
72
+ end
73
+
74
+ def instances_by_zone
75
+ @instances_by_zone ||= begin
76
+ app_keyword = @options[ :builder ] || @options[ :app ] || @options[ :instance ]
77
+ find_instances_by_zone( app_keyword )
78
+ end
79
+ end
80
+
81
+ def user
82
+ @user ||= begin
83
+ sub_command = self.class.to_s.split( '::' ).last.slice( 0...-7 ).downcase
84
+ if ROOT.include?( @options[ :user ] ) ||
85
+ ROOT.include?( sub_command ) ||
86
+ ROOT.include?( instance_details[ :tag ].split( '-' ).first ) ||
87
+ ROOT.include?( instance_details[ :tag ].split( '.' ).first ) ||
88
+ instance_details[ :tag ].include?( 'bastion' ) ||
89
+ instance_details[ :tag ].include?( 'efsm' )
90
+ 'root'
91
+ elsif instance_details[ :tag ].include?( 'worker' )
92
+ 'worker'
93
+ elsif instance_details[ :tag ].include?( 'build' )
94
+ 'ec2-user'
95
+ elsif APIS.include?(
96
+ instance_details[ :tag ].split( '-' ).first
97
+ )
98
+ 'httpd'
99
+ else
100
+ instance_details[ :tag ].split( '-' ).first
101
+ end
102
+ end
103
+ end
104
+
105
+ def key
106
+ @key ||= File.join( '~/.ssh/', ENV[ 'server_key' ] )
107
+ end
108
+
109
+ def account
110
+ @account ||= begin
111
+ id = aws_client.describe_instances.reservations.first[ :owner_id ]
112
+ id == '561599644983' ? account = 'production' : account = 'development'
113
+ end
114
+ end
115
+
116
+ #----------------------------------------------------------------------------
117
+ # Instance Details
118
+
119
+ def write_instance_details( instance_id, options = {} )
120
+ instance = aws_client.describe_instances(
121
+ filters:[ { name: 'instance-id', values: [ instance_id ] } ]
122
+ ).first.to_h[ :reservations ].first[ :instances ].first
123
+
124
+ message = "Name: #{ instance[ :tags ].first[ :value ] }\n" +
125
+ "Instance ID: #{ instance[ :instance_id ] }\n" +
126
+ "Public DNS: #{ instance[ :public_dns_name ] }\n"
127
+
128
+ if options[ :full ]
129
+ message += "Launch Time: #{ instance[ :launch_time ] }\n" +
130
+ "Instance Type: #{ instance[ :instance_type ] }\n"
131
+
132
+ if instance[ :iam_instance_profile ]
133
+ message += "Instance Profile: " +
134
+ "#{ instance[ :iam_instance_profile ][ :arn ] }"
135
+ end
136
+ end
137
+
138
+ write( message: message )
139
+ end
140
+
141
+ private; def instances( app_keyword )
142
+ available_instances = find_instances_by_zone( app_keyword ).flatten.map do | instance |
143
+ if instance[ :state ][ :name ] == 'running'
144
+ { instance[ :tags ].find { | item | item[ :key ] == 'Name' }[ :value ] +
145
+ ' ' + '(' + instance[ :instance_id ] +
146
+ ')' => instance[ :public_dns_name ]
147
+ }
148
+ end
149
+ end
150
+ if available_instances.empty? ||
151
+ ( available_instances.include?( nil ) && available_instances.count == 1 )
152
+ false
153
+ else
154
+ available_instances.compact
155
+ end
156
+ end
157
+
158
+ private; def find_instances_by_zone( app_keyword )
159
+ descriptions = aws_client.describe_instances(
160
+ filters:[ { name: 'tag:Name', values: [ "*#{ app_keyword }*" ] } ]
161
+ ).to_h
162
+
163
+ instances_hash = descriptions.map { | hash | hash[ 1 ] }.first
164
+ instances_by_zone = instances_hash.map do | instances |
165
+ instances[ :instances ]
166
+ end.flatten
167
+ end
168
+
169
+ private; def menu( names )
170
+ prompt = TTY::Prompt.new(
171
+ interrupt: :exit, help_color: :magenta, prefix: 'Citadel:'
172
+ )
173
+ prompt.select( 'Select?', names, active_color: :cyan )
174
+ end
175
+
176
+ private; def development_vpc( vpc )
177
+ vpc == 'vpc-009eb264' ? environment = 'acceptance' : environment = 'staging'
178
+ end
179
+
180
+ private; def collect_instance_details( chosen_instance )
181
+ dns = instance_list.reduce(
182
+ Hash.new, :merge
183
+ ).select { | key, value | key == chosen_instance }[ chosen_instance ]
184
+
185
+ instance_id = chosen_instance.split( ' ' )[ -1 ].gsub( /[()]/, "" )
186
+ tag = chosen_instance.split( ' ' ).first
187
+
188
+ subnet_hash = instances_by_zone.map do | instance |
189
+ if instance[ :state ][ :name ] == 'running'
190
+ { instance[ :instance_id ] => instance[ :subnet_id ] }
191
+ end
192
+ end
193
+
194
+ subnets = subnet_hash.compact.reduce( Hash.new, :merge )
195
+ chosen_subnet = subnets[ instance_id ]
196
+
197
+ concealed = PRIVATE_SUBNETS.any? do | private_subnet |
198
+ chosen_subnet.include?( private_subnet )
199
+ end
200
+
201
+ vpc_hash = instances_by_zone.map do | instance |
202
+ if instance[ :state ][ :name ] == 'running'
203
+ { instance[ :instance_id ] => instance[ :vpc_id ] }
204
+ end
205
+ end
206
+
207
+ vpcs = vpc_hash.compact.reduce( Hash.new, :merge )
208
+
209
+ if account == 'development'
210
+ development_environment = development_vpc( vpcs[ instance_id ] )
211
+ bastion_hash = instances(
212
+ "bastion.#{ development_environment }.#{ ENV[ 'AWS_REGION' ] }"
213
+ )
214
+ else
215
+ bastion_hash = instances( "bastion.#{ ENV[ 'AWS_REGION' ] }" )
216
+ end
217
+
218
+ bastion_dns = bastion_hash.reduce( Hash.new, :merge ).values.first
219
+
220
+ {
221
+ bastion_dns: bastion_dns,
222
+ dns: dns,
223
+ tag: tag,
224
+ instance_id: instance_id,
225
+ account: account,
226
+ concealed: concealed
227
+ }
228
+ end
229
+
230
+ #----------------------------------------------------------------------------
231
+ # Command Execution
232
+
233
+ def execute_command( remote_command )
234
+ command = base_command +
235
+ " -t commands #{ remote_command }"
236
+ command += " #{ @options[ :commands ] }" if @options[ :commands ]
237
+ exec command
238
+ end
239
+
240
+ private; def base_command
241
+ base = "ssh -o StrictHostKeyChecking=no -i #{ key } "
242
+
243
+ if instance_details[ :bastion_dns ] == instance_details[ :dns ] ||
244
+ instance_details[ :tag ] == 'efsm'
245
+
246
+ base += "root@#{ instance_details[ :dns ] }"
247
+
248
+ elsif instance_details[ :account ] == 'production' ||
249
+ instance_details[ :concealed ]
250
+
251
+ base += "root@#{ instance_details[ :bastion_dns ] }"
252
+
253
+ else
254
+ user_to_use = @options[ :user ] || user
255
+ base += "#{ user_to_use }@#{ instance_details[ :dns ] }"
256
+ end
257
+ end
258
+
259
+ def execute_tunnel( remote_command )
260
+ write(
261
+ message: 'Running Via SSH Tunnel. ' +
262
+ 'This command could take a while; please be patient.'
263
+ )
264
+ if instance_details[ :account ] == 'production' ||
265
+ instance_details[ :concealed ]
266
+
267
+ user_to_use = 'root'
268
+ dns_to_use = instance_details[ :bastion_dns ]
269
+ to_exec = "/home/root/scripts/intraface " +
270
+ "#{ instance_details[ :instance_id ] } root" +
271
+ "-t -t #{ remote_command }"
272
+ else
273
+ user_to_use = user
274
+ dns_to_use = instance_details[ :dns ]
275
+ to_exec = "#{ remote_command }"
276
+ end
277
+
278
+ Net::SSH.start( dns_to_use, user_to_use, keys: [ key ] ) do | ssh |
279
+ output = ssh.exec!( to_exec )
280
+ write( message: output )
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,18 @@
1
+ module UnimatrixCLI
2
+ module Citadel
3
+ module Instance
4
+ class DetailsCommand < CitadelCommand
5
+
6
+ option :instance, "Keyword for the name of the instance", type: :string, required: :true
7
+
8
+ synopsis "Display full details for the specified instance"
9
+
10
+ def execute
11
+ if load_environment && instance_list
12
+ write_instance_details( instance_details[ :instance_id ], full: true )
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,48 @@
1
+ module UnimatrixCLI
2
+ module Citadel
3
+ module Instance
4
+ class ScpCommand < CitadelCommand
5
+
6
+ option :instance, "The instance to copy a file from or to", type: :string, required: true
7
+ option :local_file, "The local path and filename", type: :string, required: true
8
+ option :remote_file, "The remote path and filename", type: :string, required: true
9
+ option :direction, "Copy from or to an instance", type: :string, required: true
10
+ option :user, "Connect as a different user (optional)", type: :string
11
+
12
+ synopsis "Copy a file from or to a specific instance"
13
+
14
+ DIRECTIONS = %w( from to )
15
+
16
+ def execute
17
+ if @options[ :direction ] &&
18
+ DIRECTIONS.include?( @options[ :direction ] )
19
+ super
20
+ execute_scp
21
+ else
22
+ write(
23
+ message: "#{ @options[ :direction ] } is not a valid direction",
24
+ error: true
25
+ )
26
+ end
27
+ end
28
+
29
+ #----------------------------------------------------------------------------
30
+
31
+ def execute_scp
32
+ user_to_use = @options[ :user ] || user
33
+ base = "scp -o StrictHostKeyChecking=no -i #{ key } "
34
+
35
+ if @options[ :direction ] == 'to'
36
+ command = base + "#{ @options[ :local_file ] } #{ user_to_use }@" +
37
+ "#{ instance_details[ :dns ] }:#{ @options[ :remote_file ] }"
38
+ else
39
+ command = base + "#{ user_to_use }@#{ instance_details[ :dns ] }" +
40
+ ":#{ @options[ :remote_file ] } #{ @options[ :local_file ] }"
41
+ end
42
+
43
+ exec command
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,29 @@
1
+ module UnimatrixCLI
2
+ module Citadel
3
+ module Instance
4
+ class SshCommand < CitadelCommand
5
+
6
+ option :instance, "Keyword for the name of the instance", type: :string, required: true
7
+ option :user, "Connect as a different user (optional)", type: :string
8
+ option :commands, "Remote commands (optional)", type: :string
9
+
10
+ synopsis "Enter ssh shell on the specified instance"
11
+
12
+ def execute
13
+ super
14
+ execute_shell
15
+ end
16
+
17
+ #----------------------------------------------------------------------------
18
+
19
+ def execute_shell
20
+ command = base_command
21
+ if @options[ :commands ]
22
+ command += " #{ @options[ :commands ] }"
23
+ end
24
+ exec command
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ module UnimatrixCLI
2
+ module Citadel
3
+ module Instance
4
+ class StatusCommand < CitadelCommand
5
+
6
+ option :instance, "Keyword for the name of the instance", type: :string, required: :true
7
+
8
+ synopsis "Display bluepill status for the specified instance"
9
+
10
+ def execute
11
+ super
12
+ execute_tunnel( 'bluepilld status' )
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module UnimatrixCLI
2
+ module Citadel
3
+ module Instance
4
+ class UserDataCommand < CitadelCommand
5
+
6
+ option :instance, "Keyword for the name of the instance", type: :string, required: :true
7
+
8
+ synopsis "Display user_data for the specified instance"
9
+
10
+ def execute
11
+ super
12
+ execute_tunnel( 'cat /tmp/user-data' )
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module UnimatrixCLI
2
+ module Citadel
3
+ module Instance
4
+ class UserDataLogsCommand < CitadelCommand
5
+
6
+ option :instance, "Keyword for the name of the instance", type: :string, required: :true
7
+
8
+ synopsis "Display user_data logs for the specified instance"
9
+
10
+ def execute
11
+ super
12
+ execute_tunnel( 'cat /var/log/user-data.log' )
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end