kontena-plugin-app-command 0.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|