unimatrix-cli 2.3.0 → 2.4.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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/unimatrix_cli.rb +11 -29
- data/lib/unimatrix_cli/command.rb +0 -1
- data/lib/unimatrix_cli/config/acceptance.yml +2 -2
- data/lib/unimatrix_cli/config/configuration.rb +1 -1
- data/lib/unimatrix_cli/config/production.yml +2 -2
- data/lib/unimatrix_cli/config/staging.yml +2 -2
- data/lib/unimatrix_cli/iris/stream_converter/create_command.rb +48 -0
- data/lib/unimatrix_cli/iris/stream_converter/describe_command.rb +37 -0
- data/lib/unimatrix_cli/keymaker/policy/destroy_command.rb +69 -0
- data/lib/unimatrix_cli/keymaker/policy/write_command.rb +156 -0
- data/lib/unimatrix_cli/keymaker/resource/list_command.rb +37 -0
- data/lib/unimatrix_cli/keymaker/resource_owner/find_command.rb +76 -0
- data/lib/unimatrix_cli/keymaker/resource_owner/invite_command.rb +60 -0
- data/lib/unimatrix_cli/keymaker/resource_owner/policies_command.rb +66 -0
- data/lib/unimatrix_cli/keymaker/resource_owner/remove_command.rb +92 -0
- data/lib/unimatrix_cli/keymaker/resource_server/list_command.rb +32 -0
- data/lib/unimatrix_cli/login_command.rb +1 -1
- data/lib/unimatrix_cli/version.rb +1 -1
- data/unimatrix-cli.gemspec +1 -1
- metadata +15 -31
- data/lib/unimatrix_cli/citadel/app/build_command.rb +0 -28
- data/lib/unimatrix_cli/citadel/app/console_command.rb +0 -17
- data/lib/unimatrix_cli/citadel/app/db_setup_command.rb +0 -17
- data/lib/unimatrix_cli/citadel/app/environment_command.rb +0 -30
- data/lib/unimatrix_cli/citadel/app/logs_command.rb +0 -17
- data/lib/unimatrix_cli/citadel/app/migrate_command.rb +0 -17
- data/lib/unimatrix_cli/citadel/app/migrate_status_command.rb +0 -17
- data/lib/unimatrix_cli/citadel/app/rake_command.rb +0 -18
- data/lib/unimatrix_cli/citadel/app/routes_command.rb +0 -17
- data/lib/unimatrix_cli/citadel/citadel_command.rb +0 -285
- data/lib/unimatrix_cli/citadel/instance/details_command.rb +0 -18
- data/lib/unimatrix_cli/citadel/instance/scp_command.rb +0 -48
- data/lib/unimatrix_cli/citadel/instance/ssh_command.rb +0 -29
- data/lib/unimatrix_cli/citadel/instance/status_command.rb +0 -17
- data/lib/unimatrix_cli/citadel/instance/user_data_command.rb +0 -17
- data/lib/unimatrix_cli/citadel/instance/user_data_logs_command.rb +0 -17
- data/lib/unimatrix_cli/zephyrus/input/create_command.rb +0 -40
- data/lib/unimatrix_cli/zephyrus/input/describe_command.rb +0 -35
- data/lib/unimatrix_cli/zephyrus/input/list_command.rb +0 -38
- data/lib/unimatrix_cli/zephyrus/output/list_command.rb +0 -44
- data/lib/unimatrix_cli/zephyrus/recording/configuration_command.rb +0 -43
- data/lib/unimatrix_cli/zephyrus/rendition/list_command.rb +0 -40
- data/lib/unimatrix_cli/zephyrus/routing/configuration_command.rb +0 -43
- data/lib/unimatrix_cli/zephyrus/transcoding/configuration_command.rb +0 -55
- data/lib/unimatrix_cli/zephyrus/transcribing/configuration_command.rb +0 -55
- data/lib/unimatrix_cli/zephyrus/transmutation/configuration_command.rb +0 -55
@@ -1,28 +0,0 @@
|
|
1
|
-
module UnimatrixCLI
|
2
|
-
module Citadel
|
3
|
-
module App
|
4
|
-
class BuildCommand < CitadelCommand
|
5
|
-
|
6
|
-
option :builder, "Keyword for the name of the build server", type: :string, required: true
|
7
|
-
option :app, "Keyword for the name of the app to build", type: :string, required: true
|
8
|
-
option :branch, "The recipe or branch to build from (optional)", type: :string
|
9
|
-
|
10
|
-
synopsis "Build a package of the specified app"
|
11
|
-
|
12
|
-
def execute
|
13
|
-
if load_environment
|
14
|
-
execute_build
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
#----------------------------------------------------------------------------
|
19
|
-
|
20
|
-
def execute_build
|
21
|
-
command = base_command + " -t do_it #{ @options[ :builder ] } " +
|
22
|
-
"#{ @options[ :app ] } #{ @options[ :branch ] }"
|
23
|
-
system( command )
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module UnimatrixCLI
|
2
|
-
module Citadel
|
3
|
-
module App
|
4
|
-
class ConsoleCommand < CitadelCommand
|
5
|
-
|
6
|
-
option :app, "Keyword for the name of the app", type: :string, required: true
|
7
|
-
|
8
|
-
synopsis "Enter the console on a Rails-based API instance"
|
9
|
-
|
10
|
-
def execute
|
11
|
-
super
|
12
|
-
execute_command( 'console' )
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module UnimatrixCLI
|
2
|
-
module Citadel
|
3
|
-
module App
|
4
|
-
class DbSetupCommand < CitadelCommand
|
5
|
-
|
6
|
-
option :app, "Keyword for the name of the app", type: :string, required: true
|
7
|
-
|
8
|
-
synopsis "Set up the database on a Rails-based API instance"
|
9
|
-
|
10
|
-
def execute
|
11
|
-
super
|
12
|
-
execute_command( 'db:setup' )
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module UnimatrixCLI
|
2
|
-
module Citadel
|
3
|
-
module App
|
4
|
-
class EnvironmentCommand < CitadelCommand
|
5
|
-
|
6
|
-
option :app, "Keyword for the name of the app", type: :string, required: true
|
7
|
-
option :set, "Set env variables in the format KEY=value", type: :string
|
8
|
-
option :unset, "Remove env variables in the format KEY=value", type: :string
|
9
|
-
|
10
|
-
synopsis "Display or change environment variables"
|
11
|
-
|
12
|
-
def execute
|
13
|
-
super
|
14
|
-
|
15
|
-
if @options[ :set ]
|
16
|
-
@options[ :commands ] = "set '#{ @options[ :set ] }'"
|
17
|
-
elsif @options[ :unset ]
|
18
|
-
@options[ :commands ] = "unset '#{ @options[ :unset ] }'"
|
19
|
-
end
|
20
|
-
|
21
|
-
if @options[ :commands ]
|
22
|
-
execute_command( 'environment' )
|
23
|
-
else
|
24
|
-
execute_tunnel( 'cat /etc/environment' )
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module UnimatrixCLI
|
2
|
-
module Citadel
|
3
|
-
module App
|
4
|
-
class LogsCommand < CitadelCommand
|
5
|
-
|
6
|
-
option :app, "Keyword for the name of the app", type: :string, required: true
|
7
|
-
|
8
|
-
synopsis "Tail logs on the specified app"
|
9
|
-
|
10
|
-
def execute
|
11
|
-
super
|
12
|
-
execute_command( 'logs' )
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module UnimatrixCLI
|
2
|
-
module Citadel
|
3
|
-
module App
|
4
|
-
class MigrateCommand < CitadelCommand
|
5
|
-
|
6
|
-
option :app, "Keyword for the name of the app", type: :string, required: true
|
7
|
-
|
8
|
-
synopsis "Migrate the database on a Rails-based API instance"
|
9
|
-
|
10
|
-
def execute
|
11
|
-
super
|
12
|
-
execute_command( 'migrate' )
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module UnimatrixCLI
|
2
|
-
module Citadel
|
3
|
-
module App
|
4
|
-
class MigrateStatusCommand < CitadelCommand
|
5
|
-
|
6
|
-
option :app, "Keyword for the name of the app", type: :string, required: true
|
7
|
-
|
8
|
-
synopsis "Check database migration status on a Rails-based API instance"
|
9
|
-
|
10
|
-
def execute
|
11
|
-
super
|
12
|
-
execute_command( 'migrate:status' )
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module UnimatrixCLI
|
2
|
-
module Citadel
|
3
|
-
module App
|
4
|
-
class RakeCommand < CitadelCommand
|
5
|
-
|
6
|
-
option :app, "Keyword for the name of the app", type: :string, required: true
|
7
|
-
option :commands, "The rake task commands", type: :string
|
8
|
-
|
9
|
-
synopsis "Run a rake task on a Rails-based API instance"
|
10
|
-
|
11
|
-
def execute
|
12
|
-
super
|
13
|
-
execute_command( 'rake' )
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,17 +0,0 @@
|
|
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
|
@@ -1,285 +0,0 @@
|
|
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
|