percheron 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.cane +4 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +31 -0
- data/.travis.yml +4 -1
- data/Guardfile +3 -3
- data/README.md +19 -3
- data/Rakefile +24 -1
- data/bin/percheron +2 -20
- data/lib/percheron/actions/base.rb +0 -39
- data/lib/percheron/actions/build.rb +9 -7
- data/lib/percheron/actions/create.rb +64 -38
- data/lib/percheron/actions/exec.rb +40 -8
- data/lib/percheron/actions/exec_local.rb +4 -4
- data/lib/percheron/actions/logs.rb +44 -0
- data/lib/percheron/actions/purge.rb +15 -4
- data/lib/percheron/actions/recreate.rb +15 -65
- data/lib/percheron/actions/restart.rb +2 -1
- data/lib/percheron/actions/shell.rb +27 -0
- data/lib/percheron/actions/start.rb +10 -7
- data/lib/percheron/actions.rb +2 -1
- data/lib/percheron/commands/abstract.rb +20 -8
- data/lib/percheron/commands/build.rb +13 -0
- data/lib/percheron/commands/console.rb +53 -0
- data/lib/percheron/commands/create.rb +3 -3
- data/lib/percheron/commands/list.rb +7 -3
- data/lib/percheron/commands/logs.rb +16 -0
- data/lib/percheron/commands/main.rb +5 -2
- data/lib/percheron/commands/purge.rb +2 -2
- data/lib/percheron/commands/recreate.rb +3 -11
- data/lib/percheron/commands/restart.rb +2 -2
- data/lib/percheron/commands/shell.rb +16 -0
- data/lib/percheron/commands/start.rb +2 -2
- data/lib/percheron/commands/stop.rb +2 -2
- data/lib/percheron/commands.rb +3 -0
- data/lib/percheron/config.rb +64 -3
- data/lib/percheron/config_delegator.rb +0 -1
- data/lib/percheron/container.rb +95 -38
- data/lib/percheron/core_extensions.rb +1 -4
- data/lib/percheron/docker_connection.rb +5 -1
- data/lib/percheron/formatters/stack/table.rb +24 -12
- data/lib/percheron/logger.rb +21 -0
- data/lib/percheron/metastore.rb +1 -0
- data/lib/percheron/null_stack.rb +5 -0
- data/lib/percheron/oh_dear.rb +15 -7
- data/lib/percheron/stack.rb +77 -72
- data/lib/percheron/validators/config.rb +1 -1
- data/lib/percheron/validators/container.rb +31 -11
- data/lib/percheron/validators/stack.rb +3 -5
- data/lib/percheron/version.rb +1 -1
- data/lib/percheron.rb +2 -0
- data/percheron.gemspec +4 -1
- metadata +54 -4
- data/assets/percheron.png +0 -0
- data/lib/percheron/actions/rename.rb +0 -65
data/lib/percheron/container.rb
CHANGED
@@ -4,27 +4,78 @@ module Percheron
|
|
4
4
|
extend Forwardable
|
5
5
|
extend ConfigDelegator
|
6
6
|
|
7
|
-
def_delegators :container_config, :name
|
7
|
+
def_delegators :container_config, :name, :pseudo_name, :docker_image
|
8
|
+
def_config_item_with_default :container_config, [], :env, :ports, :volumes,
|
9
|
+
:dependant_container_names, :pre_build_scripts,
|
10
|
+
:post_start_scripts, :start_args
|
11
|
+
def_config_item_with_default :container_config, %w(127.0.0.1 8.8.8.8), :dns
|
12
|
+
def_config_item_with_default :container_config, true, :startable
|
8
13
|
|
9
|
-
|
10
|
-
def_config_item_with_default :container_config, [], :env, :ports, :volumes, :dependant_container_names, :pre_build_scripts, :post_create_scripts, :post_start_scripts
|
14
|
+
attr_reader :config_file_base_path
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
def initialize(config, stack, container_name)
|
15
|
-
@config = config
|
16
|
+
def initialize(stack, container_name, config_file_base_path)
|
16
17
|
@stack = stack
|
17
18
|
@container_name = container_name
|
18
|
-
|
19
|
+
@config_file_base_path = config_file_base_path
|
19
20
|
self
|
20
21
|
end
|
21
22
|
|
23
|
+
def dependant_containers
|
24
|
+
dependant_container_names.each_with_object({}) do |container_name, all|
|
25
|
+
all[container_name] = stack.containers[container_name]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def startable_dependant_containers
|
30
|
+
dependant_containers.select { |_, container| container.startable? }
|
31
|
+
end
|
32
|
+
|
33
|
+
def metastore_key
|
34
|
+
@metastore_key ||= 'stacks.%s.containers.%s' % [ stack.name, name ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def container_config
|
38
|
+
@container_config ||= stack.container_configs[container_name] || Hashie::Mash.new({})
|
39
|
+
end
|
40
|
+
|
22
41
|
def id
|
23
|
-
exists? ? info.id[0...12] :
|
42
|
+
exists? ? info.id[0...12] : nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def hostname
|
46
|
+
container_config.fetch('hostname', name)
|
24
47
|
end
|
25
48
|
|
26
49
|
def image_name
|
27
|
-
'%s:%s' % [
|
50
|
+
'%s:%s' % [ image_repo, image_version.to_s ] if image_repo && image_version
|
51
|
+
end
|
52
|
+
|
53
|
+
def image_repo # FIXME
|
54
|
+
if pseudo?
|
55
|
+
pseudo_full_name
|
56
|
+
elsif !buildable?
|
57
|
+
container_config.docker_image.split(':')[0]
|
58
|
+
else
|
59
|
+
full_name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def image_version
|
64
|
+
if buildable?
|
65
|
+
version
|
66
|
+
elsif !container_config.docker_image.nil?
|
67
|
+
container_config.docker_image.split(':')[1]
|
68
|
+
else
|
69
|
+
fail Errors::ContainerInvalid, 'Cannot determine image version'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def full_name
|
74
|
+
'%s_%s' % [ stack.name, name ]
|
75
|
+
end
|
76
|
+
|
77
|
+
def pseudo_full_name
|
78
|
+
'%s_%s' % [ stack.name, pseudo_name ]
|
28
79
|
end
|
29
80
|
|
30
81
|
def image
|
@@ -38,49 +89,46 @@ module Percheron
|
|
38
89
|
end
|
39
90
|
|
40
91
|
def built_version
|
41
|
-
Semantic::Version.new(
|
92
|
+
Semantic::Version.new(built_image_version)
|
42
93
|
end
|
43
94
|
|
44
95
|
def dockerfile
|
45
|
-
|
96
|
+
return nil unless container_config.dockerfile
|
97
|
+
Pathname.new(File.expand_path(container_config.dockerfile, config_file_base_path))
|
46
98
|
end
|
47
99
|
|
48
100
|
def exposed_ports
|
49
|
-
ports.
|
50
|
-
all[p.split(':')[1]] = {}
|
51
|
-
all
|
52
|
-
end
|
101
|
+
ports.each_with_object({}) { |p, all| all[p.split(':')[1]] = {} }
|
53
102
|
end
|
54
103
|
|
55
104
|
def links
|
56
|
-
|
57
|
-
'%s:%s' % [
|
105
|
+
startable_dependant_containers.map do |_, container|
|
106
|
+
'%s:%s' % [ container.full_name, container.name ]
|
58
107
|
end
|
59
108
|
end
|
60
109
|
|
61
110
|
def docker_container
|
62
|
-
Docker::Container.get(
|
111
|
+
Docker::Container.get(full_name)
|
63
112
|
rescue Docker::Error::NotFoundError, Excon::Errors::SocketError
|
64
113
|
NullContainer.new
|
65
114
|
end
|
66
115
|
|
67
|
-
def
|
68
|
-
|
69
|
-
all[container_name] = stack.filter_containers[container_name]
|
70
|
-
all
|
71
|
-
end
|
116
|
+
def labels
|
117
|
+
{ version: version.to_s, created_by: "Percheron #{Percheron::VERSION}" }
|
72
118
|
end
|
73
119
|
|
74
|
-
def
|
75
|
-
|
120
|
+
def update_dockerfile_md5!
|
121
|
+
md5 = current_dockerfile_md5
|
122
|
+
$logger.info "Setting MD5 for '#{name}' container to #{md5}"
|
123
|
+
$metastore.set("#{metastore_key}.dockerfile_md5", md5)
|
76
124
|
end
|
77
125
|
|
78
|
-
def
|
79
|
-
|
126
|
+
def dockerfile_md5s_match?
|
127
|
+
dockerfile_md5 == current_dockerfile_md5
|
80
128
|
end
|
81
129
|
|
82
|
-
def
|
83
|
-
|
130
|
+
def versions_match?
|
131
|
+
version == built_version
|
84
132
|
end
|
85
133
|
|
86
134
|
def running?
|
@@ -92,32 +140,41 @@ module Percheron
|
|
92
140
|
end
|
93
141
|
|
94
142
|
def image_exists?
|
95
|
-
|
143
|
+
image.nil? ? false : true
|
96
144
|
end
|
97
145
|
|
98
|
-
def
|
99
|
-
!
|
146
|
+
def buildable?
|
147
|
+
!dockerfile.nil? && container_config.docker_image.nil?
|
100
148
|
end
|
101
149
|
|
102
150
|
def valid?
|
103
151
|
Validators::Container.new(self).valid?
|
104
152
|
end
|
105
153
|
|
154
|
+
alias_method :startable?, :startable
|
155
|
+
|
106
156
|
private
|
107
157
|
|
108
|
-
attr_reader :
|
158
|
+
attr_reader :stack, :container_name
|
159
|
+
|
160
|
+
def current_dockerfile_md5
|
161
|
+
dockerfile ? Digest::MD5.file(dockerfile).hexdigest : Digest::MD5.hexdigest(image_name)
|
162
|
+
end
|
163
|
+
|
164
|
+
def dockerfile_md5
|
165
|
+
$metastore.get("#{metastore_key}.dockerfile_md5") || current_dockerfile_md5
|
166
|
+
end
|
109
167
|
|
110
168
|
def built_image_version
|
111
|
-
info.Config.
|
169
|
+
(exists? && info.Config.Labels) ? info.Config.Labels.version : '0.0.0'
|
112
170
|
end
|
113
171
|
|
114
172
|
def info
|
115
173
|
Hashie::Mash.new(docker_container.info)
|
116
174
|
end
|
117
175
|
|
118
|
-
def
|
119
|
-
|
176
|
+
def pseudo?
|
177
|
+
!pseudo_name.nil?
|
120
178
|
end
|
121
|
-
|
122
179
|
end
|
123
180
|
end
|
@@ -2,7 +2,6 @@ module Percheron
|
|
2
2
|
module CoreExtensions
|
3
3
|
module Array
|
4
4
|
module Extras
|
5
|
-
|
6
5
|
def return
|
7
6
|
result = nil
|
8
7
|
each do |x|
|
@@ -16,12 +15,10 @@ module Percheron
|
|
16
15
|
end
|
17
16
|
|
18
17
|
def to_hash_by_key(key_attr)
|
19
|
-
|
18
|
+
each_with_object({}) do |e, all|
|
20
19
|
all[e.send(key_attr)] = e unless all[e.send(key_attr)]
|
21
|
-
all
|
22
20
|
end
|
23
21
|
end
|
24
|
-
|
25
22
|
end
|
26
23
|
end
|
27
24
|
end
|
@@ -23,6 +23,7 @@ module Percheron
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def set_options!
|
26
|
+
Excon.defaults[:ssl_verify_peer] = config.docker.fetch('ssl_verify_peer', true)
|
26
27
|
Docker.options = docker_options
|
27
28
|
end
|
28
29
|
|
@@ -31,7 +32,10 @@ module Percheron
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def base_docker_options
|
34
|
-
{
|
35
|
+
{
|
36
|
+
connect_timeout: config.docker.connect_timeout || 5,
|
37
|
+
read_timeout: config.docker.read_timeout || 300
|
38
|
+
}
|
35
39
|
end
|
36
40
|
|
37
41
|
def extra_docker_opts
|
@@ -8,36 +8,48 @@ module Percheron
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def generate
|
11
|
-
Terminal::Table.new(
|
12
|
-
title: stack.name,
|
13
|
-
headings: headings,
|
14
|
-
rows: rows
|
15
|
-
)
|
11
|
+
Terminal::Table.new(title: title, headings: headings, rows: rows)
|
16
12
|
end
|
17
13
|
|
18
14
|
private
|
19
15
|
|
20
16
|
attr_reader :stack
|
21
17
|
|
18
|
+
def title
|
19
|
+
stack.name
|
20
|
+
end
|
21
|
+
|
22
22
|
def headings
|
23
23
|
[
|
24
|
-
'Container
|
24
|
+
'Container',
|
25
25
|
'ID',
|
26
|
-
'
|
27
|
-
'
|
26
|
+
'Running?',
|
27
|
+
'Ports',
|
28
|
+
'Volumes',
|
29
|
+
'Version'
|
28
30
|
]
|
29
31
|
end
|
30
32
|
|
31
33
|
def rows
|
32
|
-
stack.
|
34
|
+
stack.containers.map do |_, container|
|
33
35
|
[
|
34
|
-
|
36
|
+
container.name,
|
35
37
|
container.id,
|
36
|
-
container
|
37
|
-
container.
|
38
|
+
startable(container),
|
39
|
+
container.ports.join(', '),
|
40
|
+
container.volumes.join(', '),
|
41
|
+
(container.built_version == '0.0.0') ? '' : container.built_version
|
38
42
|
]
|
39
43
|
end
|
40
44
|
end
|
45
|
+
|
46
|
+
def startable(container)
|
47
|
+
if container.startable?
|
48
|
+
container.running? ? 'yes' : '-'
|
49
|
+
else
|
50
|
+
'n/a'
|
51
|
+
end
|
52
|
+
end
|
41
53
|
end
|
42
54
|
end
|
43
55
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
$logger = Logger.new(STDOUT)
|
4
|
+
|
5
|
+
logger_level = Logger::INFO
|
6
|
+
logger_level = Logger::WARN if ENV['QUIET'] == 'true'
|
7
|
+
|
8
|
+
if ENV['DEBUG'] == 'true' || ENV['DOCKER_DEBUG'] == 'true'
|
9
|
+
begin
|
10
|
+
require 'pry-byebug'
|
11
|
+
require 'awesome_print'
|
12
|
+
rescue LoadError
|
13
|
+
$logger.warn 'Debugging gems are not installed'
|
14
|
+
end
|
15
|
+
|
16
|
+
logger_level = Logger::DEBUG
|
17
|
+
|
18
|
+
Docker.logger = $logger if ENV['DOCKER_DEBUG'] == 'true'
|
19
|
+
end
|
20
|
+
|
21
|
+
$logger.level = logger_level
|
@@ -0,0 +1 @@
|
|
1
|
+
$metastore = Metastore::Cabinet.new(File.join(ENV['HOME'], '.percheron', 'metastore.yaml'))
|
data/lib/percheron/oh_dear.rb
CHANGED
@@ -6,7 +6,16 @@ module Percheron
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def generate
|
9
|
-
|
9
|
+
template
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :exception
|
15
|
+
|
16
|
+
# rubocop:disable Metrics/MethodLength
|
17
|
+
def template
|
18
|
+
<<-EOS
|
10
19
|
|
11
20
|
OH DEAR, we are terribly sorry.. something unexpected occurred :(
|
12
21
|
|
@@ -25,14 +34,13 @@ Trace
|
|
25
34
|
|
26
35
|
--snip--
|
27
36
|
|
28
|
-
Please copy the detail between the --snip--'s above and raise a ticket
|
37
|
+
Please copy the detail between the --snip--'s above and raise a ticket please :)
|
29
38
|
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
39
|
+
https://github.com/ashmckenzie/percheron/issues/new?labels=bug
|
34
40
|
|
35
|
-
|
41
|
+
EOS
|
42
|
+
end
|
43
|
+
# rubocop:enable Metrics/MethodLength
|
36
44
|
|
37
45
|
def exception_message
|
38
46
|
exception.inspect
|
data/lib/percheron/stack.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
|
1
3
|
module Percheron
|
2
4
|
class Stack
|
3
|
-
|
4
5
|
extend Forwardable
|
5
6
|
|
6
7
|
def_delegators :stack_config, :name, :description
|
@@ -8,73 +9,84 @@ module Percheron
|
|
8
9
|
def initialize(config, stack_name)
|
9
10
|
@config = config
|
10
11
|
@stack_name = stack_name
|
11
|
-
valid?
|
12
12
|
self
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.get(config,
|
16
|
-
|
15
|
+
def self.get(config, name = nil)
|
16
|
+
stacks = name.nil? ? config.stacks : { name => config.stacks[name] }
|
17
|
+
stacks.each_with_object({}) do |stack_config, all|
|
18
|
+
stack_name, _ = stack_config
|
17
19
|
stack = new(config, stack_name)
|
18
|
-
stack
|
19
|
-
else
|
20
|
-
all = {}
|
21
|
-
config.stacks.each do |stack_name, _|
|
22
|
-
stack = new(config, stack_name)
|
23
|
-
all[stack.name] = stack
|
24
|
-
end
|
25
|
-
all
|
20
|
+
all[stack.name] = stack
|
26
21
|
end
|
27
22
|
end
|
28
23
|
|
29
24
|
def container_configs
|
30
|
-
stack_config.containers
|
25
|
+
stack_config.containers
|
31
26
|
end
|
32
27
|
|
33
|
-
|
34
|
-
def filter_containers(container_names=[])
|
28
|
+
def containers(container_names = [])
|
35
29
|
container_names = !container_names.empty? ? container_names : filter_container_names
|
36
|
-
container_names.
|
37
|
-
all[container_name] =
|
38
|
-
all
|
30
|
+
container_names.each_with_object({}) do |container_name, all|
|
31
|
+
all[container_name] = container_from_name(container_name)
|
39
32
|
end
|
40
33
|
end
|
41
34
|
|
35
|
+
def shell!(container_name, shell: Percheron::Actions::Shell::DEFAULT_SHELL)
|
36
|
+
Actions::Shell.new(container_from_name(container_name), shell: shell).execute!
|
37
|
+
end
|
38
|
+
|
39
|
+
def logs!(container_name, follow: false)
|
40
|
+
Actions::Logs.new(container_from_name(container_name), follow: follow).execute!
|
41
|
+
end
|
42
|
+
|
42
43
|
def stop!(container_names: [])
|
43
|
-
|
44
|
-
exec_on_dependant_containers_for(container_names) { |container| Actions::Stop.new(container).execute! }
|
44
|
+
execute!(Actions::Stop, filter_container_names(container_names).reverse)
|
45
45
|
end
|
46
46
|
|
47
|
+
# FIXME: bug when non-startable container specified, all containers started
|
47
48
|
def start!(container_names: [])
|
48
49
|
container_names = dependant_containers_for(container_names)
|
49
|
-
exec_on_dependant_containers_for(container_names)
|
50
|
+
exec_on_dependant_containers_for(container_names) do |container|
|
51
|
+
dependant_containers = container.startable_dependant_containers.values
|
52
|
+
Actions::Start.new(container, dependant_containers: dependant_containers).execute!
|
53
|
+
end
|
54
|
+
nil
|
50
55
|
end
|
51
56
|
|
52
57
|
def restart!(container_names: [])
|
53
|
-
|
54
|
-
exec_on_dependant_containers_for(container_names) { |container| Actions::Restart.new(container).execute! }
|
58
|
+
execute!(Actions::Restart, filter_container_names(container_names))
|
55
59
|
end
|
56
60
|
|
57
|
-
def
|
61
|
+
def build!(container_names: [])
|
58
62
|
container_names = dependant_containers_for(container_names)
|
59
|
-
exec_on_dependant_containers_for(container_names)
|
63
|
+
exec_on_dependant_containers_for(container_names) do |container|
|
64
|
+
Actions::Build.new(container).execute!
|
65
|
+
end
|
66
|
+
nil
|
60
67
|
end
|
61
68
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
# FIXME: make this suck less
|
66
|
-
while true
|
67
|
-
current = deps = containers_affected(current).uniq
|
68
|
-
break if deps.empty?
|
69
|
-
container_names_final += deps
|
70
|
-
end
|
69
|
+
def create!(container_names: [], start: false)
|
70
|
+
execute!(Actions::Create, dependant_containers_for(container_names), start: start)
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
+
def recreate!(container_names: [], start: false)
|
74
|
+
execute!(Actions::Recreate, filter_container_names(container_names), start: start)
|
73
75
|
end
|
74
76
|
|
75
77
|
def purge!(container_names: [])
|
76
|
-
|
77
|
-
|
78
|
+
execute!(Actions::Purge, filter_container_names(container_names).reverse)
|
79
|
+
end
|
80
|
+
|
81
|
+
def execute!(klass, container_names, args=nil)
|
82
|
+
exec_on_dependant_containers_for(container_names) do |container|
|
83
|
+
if args
|
84
|
+
klass.new(container, args).execute!
|
85
|
+
else
|
86
|
+
klass.new(container).execute!
|
87
|
+
end
|
88
|
+
end
|
89
|
+
nil
|
78
90
|
end
|
79
91
|
|
80
92
|
def valid?
|
@@ -89,59 +101,52 @@ module Percheron
|
|
89
101
|
@stack_config ||= (config.stacks[stack_name] || Hashie::Mash.new({}))
|
90
102
|
end
|
91
103
|
|
92
|
-
|
93
|
-
|
94
|
-
|
104
|
+
# FIXME: yuck
|
105
|
+
# rubocop:disable Style/Next
|
106
|
+
def filter_container_names(container_names = [])
|
107
|
+
stack_config.fetch('containers', {}).map do |container_name, container_config|
|
108
|
+
if container_names.empty? || container_names.include?(container_name) ||
|
109
|
+
(container_config.pseudo_name &&
|
110
|
+
container_names.include?(container_config.pseudo_name))
|
95
111
|
container_config.name
|
96
112
|
end
|
97
113
|
end.compact
|
98
114
|
end
|
99
|
-
|
100
|
-
def exec_on_containers(container_names)
|
101
|
-
filter_containers(container_names).each { |_, container| yield(container) }
|
102
|
-
end
|
115
|
+
# rubocop:enable Style/Next
|
103
116
|
|
104
117
|
def exec_on_dependant_containers_for(container_names)
|
105
|
-
serial_processor(container_names) do |container|
|
106
|
-
$logger.info '' if yield(container)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def serial_processor(container_names)
|
111
118
|
exec_on_containers(container_names) do |container|
|
119
|
+
$logger.debug "Processing '#{container.name}' container"
|
112
120
|
yield(container)
|
113
|
-
container_names.delete(container.
|
121
|
+
container_names.delete(container.full_name)
|
114
122
|
end
|
115
123
|
end
|
116
124
|
|
117
|
-
def
|
118
|
-
|
119
|
-
container_names.each do |container_name|
|
120
|
-
filter_containers.each do |_, container|
|
121
|
-
deps << container.name if container.dependant_container_names.include?(container_name)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
deps
|
125
|
-
end
|
126
|
-
|
127
|
-
def containers_and_their_dependants(container_names)
|
128
|
-
all_containers = filter_containers
|
129
|
-
container_names.inject({}) do |all, container_name|
|
130
|
-
all[container_name] = all_containers[container_name].dependant_container_names
|
131
|
-
all
|
132
|
-
end
|
125
|
+
def exec_on_containers(container_names)
|
126
|
+
containers(container_names).each { |_, container| yield(container) }
|
133
127
|
end
|
134
128
|
|
135
129
|
def dependant_containers_for(container_names)
|
130
|
+
list = []
|
136
131
|
container_names = filter_container_names(container_names)
|
132
|
+
containers = all_containers_and_dependants(container_names)
|
133
|
+
containers.each do |container_name, dependant_container_names|
|
134
|
+
list += dependant_container_names unless dependant_container_names.empty?
|
135
|
+
list << container_name
|
136
|
+
end
|
137
|
+
list.uniq
|
138
|
+
end
|
137
139
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
140
|
+
def all_containers_and_dependants(container_names)
|
141
|
+
all_containers = containers
|
142
|
+
containers = container_names.each_with_object({}) do |container_name, all|
|
143
|
+
all[container_name] = all_containers[container_name].dependant_container_names
|
142
144
|
end
|
143
|
-
|
145
|
+
containers.sort { |x, y| x[1].length <=> y[1].length } # FIXME
|
144
146
|
end
|
145
147
|
|
148
|
+
def container_from_name(container_name)
|
149
|
+
Container.new(self, container_name, config.file_base_path)
|
150
|
+
end
|
146
151
|
end
|
147
152
|
end
|
@@ -10,7 +10,7 @@ module Percheron
|
|
10
10
|
message = rules.return { |rule| send(rule) }
|
11
11
|
|
12
12
|
if message
|
13
|
-
|
13
|
+
fail Errors::ContainerInvalid, formatted_message(message)
|
14
14
|
else
|
15
15
|
true
|
16
16
|
end
|
@@ -24,32 +24,52 @@ module Percheron
|
|
24
24
|
if container.name
|
25
25
|
"Container config for '%s' is invalid: %s" % [ container.name, message ]
|
26
26
|
else
|
27
|
-
"Container config is invalid:
|
27
|
+
"Container config is invalid: #{message}"
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
def rules
|
32
32
|
[
|
33
33
|
:validate_name,
|
34
|
-
:
|
35
|
-
:validate_dockerfile
|
34
|
+
:validate_dockerfile_and_image_name,
|
35
|
+
:validate_dockerfile,
|
36
|
+
:validate_image,
|
37
|
+
:validate_version
|
36
38
|
]
|
37
39
|
end
|
38
40
|
|
41
|
+
# rubocop:disable Style/GuardClause
|
39
42
|
def validate_name
|
40
|
-
|
43
|
+
if container.name.nil? || !container.name.to_s.match(/[\w]{3,}/)
|
44
|
+
'Container name is invalid'
|
45
|
+
end
|
41
46
|
end
|
42
47
|
|
43
|
-
def
|
44
|
-
container.
|
45
|
-
|
46
|
-
|
47
|
-
'Version is invalid'
|
48
|
+
def validate_dockerfile_and_image_name
|
49
|
+
if container.dockerfile.nil? && container.docker_image.nil?
|
50
|
+
'Container Dockerfile OR image name not provided'
|
51
|
+
end
|
48
52
|
end
|
49
53
|
|
50
54
|
def validate_dockerfile
|
51
|
-
|
55
|
+
if !container.dockerfile.nil? && !File.exist?(container.dockerfile)
|
56
|
+
'Container Dockerfile is invalid'
|
57
|
+
end
|
52
58
|
end
|
59
|
+
|
60
|
+
def validate_image
|
61
|
+
if !container.docker_image.nil? && !container.docker_image.match(/^.+:.+$/)
|
62
|
+
'Container Docker image is invalid'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# rubocop:enable Style/GuardClause
|
66
|
+
|
67
|
+
def validate_version
|
68
|
+
container.version ? nil : fail(ArgumentError)
|
69
|
+
rescue ArgumentError
|
70
|
+
'Container version is invalid'
|
71
|
+
end
|
72
|
+
|
53
73
|
end
|
54
74
|
end
|
55
75
|
end
|