kontena-plugin-app-command 0.1.0.rc1
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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.travis.yml +21 -0
- data/Dockerfile +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +191 -0
- data/README.md +299 -0
- data/Rakefile +6 -0
- data/kontena-plugin-app-command.gemspec +28 -0
- data/lib/kontena/cli/apps/build_command.rb +28 -0
- data/lib/kontena/cli/apps/common.rb +172 -0
- data/lib/kontena/cli/apps/config_command.rb +25 -0
- data/lib/kontena/cli/apps/deploy_command.rb +137 -0
- data/lib/kontena/cli/apps/docker_compose_generator.rb +61 -0
- data/lib/kontena/cli/apps/docker_helper.rb +80 -0
- data/lib/kontena/cli/apps/dockerfile_generator.rb +16 -0
- data/lib/kontena/cli/apps/init_command.rb +89 -0
- data/lib/kontena/cli/apps/kontena_yml_generator.rb +105 -0
- data/lib/kontena/cli/apps/list_command.rb +59 -0
- data/lib/kontena/cli/apps/logs_command.rb +37 -0
- data/lib/kontena/cli/apps/monitor_command.rb +93 -0
- data/lib/kontena/cli/apps/remove_command.rb +74 -0
- data/lib/kontena/cli/apps/restart_command.rb +39 -0
- data/lib/kontena/cli/apps/scale_command.rb +33 -0
- data/lib/kontena/cli/apps/service_generator.rb +114 -0
- data/lib/kontena/cli/apps/service_generator_v2.rb +27 -0
- data/lib/kontena/cli/apps/show_command.rb +23 -0
- data/lib/kontena/cli/apps/start_command.rb +40 -0
- data/lib/kontena/cli/apps/stop_command.rb +40 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/affinities_validator.rb +19 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/build_validator.rb +22 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/extends_validator.rb +20 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/hooks_validator.rb +54 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/secrets_validator.rb +22 -0
- data/lib/kontena/cli/apps/yaml/reader.rb +213 -0
- data/lib/kontena/cli/apps/yaml/service_extender.rb +77 -0
- data/lib/kontena/cli/apps/yaml/validations.rb +71 -0
- data/lib/kontena/cli/apps/yaml/validator.rb +38 -0
- data/lib/kontena/cli/apps/yaml/validator_v2.rb +53 -0
- data/lib/kontena/plugin/app-command/app_command.rb +21 -0
- data/lib/kontena/plugin/app-command/version.rb +7 -0
- data/lib/kontena_cli_plugin.rb +4 -0
- metadata +143 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module Kontena::Cli::Apps
|
2
|
+
module DockerHelper
|
3
|
+
|
4
|
+
# @param [Hash] services
|
5
|
+
# @param [Boolean] force_build
|
6
|
+
# @param [Boolean] no_cache
|
7
|
+
def process_docker_images(services, force_build = false, no_cache = false)
|
8
|
+
services.each do |name, service|
|
9
|
+
if service['build'] && (!image_exist?(service['image']) || force_build)
|
10
|
+
dockerfile = service['build']['dockerfile'] || 'Dockerfile'
|
11
|
+
raise ("'#{service['image']}' is not valid Docker image name") unless validate_image_name(service['image'])
|
12
|
+
raise ("'#{service['build']['context']}' does not have #{dockerfile}") unless dockerfile_exist?(service['build']['context'], dockerfile)
|
13
|
+
if service['hooks'] && service['hooks']['pre_build']
|
14
|
+
puts "Running pre_build hook".colorize(:cyan)
|
15
|
+
run_pre_build_hook(service['hooks']['pre_build'])
|
16
|
+
end
|
17
|
+
puts "Building image #{service['image'].colorize(:cyan)}"
|
18
|
+
build_docker_image(service, no_cache)
|
19
|
+
puts "Pushing image #{service['image'].colorize(:cyan)} to registry"
|
20
|
+
push_docker_image(service['image'])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [String] name
|
26
|
+
# @return [Boolean]
|
27
|
+
def validate_image_name(name)
|
28
|
+
!(/^[\w.\/\-:]+:?+[\w+.]+$/ =~ name).nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param [Hash] service
|
32
|
+
# @param [Boolean] no_cache
|
33
|
+
# @return [Integer]
|
34
|
+
def build_docker_image(service, no_cache = false)
|
35
|
+
dockerfile = dockerfile = service['build']['dockerfile'] || 'Dockerfile'
|
36
|
+
build_context = service['build']['context']
|
37
|
+
cmd = ['docker', 'build', '-t', service['image']]
|
38
|
+
cmd << ['-f', File.join(File.expand_path(build_context), dockerfile)] if dockerfile != "Dockerfile"
|
39
|
+
cmd << '--no-cache' if no_cache
|
40
|
+
args = service['build']['args'] || {}
|
41
|
+
args.each do |k, v|
|
42
|
+
cmd << "--build-arg=#{k}=#{v}"
|
43
|
+
end
|
44
|
+
cmd << build_context
|
45
|
+
ret = system(*cmd.flatten)
|
46
|
+
raise ("Failed to build image #{service['image'].colorize(:cyan)}") unless ret
|
47
|
+
ret
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [String] image
|
51
|
+
# @return [Integer]
|
52
|
+
def push_docker_image(image)
|
53
|
+
ret = system('docker', 'push', image)
|
54
|
+
raise ("Failed to push image #{image.colorize(:cyan)}") unless ret
|
55
|
+
ret
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param [String] image
|
59
|
+
# @return [Boolean]
|
60
|
+
def image_exist?(image)
|
61
|
+
system("docker history '#{image}' >/dev/null 2>/dev/null")
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param [String] path
|
65
|
+
# @param [String] dockerfile
|
66
|
+
# @return [Boolean]
|
67
|
+
def dockerfile_exist?(path, dockerfile)
|
68
|
+
file = File.join(File.expand_path(path), dockerfile)
|
69
|
+
File.exist?(file)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param [Hash] hook
|
73
|
+
def run_pre_build_hook(hook)
|
74
|
+
hook.each do |h|
|
75
|
+
ret = system(h['cmd'])
|
76
|
+
raise ("Failed to run pre_build hook: #{h['name']}!") unless ret
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require_relative 'common'
|
3
|
+
|
4
|
+
module Kontena::Cli::Apps
|
5
|
+
class DockerfileGenerator
|
6
|
+
include Common
|
7
|
+
|
8
|
+
def generate(base_image)
|
9
|
+
|
10
|
+
dockerfile = File.new('Dockerfile', 'w')
|
11
|
+
dockerfile.puts "FROM #{base_image}"
|
12
|
+
dockerfile.puts 'CMD ["/start", "web"]'
|
13
|
+
dockerfile.close
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require_relative 'common'
|
3
|
+
require_relative 'dockerfile_generator'
|
4
|
+
require_relative 'docker_compose_generator'
|
5
|
+
require_relative 'kontena_yml_generator'
|
6
|
+
|
7
|
+
module Kontena::Cli::Apps
|
8
|
+
class InitCommand < Kontena::Command
|
9
|
+
include Kontena::Cli::Common
|
10
|
+
include Common
|
11
|
+
|
12
|
+
option ["-f", "--file"], "FILE", "Specify a docker-compose file", attribute_name: :docker_compose_file, default: 'docker-compose.yml'
|
13
|
+
option ["-i", "--image-name"], "IMAGE_NAME", "Specify a docker image name"
|
14
|
+
option ["-b", "--base-image"], "BASE_IMAGE_NAME", "Specify a docker base image name", default: "kontena/buildstep"
|
15
|
+
option ["-p", "--project-name"], "NAME", "Specify an alternate project name (default: directory name)"
|
16
|
+
|
17
|
+
|
18
|
+
def execute
|
19
|
+
if File.exist?('Dockerfile')
|
20
|
+
puts 'Found Dockerfile'
|
21
|
+
elsif create_dockerfile?
|
22
|
+
puts "Creating #{'Dockerfile'.colorize(:cyan)}"
|
23
|
+
DockerfileGenerator.new.generate(base_image)
|
24
|
+
end
|
25
|
+
|
26
|
+
if File.exist?('Procfile')
|
27
|
+
procfile = ::YAML.safe_load(File.read('Procfile'))
|
28
|
+
else
|
29
|
+
procfile = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
app_env = create_env_file(app_json['env']) if app_json['env']
|
33
|
+
addons = app_json['addons'] || []
|
34
|
+
|
35
|
+
if File.exist?(docker_compose_file)
|
36
|
+
puts "Found #{docker_compose_file}."
|
37
|
+
elsif create_docker_compose_yml?
|
38
|
+
puts "Creating #{docker_compose_file.colorize(:cyan)}"
|
39
|
+
docker_compose_generator = DockerComposeGenerator.new(docker_compose_file)
|
40
|
+
docker_compose_generator.generate(procfile, addons, app_env)
|
41
|
+
end
|
42
|
+
|
43
|
+
if File.exist?('kontena.yml')
|
44
|
+
puts "Updating #{'kontena.yml'.colorize(:cyan)}"
|
45
|
+
else
|
46
|
+
puts "Creating #{'kontena.yml'.colorize(:cyan)}"
|
47
|
+
end
|
48
|
+
|
49
|
+
kontena_yml_generator = KontenaYmlGenerator.new(image_name, service_prefix)
|
50
|
+
if File.exist?(docker_compose_file)
|
51
|
+
kontena_yml_generator.generate_from_compose_file(docker_compose_file)
|
52
|
+
else
|
53
|
+
kontena_yml_generator.generate(procfile, addons, app_env)
|
54
|
+
end
|
55
|
+
|
56
|
+
puts "Your app is ready! Deploy with 'kontena app deploy'.".colorize(:green)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def service_prefix
|
63
|
+
@service_prefix ||= project_name || current_dir
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_dockerfile?
|
67
|
+
prompt.yes?('Dockerfile not found. Do you want to create it?')
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_env_file(env)
|
71
|
+
app_env = File.new('.env', 'w')
|
72
|
+
app_json['env'].each do |key, env|
|
73
|
+
if env['generator'] == 'secret'
|
74
|
+
value = SecureRandom.hex(64)
|
75
|
+
else
|
76
|
+
value = env['value']
|
77
|
+
end
|
78
|
+
app_env.puts "#{key}=#{value}"
|
79
|
+
end
|
80
|
+
app_env.close
|
81
|
+
'.env'
|
82
|
+
end
|
83
|
+
|
84
|
+
def create_docker_compose_yml?
|
85
|
+
prompt.yes?("#{docker_compose_file} not found. Do you want to create it?")
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Kontena::Cli::Apps
|
4
|
+
class KontenaYmlGenerator
|
5
|
+
include Common
|
6
|
+
|
7
|
+
attr_reader :image_name, :service_prefix
|
8
|
+
|
9
|
+
def initialize(image_name, service_prefix)
|
10
|
+
@image_name = image_name
|
11
|
+
@service_prefix = service_prefix
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_from_compose_file(docker_compose_file)
|
15
|
+
services = {}
|
16
|
+
# extend services from docker-compose.yml
|
17
|
+
file = File.read(docker_compose_file)
|
18
|
+
|
19
|
+
yml_services(file).each do |name, options|
|
20
|
+
services[name] = {'extends' => { 'file' => 'docker-compose.yml', 'service' => name }}
|
21
|
+
if options.has_key?('build')
|
22
|
+
image = image_name || "registry.kontena.local/#{File.basename(Dir.getwd)}:latest"
|
23
|
+
services[name]['image'] = image
|
24
|
+
end
|
25
|
+
|
26
|
+
# set Heroku addon service as stateful by default
|
27
|
+
if valid_addons.has_key?(name)
|
28
|
+
services[name]['stateful'] = true
|
29
|
+
end
|
30
|
+
|
31
|
+
# we have to generate Kontena urls to env vars for Heroku addons
|
32
|
+
# redis://openredis:6379 -> redis://project-name-openredis:6379
|
33
|
+
if options['links']
|
34
|
+
options['links'].each do |link|
|
35
|
+
service_link = link.split(':').first
|
36
|
+
if valid_addons.has_key?(service_link)
|
37
|
+
services[name]['environment'] ||= []
|
38
|
+
services[name]['environment'] += valid_addons(service_prefix)[service_link]['environment']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
create_yml(services, 'kontena.yml')
|
44
|
+
end
|
45
|
+
|
46
|
+
def yml_services(file)
|
47
|
+
yml = ::YAML.safe_load(file)
|
48
|
+
if yml['version'] == '2'
|
49
|
+
yml['services']
|
50
|
+
else
|
51
|
+
yml
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate(procfile, addons, env_file)
|
56
|
+
image = image_name || "registry.kontena.local/#{File.basename(Dir.getwd)}:latest"
|
57
|
+
if procfile.keys.size > 0
|
58
|
+
# generate services found in Procfile
|
59
|
+
services = {}
|
60
|
+
procfile.keys.each do |name|
|
61
|
+
services[name] = {'image' => image}
|
62
|
+
services[name]['environment'] = ['PORT=5000'] if app_json && name == 'web' # Heroku generates PORT env variable so should we do too
|
63
|
+
services[name]['command'] = "/start #{name}" if name != 'web'
|
64
|
+
services[name]['env_file'] = env_file if env_file
|
65
|
+
|
66
|
+
# generate addon services
|
67
|
+
addons.each do |addon|
|
68
|
+
addon_service = addon.split(":")[0]
|
69
|
+
addon_service.slice!('heroku-')
|
70
|
+
if valid_addons.has_key?(addon_service)
|
71
|
+
services[name]['links'] ||= []
|
72
|
+
services[name]['links'] << "#{addon_service}:#{addon_service}"
|
73
|
+
services[name]['environment'] ||= []
|
74
|
+
services[name]['environment'] += valid_addons(service_prefix)[addon_service]['environment']
|
75
|
+
services[addon_service] = {'image' => valid_addons[addon_service]['image'], 'stateful' => true}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
else
|
80
|
+
# no Procfile found, create dummy web service
|
81
|
+
services = {'web' => { 'image' => image}}
|
82
|
+
services['web']['env_file'] = env_file if env_file
|
83
|
+
end
|
84
|
+
# create kontena.yml file
|
85
|
+
create_yml(services, 'kontena.yml')
|
86
|
+
end
|
87
|
+
|
88
|
+
def create_yml(services, filename)
|
89
|
+
if File.exist?(filename) && !File.zero?(filename)
|
90
|
+
kontena_services = yml_services(File.read(filename))
|
91
|
+
services.each do |name, options|
|
92
|
+
if kontena_services[name]
|
93
|
+
services[name].merge!(kontena_services[name])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
kontena_services = {
|
98
|
+
'version' => '2',
|
99
|
+
'name' => service_prefix,
|
100
|
+
'services' => services
|
101
|
+
}
|
102
|
+
super(kontena_services, filename)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Kontena::Cli::Apps
|
4
|
+
class ListCommand < Kontena::Command
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
include Kontena::Cli::GridOptions
|
7
|
+
include Common
|
8
|
+
|
9
|
+
option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
|
10
|
+
option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
|
11
|
+
|
12
|
+
option ['-q', '--quiet'], :flag, "Output the identifying column only"
|
13
|
+
|
14
|
+
parameter "[SERVICE] ...", "Services to list"
|
15
|
+
|
16
|
+
attr_reader :services
|
17
|
+
|
18
|
+
def execute
|
19
|
+
require_config_file(filename)
|
20
|
+
|
21
|
+
@services = services_from_yaml(filename, service_list, service_prefix, true)
|
22
|
+
|
23
|
+
if quiet?
|
24
|
+
puts services.map(&:first).join("\n")
|
25
|
+
exit 0
|
26
|
+
end
|
27
|
+
|
28
|
+
if services.size > 0
|
29
|
+
show_services(services)
|
30
|
+
elsif !service_list.empty?
|
31
|
+
puts "No such service: #{service_list.join(', ')}".colorize(:red)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def show_services(services)
|
37
|
+
titles = ['NAME', 'IMAGE', 'INSTANCES', 'STATEFUL', 'STATE', 'PORTS']
|
38
|
+
puts "%-30.30s %-50.50s %-15s %-10.10s %-15.20s %-50s" % titles
|
39
|
+
|
40
|
+
services.each do |service_name, opts|
|
41
|
+
service = get_service(token, prefixed_name(service_name)) rescue false
|
42
|
+
if service
|
43
|
+
name = service['name'].sub("#{service_prefix}-", '')
|
44
|
+
state = service['stateful'] ? 'yes' : 'no'
|
45
|
+
ports = service['ports'].map{|p|
|
46
|
+
"#{p['ip']}:#{p['node_port']}->#{p['container_port']}/#{p['protocol']}"
|
47
|
+
}.join(", ")
|
48
|
+
running = service['instance_counts']['running']
|
49
|
+
desired = service['instances']
|
50
|
+
instances = "#{running} / #{desired}"
|
51
|
+
vars = [name, service['image'], instances, state, service['state'], ports]
|
52
|
+
else
|
53
|
+
vars = [service_name, '-', '-', '-', '-', '-']
|
54
|
+
end
|
55
|
+
puts "%-30.30s %-50.50s %-15.10s %-10.10s %-15.20s %-50s" % vars
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
require 'kontena/cli/helpers/log_helper'
|
3
|
+
|
4
|
+
module Kontena::Cli::Apps
|
5
|
+
class LogsCommand < Kontena::Command
|
6
|
+
include Kontena::Cli::Common
|
7
|
+
include Kontena::Cli::GridOptions
|
8
|
+
include Kontena::Cli::Helpers::LogHelper
|
9
|
+
|
10
|
+
include Common
|
11
|
+
|
12
|
+
option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
|
13
|
+
option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
|
14
|
+
parameter "[SERVICE] ...", "Show only specified service logs"
|
15
|
+
|
16
|
+
def execute
|
17
|
+
require_config_file(filename)
|
18
|
+
|
19
|
+
services = services_from_yaml(filename, service_list, service_prefix, true)
|
20
|
+
|
21
|
+
if services.empty? && !service_list.empty?
|
22
|
+
signal_error "No such service: #{service_list.join(', ')}"
|
23
|
+
elsif services.empty?
|
24
|
+
signal_error "No services for application"
|
25
|
+
end
|
26
|
+
|
27
|
+
query_services = services.map{|service_name, opts| prefixed_name(service_name)}.join ','
|
28
|
+
query_params = {
|
29
|
+
services: query_services,
|
30
|
+
}
|
31
|
+
|
32
|
+
show_logs("grids/#{current_grid}/container_logs", query_params) do |log|
|
33
|
+
show_log(log)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Kontena::Cli::Apps
|
4
|
+
class MonitorCommand < Kontena::Command
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
include Kontena::Cli::GridOptions
|
7
|
+
include Common
|
8
|
+
|
9
|
+
option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
|
10
|
+
option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
|
11
|
+
|
12
|
+
parameter "[SERVICE] ...", "Services to start"
|
13
|
+
|
14
|
+
attr_reader :services
|
15
|
+
|
16
|
+
def execute
|
17
|
+
require_config_file(filename)
|
18
|
+
|
19
|
+
@services = services_from_yaml(filename, service_list, service_prefix, true)
|
20
|
+
if services.size > 0
|
21
|
+
show_monitor(services)
|
22
|
+
elsif !service_list.empty?
|
23
|
+
puts "No such service: #{service_list.join(', ')}".colorize(:red)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def show_monitor(services)
|
28
|
+
require_api_url
|
29
|
+
token = require_token
|
30
|
+
loop do
|
31
|
+
nodes = {}
|
32
|
+
services.each do |name, data|
|
33
|
+
service = prefixed_name(name)
|
34
|
+
result = client(token).get("services/#{current_grid}/#{service}/containers") rescue nil
|
35
|
+
if result
|
36
|
+
services[name]['instances'] = result['containers'].size
|
37
|
+
result['containers'].each do |container|
|
38
|
+
container['service'] = name
|
39
|
+
nodes[container['node']['name']] ||= []
|
40
|
+
nodes[container['node']['name']] << container
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
clear_terminal
|
45
|
+
puts "services:"
|
46
|
+
services.each do |name, data|
|
47
|
+
color = color_for_service(name)
|
48
|
+
puts " #{"■".colorize(color)} #{name} (#{data['instances']} instances)"
|
49
|
+
end
|
50
|
+
puts "nodes:"
|
51
|
+
node_names = nodes.keys.sort
|
52
|
+
node_names.each do |name|
|
53
|
+
containers = nodes[name]
|
54
|
+
puts " #{name} (#{containers.size} instances)"
|
55
|
+
print " "
|
56
|
+
containers.each do |container|
|
57
|
+
icon = "■"
|
58
|
+
if container['status'] != 'running'
|
59
|
+
icon = "□"
|
60
|
+
end
|
61
|
+
color = color_for_service(container['service'])
|
62
|
+
print icon.colorize(color)
|
63
|
+
end
|
64
|
+
puts ''
|
65
|
+
end
|
66
|
+
sleep 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def color_for_service(service)
|
71
|
+
color_maps[service] = colors.shift unless color_maps[service]
|
72
|
+
color_maps[service].to_sym
|
73
|
+
end
|
74
|
+
|
75
|
+
def color_maps
|
76
|
+
@color_maps ||= {}
|
77
|
+
end
|
78
|
+
|
79
|
+
def colors
|
80
|
+
if(@colors.nil? || @colors.size == 0)
|
81
|
+
@colors = %i(
|
82
|
+
red green yellow blue magenta cyan bright_red bright_green
|
83
|
+
bright_yellow bright_blue bright_magenta bright_cyan
|
84
|
+
)
|
85
|
+
end
|
86
|
+
@colors
|
87
|
+
end
|
88
|
+
|
89
|
+
def clear_terminal
|
90
|
+
print "\e[H\e[2J"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|