docker-builder 0.1.50 → 0.1.55
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/{Rakefile → Rakefile_temp} +8 -7
- data/install_local.sh +1 -1
- data/lib/docker_builder.rb +4 -1
- data/lib/docker_builder/cli.rb +23 -56
- data/lib/docker_builder/config.rb +75 -34
- data/lib/docker_builder/config/dsl.rb +17 -1
- data/lib/docker_builder/{manager.rb → manager_container.rb} +25 -308
- data/lib/docker_builder/manager_image.rb +103 -0
- data/lib/docker_builder/provisioner/base.rb +188 -0
- data/lib/docker_builder/provisioner/{provisioner_chef.rb → chef.rb} +5 -1
- data/lib/docker_builder/server_settings.rb +34 -64
- data/lib/docker_builder/version.rb +1 -1
- data/readme.md +118 -79
- data/temp_config.rb +17 -0
- metadata +8 -58
- data/examples/example-apps-php/.chef/knife.rb +0 -6
- data/examples/example-apps-php/config.rb +0 -19
- data/examples/example-apps-php/servers/apps-php/.chef/knife.rb +0 -2
- data/examples/example-apps-php/servers/apps-php/config.rb +0 -69
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/README.md +0 -1
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/metadata.rb +0 -9
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/recipes/build.rb +0 -43
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/recipes/install.rb +0 -55
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/recipes/install_app.rb +0 -27
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/recipes/install_host.rb +0 -9
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/templates/index.html.erb +0 -4
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/templates/nginx-sites/app.conf.erb +0 -55
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/templates/nginx-sites/app.conf.erb.1 +0 -55
- data/examples/example-apps-php/servers/apps-php/cookbooks/apps-php/templates/nginx-sites/default.conf.erb +0 -45
- data/examples/example-apps-php/servers/apps-php/readme.md +0 -0
- data/examples/example-apps-php/temp/apps-php.1.json +0 -14
- data/examples/example-base-chef/.chef/knife.rb +0 -6
- data/examples/example-base-chef/config.rb +0 -19
- data/examples/example-base-chef/readme.md +0 -8
- data/examples/example-base-chef/servers/my/.chef/knife.rb +0 -2
- data/examples/example-base-chef/servers/my/config.rb +0 -53
- data/examples/example-base-chef/servers/my/cookbooks/my/README.md +0 -1
- data/examples/example-base-chef/servers/my/cookbooks/my/metadata.rb +0 -9
- data/examples/example-base-chef/servers/my/cookbooks/my/recipes/build.rb +0 -23
- data/examples/example-base-chef/servers/my/cookbooks/my/recipes/install.rb +0 -0
- data/examples/example-base-chef/servers/my/cookbooks/my/recipes/install_host.rb +0 -9
- data/examples/example-base-chef/servers/my/readme.md +0 -0
- data/examples/example-kafka-zookeeper/config.rb +0 -87
- data/examples/example-kafka-zookeeper/servers/kafka/Dockerfile +0 -21
- data/examples/example-kafka-zookeeper/servers/kafka/README.md +0 -78
- data/examples/example-kafka-zookeeper/servers/kafka/files/LICENSE +0 -202
- data/examples/example-kafka-zookeeper/servers/kafka/files/broker-list.sh +0 -5
- data/examples/example-kafka-zookeeper/servers/kafka/files/create-topics.sh +0 -32
- data/examples/example-kafka-zookeeper/servers/kafka/files/download-kafka.sh +0 -5
- data/examples/example-kafka-zookeeper/servers/kafka/files/start-kafka-shell.sh +0 -2
- data/examples/example-kafka-zookeeper/servers/kafka/files/start-kafka.sh +0 -67
- data/examples/example-nginx/config.rb +0 -19
- data/examples/example-nginx/servers/nginx/.chef/knife.rb +0 -8
- data/examples/example-nginx/servers/nginx/config.rb +0 -55
- data/examples/example-nginx/servers/nginx/cookbooks/nginx/README.md +0 -1
- data/examples/example-nginx/servers/nginx/cookbooks/nginx/metadata.rb +0 -9
- data/examples/example-nginx/servers/nginx/cookbooks/nginx/recipes/build.rb +0 -10
- data/examples/example-nginx/servers/nginx/cookbooks/nginx/recipes/install.rb +0 -28
- data/examples/example-nginx/servers/nginx/cookbooks/nginx/recipes/install_host.rb +0 -9
- data/examples/example-nginx/servers/nginx/cookbooks/nginx/templates/index.html.erb +0 -4
- data/examples/example-nginx/servers/nginx/cookbooks/nginx/templates/nginx-sites/default.conf.erb +0 -45
- data/examples/example-nginx/servers/nginx/readme.md +0 -0
- data/examples/example-nginx/servers/nginx/scripts/install.sh +0 -3
- data/examples/example-nginx/temp/ex_nginx.json +0 -20
- data/examples/example-nginx/temp/nginx.json +0 -1
- data/examples/example-swarm-redis/config.rb +0 -61
- data/examples/example-swarm-redis/readme.md +0 -10
- data/lib/docker_builder/temp.rb +0 -1
@@ -1,5 +1,5 @@
|
|
1
1
|
module DockerBuilder
|
2
|
-
class
|
2
|
+
class ManagerContainer
|
3
3
|
|
4
4
|
def self.save_chef_config(settings)
|
5
5
|
require 'json'
|
@@ -24,59 +24,6 @@ class Manager
|
|
24
24
|
true
|
25
25
|
end
|
26
26
|
|
27
|
-
###
|
28
|
-
def self.build_image(server_name, settings=nil)
|
29
|
-
puts "building image for #{server_name}..."
|
30
|
-
#puts "settings: #{settings}"
|
31
|
-
#puts "debug: #{settings['properties']}"
|
32
|
-
|
33
|
-
#settings = load_settings(server_name)
|
34
|
-
|
35
|
-
t = settings['build']['build_type']
|
36
|
-
if t=='' || t=='none'
|
37
|
-
#
|
38
|
-
puts "no build needed..."
|
39
|
-
|
40
|
-
elsif t.downcase=='dockerfile'
|
41
|
-
return build_image_with_dockerfile(settings)
|
42
|
-
elsif t=='chef'
|
43
|
-
return build_image_with_chef(settings)
|
44
|
-
elsif t=='packer'
|
45
|
-
return build_image_with_packer(settings)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.build_image_with_dockerfile(settings)
|
50
|
-
puts "build image with Dockerfile"
|
51
|
-
|
52
|
-
#cmd %Q(cd #{name} && docker build -t #{settings.image_name} . )
|
53
|
-
cmd %Q(docker build -t #{settings.image_name} #{settings.dir_server_root} )
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.build_image_with_chef(settings)
|
58
|
-
puts "build image with chef"
|
59
|
-
|
60
|
-
# config json
|
61
|
-
save_chef_config(settings)
|
62
|
-
|
63
|
-
# check node
|
64
|
-
cmd %Q(cd #{Config.root_path} && chef exec knife node show #{settings.chef_node_name} -c #{chef_config_knife_path})
|
65
|
-
|
66
|
-
|
67
|
-
#cmd %Q(SERVER_NAME=#{settings.name} SERVER_PATH=#{settings.dir_server_root} chef exec chef-client -z -N #{settings.image_name} -j #{settings.filename_config_json} -c #{chef_config_knife_path} #{chef_recipe_path('chef_build_image.rb')} )
|
68
|
-
res_recipe = run_chef_recipe(settings, 'chef_build_image.rb')
|
69
|
-
end
|
70
|
-
|
71
|
-
|
72
|
-
def self.build_image_with_packer(settings)
|
73
|
-
require_relative '../../lib/docker_builder/builder/packer'
|
74
|
-
|
75
|
-
puts "build image with packer"
|
76
|
-
|
77
|
-
builder = DockerBuilder::Builder::Packer.new(settings)
|
78
|
-
builder.build
|
79
|
-
end
|
80
27
|
|
81
28
|
|
82
29
|
|
@@ -86,84 +33,29 @@ class Manager
|
|
86
33
|
puts "creating and running container.."
|
87
34
|
#settings = load_settings(server_name)
|
88
35
|
|
36
|
+
# generate config
|
37
|
+
save_config_json(settings)
|
38
|
+
|
39
|
+
|
89
40
|
# destroy
|
90
41
|
destroy_container(server_name, settings)
|
91
42
|
|
43
|
+
# create
|
44
|
+
create_container(settings)
|
92
45
|
|
93
|
-
# provision host before running container
|
94
|
-
res_install = _install_container_provision_host(settings)
|
95
46
|
|
96
|
-
# run
|
97
|
-
|
47
|
+
# START && run provision after start
|
48
|
+
start_container(name, settings)
|
98
49
|
|
99
50
|
|
100
51
|
# TODO: systemd service
|
101
52
|
#res_service = _install_service_container(settings)
|
102
53
|
|
103
54
|
|
104
|
-
end
|
105
|
-
|
106
|
-
def self._install_container_provision_host(settings)
|
107
|
-
script_type = (settings['install']['host']['script_type'] rescue nil)
|
108
|
-
return true unless script_type
|
109
|
-
|
110
|
-
# run provision script on the host machine
|
111
|
-
if script_type=='chef_recipe'
|
112
|
-
return _install_container_provision_host_chef_recipe(settings)
|
113
|
-
else
|
114
|
-
# do nothing
|
115
|
-
end
|
116
|
-
|
117
|
-
true
|
118
|
-
end
|
119
|
-
|
120
|
-
|
121
|
-
#
|
122
|
-
def self._install_container_provision_host_chef_recipe(settings)
|
123
|
-
# run script on host machine
|
124
|
-
script_name = settings['install']['host']['script'] || 'install_host'
|
125
|
-
|
126
|
-
# check script exists
|
127
|
-
#script_path = "#{settings.name}/cookbooks/#{settings.name}/recipes/#{script_name}.rb"
|
128
|
-
#f = File.expand_path('.', script_path)
|
129
|
-
|
130
|
-
#if !File.exists?(f)
|
131
|
-
# puts "script not found: #{f}. Skipping"
|
132
|
-
# return false
|
133
|
-
#end
|
134
|
-
|
135
|
-
#puts "pwd= #{Dir.pwd}"
|
136
|
-
#puts "root = #{Config.root_path}"
|
137
|
-
#exit
|
138
|
-
|
139
|
-
#
|
140
|
-
res_chef = run_chef_recipe_server_recipe(settings, script_name)
|
141
|
-
#cmd %Q(SERVER_NAME=#{settings.name} chef-client -z -N #{settings.name} --override-runlist 'recipe[#{settings.name}::#{script_name}]' )
|
142
|
-
|
143
|
-
return true
|
144
|
-
end
|
145
|
-
|
146
|
-
# run
|
147
|
-
def self._run_container(settings)
|
148
|
-
puts "run container ..."
|
149
|
-
|
150
|
-
# generate config
|
151
|
-
save_config_json(settings)
|
152
|
-
|
153
|
-
# create
|
154
|
-
create_container(settings)
|
155
|
-
|
156
|
-
setup_network(settings)
|
157
|
-
|
158
|
-
|
159
|
-
### START && run provision after start
|
160
|
-
start_container(name, settings)
|
161
|
-
|
162
55
|
true
|
163
56
|
end
|
164
57
|
|
165
58
|
def self.create_container(settings)
|
166
|
-
#puts "networks: #{settings['docker']['network']}"
|
167
59
|
# create
|
168
60
|
net_options = ""
|
169
61
|
networks = settings['docker'].fetch('network', {}).fetch('networks', [])
|
@@ -177,6 +69,9 @@ class Manager
|
|
177
69
|
|
178
70
|
|
179
71
|
cmd %Q(docker create --name #{settings.container_name} #{net_options} #{settings.docker_ports_string} #{settings.docker_volumes_string} #{settings.docker_volumes_from_string} #{settings.docker_links_string} #{settings.run_extra_options_string} #{settings.run_env_variables_string} #{settings.image_name} #{settings['docker']['command']} #{settings['docker']['run_options']})
|
72
|
+
|
73
|
+
# network
|
74
|
+
setup_network(settings)
|
180
75
|
end
|
181
76
|
|
182
77
|
|
@@ -196,8 +91,6 @@ class Manager
|
|
196
91
|
# skip first network
|
197
92
|
next if ind==1
|
198
93
|
|
199
|
-
|
200
|
-
|
201
94
|
# connect
|
202
95
|
ip = net['ip']
|
203
96
|
s_ip = "--ip #{ip}" if ip
|
@@ -215,34 +108,28 @@ class Manager
|
|
215
108
|
|
216
109
|
|
217
110
|
|
111
|
+
|
218
112
|
def self.start_container(name, settings)
|
219
113
|
### BEFORE START
|
220
|
-
|
221
114
|
# run setup provision scripts
|
222
|
-
|
223
|
-
if setup_scripts
|
224
|
-
setup_scripts.each do |script|
|
225
|
-
_run_setup_script(settings, script)
|
226
|
-
end
|
227
|
-
end
|
115
|
+
DockerBuilder::Provisioner::Base.run_provision_scripts_setup(settings)
|
228
116
|
|
229
117
|
|
230
|
-
|
231
|
-
prepare_before_start(settings)
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
# start
|
118
|
+
### start
|
236
119
|
cmd %Q(docker start #{settings.container_name})
|
237
120
|
|
238
121
|
# wait
|
239
122
|
wait_until_running(settings.container_name)
|
240
123
|
|
124
|
+
### AFTER START
|
125
|
+
|
241
126
|
# setup
|
242
127
|
setup_container_after_start(settings)
|
243
128
|
|
244
129
|
# provision after start
|
245
|
-
|
130
|
+
# run bootstrap provision scripts
|
131
|
+
DockerBuilder::Provisioner::Base.run_provision_scripts_bootstrap(settings)
|
132
|
+
|
246
133
|
end
|
247
134
|
|
248
135
|
|
@@ -261,33 +148,18 @@ class Manager
|
|
261
148
|
assert res, "Container #{container_name} is not running"
|
262
149
|
end
|
263
150
|
|
264
|
-
def self.prepare_before_start(settings)
|
265
|
-
puts "prepare_before_start"
|
266
|
-
|
267
|
-
# prepare for chef scripts
|
268
|
-
bootstrap_scripts = (settings['provision']['bootstrap'] rescue [])
|
269
|
-
|
270
|
-
# before start
|
271
|
-
if bootstrap_scripts
|
272
|
-
bootstrap_scripts.each do |script|
|
273
|
-
if script['type']=='chef'
|
274
|
-
_prepare_provision_before_start_chef(settings, script)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
end
|
280
151
|
|
152
|
+
=begin
|
281
153
|
def self._prepare_provision_before_start_chef(settings, script)
|
282
154
|
puts "_prepare_provision_before_start_chef"
|
283
155
|
|
284
156
|
require_relative '../../lib/docker_builder/provisioner/provisioner_chef'
|
285
157
|
|
286
|
-
provisioner = DockerBuilder::Provisioner::
|
158
|
+
provisioner = DockerBuilder::Provisioner::Chef.new(settings)
|
287
159
|
provisioner.copy_config_file
|
288
160
|
|
289
161
|
end
|
290
|
-
|
162
|
+
=end
|
291
163
|
|
292
164
|
def self.setup_container_after_start(settings)
|
293
165
|
|
@@ -315,87 +187,6 @@ class Manager
|
|
315
187
|
|
316
188
|
|
317
189
|
|
318
|
-
def self.run_provision_after_start(settings)
|
319
|
-
puts "run_provision_after_start"
|
320
|
-
|
321
|
-
# run bootstrap provision scripts
|
322
|
-
bootstrap_scripts = (settings['provision']['bootstrap'] rescue [])
|
323
|
-
if bootstrap_scripts
|
324
|
-
bootstrap_scripts.each do |script|
|
325
|
-
_run_bootstrap_script(settings, script)
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
|
330
|
-
=begin
|
331
|
-
# commented - 2017-02-22
|
332
|
-
|
333
|
-
#
|
334
|
-
install_node_script_type = (settings['install']['node']['script_type'] rescue nil)
|
335
|
-
install_bootstrap_script = (settings['install']['bootstrap']['script'] rescue nil)
|
336
|
-
|
337
|
-
if install_node_script_type && install_node_script_type=='chef_recipe'
|
338
|
-
# run container and provision with chef
|
339
|
-
#_run_container_chef(settings)
|
340
|
-
|
341
|
-
# ???
|
342
|
-
#_provision_container_chef_recipe(settings)
|
343
|
-
|
344
|
-
elsif install_node_script_type && install_node_script_type=='shell'
|
345
|
-
# docker run
|
346
|
-
#create_and_run_container(settings)
|
347
|
-
|
348
|
-
# provision with shell script
|
349
|
-
run_shell_script_in_container(settings, "install.sh")
|
350
|
-
|
351
|
-
else
|
352
|
-
# no script for provision
|
353
|
-
#_run_container_docker(settings)
|
354
|
-
|
355
|
-
# docker run
|
356
|
-
#create_and_run_container(settings)
|
357
|
-
|
358
|
-
end
|
359
|
-
|
360
|
-
# bootstrap
|
361
|
-
if install_bootstrap_script
|
362
|
-
#script = settings['install']['bootstrap']['script'] || '/opt/bootstrap/bootstrap.sh'
|
363
|
-
|
364
|
-
# bootstsrap with shell script
|
365
|
-
run_bootstrap_shell_script_in_container(settings, install_bootstrap_script)
|
366
|
-
end
|
367
|
-
=end
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
true
|
372
|
-
end
|
373
|
-
|
374
|
-
def self._run_bootstrap_script(settings, script)
|
375
|
-
puts "run BS script #{script}"
|
376
|
-
|
377
|
-
if script['type']=='shell' && script['run_from']=='host'
|
378
|
-
return _run_bootstrap_script_shell_from_host(settings, script)
|
379
|
-
elsif script['type']=='shell' && (script['run_from'].nil? || script['run_from']=='')
|
380
|
-
_run_bootstrap_script_shell_in_container(settings, script)
|
381
|
-
end
|
382
|
-
|
383
|
-
return nil
|
384
|
-
end
|
385
|
-
|
386
|
-
|
387
|
-
def self._run_bootstrap_script_shell_from_host(settings, script)
|
388
|
-
cmd %Q(cd #{settings.dir_server_root} && #{script['script']} )
|
389
|
-
|
390
|
-
end
|
391
|
-
|
392
|
-
|
393
|
-
def self._run_bootstrap_script_shell_in_container(settings, script)
|
394
|
-
script_path = script['script']
|
395
|
-
# exec
|
396
|
-
cmd %Q(docker exec #{settings.container_name} #{script_path} )
|
397
|
-
end
|
398
|
-
|
399
190
|
|
400
191
|
|
401
192
|
|
@@ -421,36 +212,8 @@ class Manager
|
|
421
212
|
end
|
422
213
|
|
423
214
|
|
424
|
-
### provision
|
425
|
-
|
426
|
-
def self._run_setup_script(settings, script)
|
427
|
-
if script['type']=='shell'
|
428
|
-
return _run_setup_script_shell_from_host(settings, script)
|
429
|
-
end
|
430
|
-
|
431
|
-
return nil
|
432
|
-
end
|
433
|
-
|
434
|
-
def self._run_setup_script_shell_from_host(settings, script)
|
435
|
-
cmd %Q(cd #{settings.dir_server_root} && #{script['script']} )
|
436
|
-
end
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
def self._provision_container_chef_recipe(settings)
|
442
|
-
puts "provisioning container #{settings.container_name}"
|
443
|
-
|
444
|
-
#cmd %Q(SERVER_NAME=#{settings.name} chef-client -z -N #{settings.name} #{settings.name}/cookbooks/#{settings.name}/ )
|
445
|
-
end
|
446
215
|
|
447
216
|
=begin
|
448
|
-
|
449
|
-
def self._run_container_docker(settings)
|
450
|
-
|
451
|
-
end
|
452
|
-
=end
|
453
|
-
|
454
217
|
def self._run_container_chef(settings)
|
455
218
|
# generate json config for chef
|
456
219
|
save_chef_config(settings)
|
@@ -467,47 +230,13 @@ class Manager
|
|
467
230
|
res_chef
|
468
231
|
end
|
469
232
|
|
233
|
+
=end
|
470
234
|
|
471
|
-
def self.destroy_image(server_name, settings={})
|
472
|
-
puts "destroying image for server #{server_name}"
|
473
|
-
|
474
|
-
cmd %Q(docker rmi #{settings.image_name} )
|
475
|
-
cmd %Q(docker rm -f chef.converge.#{settings.image_name} )
|
476
|
-
|
477
|
-
# delete chef data
|
478
|
-
if settings['build']['build_type']=='chef'
|
479
|
-
return destroy_image_chef(settings)
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
|
484
|
-
def self.destroy_image_chef(settings)
|
485
|
-
puts "destroying image with chef..."
|
486
|
-
|
487
|
-
# config json
|
488
|
-
save_chef_config(settings)
|
489
|
-
|
490
|
-
# destroy temp container
|
491
|
-
cmd %Q(docker rm -f chef-converge.#{settings.image_name} )
|
492
|
-
|
493
|
-
#
|
494
|
-
cmd %Q(cd #{Config.root_path} && chef exec knife node delete #{settings.chef_node_name} -y -c #{chef_config_knife_path})
|
495
|
-
|
496
|
-
res_recipe = run_chef_recipe(settings, 'chef_destroy_image.rb')
|
497
|
-
|
498
|
-
chef_remove_data(settings)
|
499
235
|
|
500
|
-
# work - before 2016-nov-19
|
501
|
-
#cmd %Q(cd #{Config.root_path} && chef exec knife node delete #{settings.chef_node_name} -y -c #{chef_config_knife_path})
|
502
236
|
|
503
|
-
# clean chef client, node
|
504
|
-
#cmd %Q(cd #{Config.root_path} && rm -f #{settings.filename_chef_node_json} )
|
505
|
-
#cmd %Q(cd #{Config.root_path} && rm -f #{settings.filename_chef_client_json} )
|
506
|
-
end
|
507
237
|
|
508
238
|
###
|
509
239
|
|
510
|
-
|
511
240
|
def self.destroy_container(server_name, settings)
|
512
241
|
puts "destroying container #{server_name}..."
|
513
242
|
|
@@ -581,6 +310,7 @@ class Manager
|
|
581
310
|
return true
|
582
311
|
end
|
583
312
|
|
313
|
+
|
584
314
|
###
|
585
315
|
def self.clear_cache(name, settings)
|
586
316
|
# common cache
|
@@ -604,19 +334,6 @@ class Manager
|
|
604
334
|
end
|
605
335
|
|
606
336
|
|
607
|
-
### helpers - shell
|
608
|
-
|
609
|
-
def self.run_shell_script_in_container(settings, script_name)
|
610
|
-
script_path = settings.make_path_full("scripts/#{script_name}")
|
611
|
-
|
612
|
-
# copy
|
613
|
-
cmd %Q(cd #{Config.root_path} && docker cp #{script_path} #{settings.container_name}:/tmp/#{script_name} )
|
614
|
-
|
615
|
-
# exec
|
616
|
-
cmd %Q(docker exec #{settings.container_name} chmod +x /tmp/#{script_name} )
|
617
|
-
cmd %Q(docker exec #{settings.container_name} /tmp/#{script_name} )
|
618
|
-
end
|
619
|
-
|
620
337
|
|
621
338
|
### helpers - chef
|
622
339
|
|