percheron 0.6.4 → 0.7.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +4 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +31 -0
  5. data/.travis.yml +4 -1
  6. data/Guardfile +3 -3
  7. data/README.md +19 -3
  8. data/Rakefile +24 -1
  9. data/bin/percheron +2 -20
  10. data/lib/percheron/actions/base.rb +0 -39
  11. data/lib/percheron/actions/build.rb +9 -7
  12. data/lib/percheron/actions/create.rb +64 -38
  13. data/lib/percheron/actions/exec.rb +40 -8
  14. data/lib/percheron/actions/exec_local.rb +4 -4
  15. data/lib/percheron/actions/logs.rb +44 -0
  16. data/lib/percheron/actions/purge.rb +15 -4
  17. data/lib/percheron/actions/recreate.rb +15 -65
  18. data/lib/percheron/actions/restart.rb +2 -1
  19. data/lib/percheron/actions/shell.rb +27 -0
  20. data/lib/percheron/actions/start.rb +10 -7
  21. data/lib/percheron/actions.rb +2 -1
  22. data/lib/percheron/commands/abstract.rb +20 -8
  23. data/lib/percheron/commands/build.rb +13 -0
  24. data/lib/percheron/commands/console.rb +53 -0
  25. data/lib/percheron/commands/create.rb +3 -3
  26. data/lib/percheron/commands/list.rb +7 -3
  27. data/lib/percheron/commands/logs.rb +16 -0
  28. data/lib/percheron/commands/main.rb +5 -2
  29. data/lib/percheron/commands/purge.rb +2 -2
  30. data/lib/percheron/commands/recreate.rb +3 -11
  31. data/lib/percheron/commands/restart.rb +2 -2
  32. data/lib/percheron/commands/shell.rb +16 -0
  33. data/lib/percheron/commands/start.rb +2 -2
  34. data/lib/percheron/commands/stop.rb +2 -2
  35. data/lib/percheron/commands.rb +3 -0
  36. data/lib/percheron/config.rb +64 -3
  37. data/lib/percheron/config_delegator.rb +0 -1
  38. data/lib/percheron/container.rb +95 -38
  39. data/lib/percheron/core_extensions.rb +1 -4
  40. data/lib/percheron/docker_connection.rb +5 -1
  41. data/lib/percheron/formatters/stack/table.rb +24 -12
  42. data/lib/percheron/logger.rb +21 -0
  43. data/lib/percheron/metastore.rb +1 -0
  44. data/lib/percheron/null_stack.rb +5 -0
  45. data/lib/percheron/oh_dear.rb +15 -7
  46. data/lib/percheron/stack.rb +77 -72
  47. data/lib/percheron/validators/config.rb +1 -1
  48. data/lib/percheron/validators/container.rb +31 -11
  49. data/lib/percheron/validators/stack.rb +3 -5
  50. data/lib/percheron/version.rb +1 -1
  51. data/lib/percheron.rb +2 -0
  52. data/percheron.gemspec +4 -1
  53. metadata +54 -4
  54. data/assets/percheron.png +0 -0
  55. data/lib/percheron/actions/rename.rb +0 -65
@@ -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
- def_config_item_with_default :container_config, false, :auto_recreate
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
- alias_method :auto_recreate?, :auto_recreate
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
- valid?
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] : 'N/A'
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' % [ name, version.to_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(exists? ? built_image_version : '0.0.0')
92
+ Semantic::Version.new(built_image_version)
42
93
  end
43
94
 
44
95
  def dockerfile
45
- container_config.dockerfile ? Pathname.new(File.expand_path(container_config.dockerfile, config.file_base_path)): nil
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.inject({}) do |all, p|
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
- dependant_container_names.map do |container_name|
57
- '%s:%s' % [ container_name, container_name ]
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(name)
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 dependant_containers
68
- dependant_container_names.inject({}) do |all, container_name|
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 metastore_key
75
- @metastore_key ||= 'stacks.%s.containers.%s' % [ stack.name, name ]
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 current_dockerfile_md5
79
- Digest::MD5.file(dockerfile).hexdigest
126
+ def dockerfile_md5s_match?
127
+ dockerfile_md5 == current_dockerfile_md5
80
128
  end
81
129
 
82
- def dockerfile_md5
83
- $metastore.get("#{metastore_key}.dockerfile_md5")
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
- !!image
143
+ image.nil? ? false : true
96
144
  end
97
145
 
98
- def dependant_containers?
99
- !dependant_container_names.empty?
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 :config, :stack, :container_name
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.Image.split(':')[1]
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 container_config
119
- @container_config ||= stack.container_configs[container_name] || Hashie::Mash.new({})
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
- inject({}) do |all, e|
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
- { connect_timeout: config.docker.timeout }
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 name',
24
+ 'Container',
25
25
  'ID',
26
- 'Version',
27
- 'Running?'
26
+ 'Running?',
27
+ 'Ports',
28
+ 'Volumes',
29
+ 'Version'
28
30
  ]
29
31
  end
30
32
 
31
33
  def rows
32
- stack.filter_containers.map do |container_name, container|
34
+ stack.containers.map do |_, container|
33
35
  [
34
- container_name,
36
+ container.name,
35
37
  container.id,
36
- container.built_version,
37
- container.running?
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'))
@@ -0,0 +1,5 @@
1
+ module Percheron
2
+ NullStack = Naught.build do |config|
3
+ config.mimic Stack
4
+ end
5
+ end
@@ -6,7 +6,16 @@ module Percheron
6
6
  end
7
7
 
8
8
  def generate
9
- <<-EOS
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 - https://github.com/ashmckenzie/percheron/issues/new?labels=bug
37
+ Please copy the detail between the --snip--'s above and raise a ticket please :)
29
38
 
30
- EOS
31
- end
32
-
33
- private
39
+ https://github.com/ashmckenzie/percheron/issues/new?labels=bug
34
40
 
35
- attr_reader :exception
41
+ EOS
42
+ end
43
+ # rubocop:enable Metrics/MethodLength
36
44
 
37
45
  def exception_message
38
46
  exception.inspect
@@ -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, stack_name=nil)
16
- if stack_name
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 ? { stack.name => 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.to_hash_by_key(:name)
25
+ stack_config.containers
31
26
  end
32
27
 
33
- # FIXME: YUCK
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.inject({}) do |all, container_name|
37
- all[container_name] = Container.new(config, self, 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
- container_names = dependant_containers_for(container_names).reverse
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) { |container| Actions::Start.new(container, dependant_containers: container.dependant_containers.values).execute! }
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
- container_names = dependant_containers_for(container_names)
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 create!(container_names: [])
61
+ def build!(container_names: [])
58
62
  container_names = dependant_containers_for(container_names)
59
- exec_on_dependant_containers_for(container_names) { |container| Actions::Create.new(container).execute! }
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 recreate!(container_names: [], force_recreate: false, delete: false)
63
- current = container_names_final = filter_container_names(container_names)
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
- exec_on_dependant_containers_for(container_names_final.uniq) { |container| Actions::Recreate.new(container, force_recreate: force_recreate, delete: delete).execute! }
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
- container_names = filter_container_names(container_names)
77
- exec_on_dependant_containers_for(container_names) { |container| Actions::Purge.new(container).execute! }
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
- def filter_container_names(container_names=[])
93
- stack_config.containers.map do |container_config|
94
- if container_names.empty? || container_names.include?(container_config.name)
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.name)
121
+ container_names.delete(container.full_name)
114
122
  end
115
123
  end
116
124
 
117
- def containers_affected(container_names)
118
- deps = []
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
- wip_list = []
139
- containers_and_their_dependants(container_names).each do |container_name, dependant_container_names|
140
- wip_list += dependant_container_names unless dependant_container_names.empty?
141
- wip_list << container_name
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
- wip_list.uniq
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
- raise Errors::ConfigFileInvalid.new(message)
13
+ fail Errors::ConfigFileInvalid, message
14
14
  else
15
15
  true
16
16
  end
@@ -10,7 +10,7 @@ module Percheron
10
10
  message = rules.return { |rule| send(rule) }
11
11
 
12
12
  if message
13
- raise Errors::ContainerInvalid.new(formatted_message(message))
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: %s" % [ message ]
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
- :validate_version,
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
- 'Name is invalid' if container.name.nil? || !container.name.to_s.match(/[\w\d]{3,}/)
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 validate_version
44
- container.version
45
- nil
46
- rescue ArgumentError
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
- 'Dockerfile is invalid' if container.dockerfile.nil? || !File.exist?(container.dockerfile)
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