percheron 0.6.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 61e4d66006bf086c797d921852dae42438f8abca
4
- data.tar.gz: b91b6698c444e51757bee46fa5f290b67211670b
3
+ metadata.gz: cbab3f52787ff0b165f9482186771b07ff673df1
4
+ data.tar.gz: ce6eb63e3621d595f814c8806728bfe9b43fae7b
5
5
  SHA512:
6
- metadata.gz: 9a136f407d6823d6983723a36ea9056f56e440411e56df65e2101348fbb4dcd957027f570a9af29b21f9e5c273698ca9a460a05666fa79353577413c00e8892e
7
- data.tar.gz: b87043f174f595bd1aed913e087089575d7ee602d5a46a5810d01c98aebd38d4816aa1a45145f2b7b685fb443bb1fd4d10c29a3e7ed0dc8396048a0299bbb97c
6
+ metadata.gz: 4b198258dc8f5226a08563b77d1826af3f56eea705c35632d85bbca4d9e49be216c17fc5b776404fe7caac50ac2e84aa1d5c507493faf4edbaf110b276a1c3bc
7
+ data.tar.gz: f790fdeb14e59ce702f5569ea4043fd4747acfd6bc91934ed86646be290bd7e7d98782e049c0da198e9446f9b83d20e7724e7d17e8229502825172bc8d940077
data/.cane ADDED
@@ -0,0 +1,4 @@
1
+ --abc-glob ./lib/**/*.rb
2
+ --style-glob ./lib/**/*.rb
3
+ --style-measure 100
4
+ --no-doc
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  .ruby-version
11
11
  percheron.sublime-*
12
+ .envrc
data/.rubocop.yml ADDED
@@ -0,0 +1,31 @@
1
+ Style/SpaceInsideBrackets:
2
+ Enabled: false
3
+
4
+ Style/EmptyLinesAroundClassBody:
5
+ Enabled: false
6
+
7
+ Style/Documentation:
8
+ Enabled: false
9
+
10
+ Style/IndentationConsistency:
11
+ Enabled: false
12
+
13
+ Style/FormatString:
14
+ Enabled: false
15
+
16
+ Metrics/LineLength:
17
+ Enabled: false
18
+
19
+ Style/GlobalVars:
20
+ Enabled: false
21
+
22
+ Style/RegexpLiteral:
23
+ EnforcedStyle: mixed
24
+ AllowInnerSlashes: false
25
+
26
+ Metrics/ClassLength:
27
+ CountComments: false
28
+ Max: 150
29
+
30
+ Style/SpaceAroundEqualsInParameterDefault:
31
+ Enabled: false
data/.travis.yml CHANGED
@@ -5,4 +5,7 @@ branches:
5
5
  only:
6
6
  - master
7
7
  - wip
8
- script: bundle exec rake spec
8
+ script:
9
+ - bundle exec cane
10
+ - bundle exec rubocop
11
+ - bundle exec rake spec:unit
data/Guardfile CHANGED
@@ -1,5 +1,5 @@
1
1
  guard :rspec, cmd: 'bundle exec rspec' do
2
- watch(%r{^spec/.+_spec\.rb$})
3
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
- watch('spec/spec_helper.rb') { "spec" }
2
+ watch(%r{^spec\/.+_spec\.rb$})
3
+ watch(%r{^lib\/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { 'spec' }
5
5
  end
data/README.md CHANGED
@@ -6,8 +6,6 @@
6
6
  [![Test Coverage](https://codeclimate.com/github/ashmckenzie/percheron/badges/coverage.svg)](https://codeclimate.com/github/ashmckenzie/percheron)
7
7
  [![Dependency Status](https://gemnasium.com/ashmckenzie/percheron.svg)](https://gemnasium.com/ashmckenzie/percheron)
8
8
 
9
- ![Percheron](https://raw.githubusercontent.com/ashmckenzie/percheron/master/assets/percheron.png)
10
-
11
9
  Organise your Docker containers with muscle and intelligence.
12
10
 
13
11
  ## Installation
@@ -30,9 +28,27 @@ Or install it yourself as:
30
28
  $ gem install percheron
31
29
  ```
32
30
 
31
+ ## Requirements
32
+
33
+ * Ruby 2.x
34
+ * Docker 1.6.x
35
+
33
36
  ## Usage
34
37
 
35
- WIP, not ready yet :)
38
+ TODO
39
+
40
+ ## Examples
41
+
42
+ * Rails - https://github.com/ashmckenzie/percheron-rails#quickstart
43
+ * Torrent - https://github.com/ashmckenzie/percheron-torrent#quickstart
44
+
45
+ ## Testing
46
+
47
+ All (cane, RuboCop, unit and integration):
48
+
49
+ ```shell
50
+ bundle exec test
51
+ ```
36
52
 
37
53
  ## Contributing
38
54
 
data/Rakefile CHANGED
@@ -1,4 +1,27 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'cane/rake_task'
4
+ require 'rubocop/rake_task'
3
5
 
4
- RSpec::Core::RakeTask.new('spec')
6
+ desc 'Run cane, rubocop, unit and integration tests'
7
+ task test: %w(test:cane test:rubocop spec:unit spec:integration)
8
+
9
+ namespace :test do
10
+ desc 'Run cane'
11
+ Cane::RakeTask.new(:cane)
12
+
13
+ desc 'Run RuboCop'
14
+ RuboCop::RakeTask.new
15
+ end
16
+
17
+ RSpec::Core::RakeTask.new('spec') do |config|
18
+ config.pattern = './spec/**{,/*/**}/*_spec.rb'
19
+ end
20
+
21
+ RSpec::Core::RakeTask.new('spec:unit') do |config|
22
+ config.pattern = './spec/unit/**{,/*/**}/*_spec.rb'
23
+ end
24
+
25
+ RSpec::Core::RakeTask.new('spec:integration') do |config|
26
+ config.pattern = './spec/integration/**{,/*/**}/*_spec.rb'
27
+ end
data/bin/percheron CHANGED
@@ -3,28 +3,10 @@
3
3
  lib = File.expand_path('../../lib', __FILE__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
- require 'logger'
7
6
  require 'percheron'
8
7
  require 'percheron/commands'
9
-
10
- $metastore = Metastore::Cabinet.new(File.join(ENV['HOME'], '.percheron', 'metastore.yaml'))
11
- $logger = Logger.new(STDOUT)
12
-
13
- logger_level = Logger::INFO
14
-
15
- if ENV['QUIET'] == 'true'
16
- logger_level = Logger::WARN
17
- end
18
-
19
- if ENV['DEBUG'] == 'true' || ENV['DOCKER_DEBUG'] == 'true'
20
- require 'pry-byebug'
21
- require 'awesome_print'
22
- logger_level = Logger::DEBUG
23
-
24
- Docker.logger = $logger if ENV['DOCKER_DEBUG'] == 'true'
25
- end
26
-
27
- $logger.level = logger_level
8
+ require 'percheron/logger'
9
+ require 'percheron/metastore'
28
10
 
29
11
  begin
30
12
  Percheron::Commands::Main.run
@@ -1,7 +1,6 @@
1
1
  module Percheron
2
2
  module Actions
3
3
  module Base
4
-
5
4
  def base_dir
6
5
  container.dockerfile.dirname.to_s
7
6
  end
@@ -13,44 +12,6 @@ module Percheron
13
12
  ensure
14
13
  Dir.chdir(old_dir)
15
14
  end
16
-
17
- def now_timestamp
18
- Time.now.strftime('%Y%m%d%H%M%S')
19
- end
20
-
21
- def insert_files!(files)
22
- files.each do |file|
23
- file = Pathname.new(File.expand_path(file, base_dir))
24
- container.image.insert_local('localPath' => file.to_s, 'outputPath' => "/tmp/#{file.basename}").tap do |new_image|
25
- new_image.tag(repo: container.name, tag: container.version.to_s, force: true)
26
- end
27
- end
28
- end
29
-
30
- def stop_containers!(containers)
31
- exec_on_containers!(containers) do |container|
32
- if container.running?
33
- $logger.debug "Stopping '#{container.name}' container"
34
- Stop.new(container).execute!
35
- end
36
- end
37
- end
38
-
39
- def start_containers!(containers, exec_scripts: true)
40
- exec_on_containers!(containers) do |container|
41
- unless container.running?
42
- $logger.debug "Starting '#{container.name}' container"
43
- Start.new(container, dependant_containers: container.dependant_containers.values, exec_scripts: exec_scripts).execute!
44
- end
45
- end
46
- end
47
-
48
- def exec_on_containers!(containers)
49
- containers.inject([]) do |all, container|
50
- all << container if yield(container)
51
- all
52
- end.compact
53
- end
54
15
  end
55
16
  end
56
17
  end
@@ -4,22 +4,24 @@ module Percheron
4
4
 
5
5
  include Base
6
6
 
7
- def initialize(container, nocache: false)
7
+ def initialize(container, nocache: false, exec_scripts: true)
8
8
  @container = container
9
9
  @nocache = nocache
10
+ @exec_scripts = exec_scripts
10
11
  end
11
12
 
12
13
  def execute!
13
14
  results = []
14
- results << build!
15
+ results << build! if container.buildable?
15
16
  results.compact.empty? ? nil : container
16
17
  end
17
18
 
18
19
  private
19
20
 
20
- attr_reader :container, :nocache
21
+ attr_reader :container, :nocache, :exec_scripts
22
+ alias_method :exec_scripts?, :exec_scripts
21
23
 
22
- def build_opts
24
+ def options
23
25
  {
24
26
  'dockerfile' => container.dockerfile.basename.to_s,
25
27
  't' => container.image_name,
@@ -30,18 +32,18 @@ module Percheron
30
32
 
31
33
  def build!
32
34
  in_working_directory(base_dir) do
33
- execute_pre_build_scripts! unless container.pre_build_scripts.empty?
35
+ execute_pre_build_scripts!
34
36
  $logger.info "Building '#{container.image_name}' image"
35
- Docker::Image.build_from_dir(base_dir, build_opts) do |out|
37
+ Docker::Image.build_from_dir(base_dir, options) do |out|
36
38
  $logger.debug '%s' % [ out.strip ]
37
39
  end
38
40
  end
39
41
  end
40
42
 
41
43
  def execute_pre_build_scripts!
44
+ return nil if !exec_scripts? && container.pre_build_scripts.empty?
42
45
  ExecLocal.new(container, container.pre_build_scripts, 'PRE build').execute!
43
46
  end
44
-
45
47
  end
46
48
  end
47
49
  end
@@ -4,37 +4,44 @@ module Percheron
4
4
 
5
5
  include Base
6
6
 
7
- def initialize(container, recreate: false)
7
+ def initialize(container, start: false, cmd: false, exec_scripts: true)
8
8
  @container = container
9
- @recreate = recreate
9
+ @start = start
10
+ @exec_scripts = exec_scripts
11
+ @cmd = cmd
12
+ @container_image_existed = container.image_exists?
10
13
  end
11
14
 
12
- def execute!(opts={})
15
+ def execute!
13
16
  results = []
14
- if recreate? || !container.exists?
15
- results << create!(opts)
16
- else
17
+ if container.exists?
17
18
  $logger.debug "Container '#{container.name}' already exists"
19
+ else
20
+ results << create!
18
21
  end
19
22
  results.compact.empty? ? nil : container
20
23
  end
21
24
 
22
25
  private
23
26
 
24
- attr_reader :container, :recreate
27
+ attr_reader :container, :start, :exec_scripts, :container_image_existed
28
+ alias_method :start?, :start
29
+ alias_method :exec_scripts?, :exec_scripts
30
+ alias_method :container_image_existed?, :container_image_existed
31
+
32
+ def cmd
33
+ @cmd ||= (@cmd || container.start_args)
34
+ end
25
35
 
26
36
  def base_options
27
37
  {
28
- 'name' => container.name,
38
+ 'name' => container.full_name,
29
39
  'Image' => container.image_name,
30
- 'Hostname' => container.name,
40
+ 'Hostname' => container.hostname,
31
41
  'Env' => container.env,
32
42
  'ExposedPorts' => container.exposed_ports,
33
- 'HostConfig' => {
34
- 'PortBindings' => port_bindings,
35
- 'Links' => container.links,
36
- 'Binds' => container.volumes
37
- }
43
+ 'Cmd' => cmd,
44
+ 'Labels' => container.labels
38
45
  }
39
46
  end
40
47
 
@@ -43,55 +50,74 @@ module Percheron
43
50
  'HostConfig' => {
44
51
  'PortBindings' => port_bindings,
45
52
  'Links' => container.links,
46
- 'Binds' => container.volumes
53
+ 'Binds' => container.volumes,
54
+ 'Dns' => container.dns
47
55
  }
48
56
  }
49
57
  end
50
58
 
59
+ def options
60
+ @options ||= base_options.merge(host_config_options)
61
+ end
62
+
51
63
  def port_bindings
52
- container.ports.inject({}) do |all, p|
64
+ container.ports.each_with_object({}) do |p, all|
53
65
  destination, source = p.split(':')
54
66
  all[source] = [ { 'HostPort' => destination } ]
55
- all
56
67
  end
57
68
  end
58
69
 
59
- def recreate?
60
- recreate
61
- end
62
-
63
- def create!(opts)
64
- build_image! unless container.image_exists?
70
+ def create!
71
+ container.buildable? ? build_image! : pull_image!
72
+ return unless container.startable?
65
73
  insert_scripts!
66
- create_container!(opts.fetch(:create, {}))
67
- execute_post_create_scripts! unless container.post_create_scripts.empty?
68
- set_dockerfile_md5!
74
+ create_container!
75
+ update_dockerfile_md5!
76
+ start!
69
77
  end
70
78
 
71
79
  def build_image!
72
- Build.new(container).execute!
80
+ Build.new(container).execute! unless container.image_exists?
73
81
  end
74
82
 
75
- def insert_scripts!
76
- insert_files!(container.post_create_scripts)
77
- insert_files!(container.post_start_scripts)
83
+ # FIXME: move this
84
+ def pull_image!
85
+ return nil if container.image_exists?
86
+ $logger.info "Pulling '#{container.image_name}' image"
87
+ Docker::Image.create(fromImage: container.image_name) do |out|
88
+ $logger.debug JSON.parse(out)
89
+ end
78
90
  end
79
91
 
80
- def create_container!(opts)
81
- options = base_options.merge(host_config_options).merge(opts)
82
- $logger.info "Creating '%s' container" % options['name']
92
+ def create_container!
93
+ $logger.info "Creating '#{container.name}' container"
83
94
  Docker::Container.create(options)
84
95
  end
85
96
 
86
- def execute_post_create_scripts!
87
- Exec.new(container, container.dependant_containers.values, container.post_create_scripts, 'POST create').execute!
97
+ def start!
98
+ return nil if !container.startable? || !start?
99
+ Start.new(container).execute!
88
100
  end
89
101
 
90
- def set_dockerfile_md5!
91
- $logger.info "Setting MD5 for '#{container.name}' container to #{container.current_dockerfile_md5}"
92
- $metastore.set("#{container.metastore_key}.dockerfile_md5", container.current_dockerfile_md5)
102
+ def update_dockerfile_md5!
103
+ container.update_dockerfile_md5!
93
104
  end
94
105
 
106
+ def insert_scripts!
107
+ return nil if container_image_existed?
108
+ insert_files!(container.post_start_scripts)
109
+ end
110
+
111
+ def insert_files!(files)
112
+ files.each { |file| insert_file!(file) }
113
+ end
114
+
115
+ def insert_file!(file)
116
+ file = Pathname.new(File.expand_path(file, base_dir))
117
+ opts = { 'localPath' => file.to_s, 'outputPath' => "/tmp/#{file.basename}" }
118
+ new_image = container.image.insert_local(opts)
119
+ new_image.tag(repo: container.image_repo, tag: container.version.to_s, force: true)
120
+ end
95
121
  end
96
122
  end
97
123
  end
@@ -12,11 +12,8 @@ module Percheron
12
12
  end
13
13
 
14
14
  def execute!
15
- results = []
16
- $logger.debug "Executing #{description} scripts '#{scripts.inspect}' on '#{container.name}'"
17
- started_dependant_containers = start_containers!(dependant_containers)
18
- results << execute_scripts_on_running_container!
19
- results << stop_containers!(started_dependant_containers)
15
+ $logger.debug "Executing #{description} #{scripts.inspect} on '#{container.name}' container"
16
+ results = exec!
20
17
  results.compact.empty? ? nil : container
21
18
  end
22
19
 
@@ -24,29 +21,64 @@ module Percheron
24
21
 
25
22
  attr_reader :container, :dependant_containers, :scripts, :description
26
23
 
24
+ def exec!
25
+ results = []
26
+ started_dependant_containers = start_containers!(dependant_containers)
27
+ results << execute_scripts_on_running_container!
28
+ results << stop_containers!(started_dependant_containers)
29
+ results
30
+ end
31
+
27
32
  def execute_scripts_on_running_container!
28
33
  container_running = container.running?
29
34
  Start.new(container, exec_scripts: false).execute! unless container_running
30
35
  execute_scripts!
36
+ commit_and_tag_new_image!
31
37
  Stop.new(container).execute! unless container_running
32
38
  end
33
39
 
40
+ # FIXME
41
+ def commit_and_tag_new_image!
42
+ new_image = container.docker_container.commit
43
+ new_image.tag(repo: container.image_repo, tag: container.version.to_s, force: true)
44
+ end
45
+
34
46
  def execute_scripts!
35
47
  scripts.each do |script|
36
48
  in_working_directory(base_dir) do
37
49
  file = Pathname.new(File.expand_path(script, base_dir))
38
- execute_command!('/bin/bash -x /tmp/%s 2>&1' % file.basename)
50
+ execute_command!('/bin/sh /tmp/%s 2>&1' % file.basename)
39
51
  end
40
52
  end
41
53
  end
42
54
 
43
55
  def execute_command!(command)
44
56
  $logger.info "Executing #{description} '#{command}' for '#{container.name}' container"
45
- container.docker_container.exec(command.split(' ')) do |device, out|
46
- $logger.debug '%s: %s' % [ device, out.strip ]
57
+ container.docker_container.exec(command.split(' ')) do |stream, out|
58
+ $logger.debug '%s: %s' % [ stream, out.strip ]
59
+ end
60
+ end
61
+
62
+ def stop_containers!(containers)
63
+ exec_on_containers!(containers) do |container|
64
+ Stop.new(container).execute! if container.running?
65
+ end
66
+ end
67
+
68
+ def start_containers!(containers, scripts: true)
69
+ exec_on_containers!(containers) do |container|
70
+ next if container.running?
71
+ containers = container.startable_dependant_containers.values
72
+ Start.new(container, dependant_containers: containers, exec_scripts: scripts).execute!
47
73
  end
48
74
  end
49
75
 
76
+ def exec_on_containers!(containers)
77
+ containers.each_with_object([]) do |container, all|
78
+ all << container if yield(container)
79
+ end.compact
80
+ end
81
+
50
82
  end
51
83
  end
52
84
  end
@@ -26,15 +26,15 @@ module Percheron
26
26
  $logger.debug "Executing #{description} scripts '#{scripts.inspect}' locally"
27
27
  scripts.each do |script|
28
28
  in_working_directory(base_dir) do
29
- execute_command!('/bin/bash -x %s 2>&1' % Pathname.new(File.expand_path(script)))
29
+ execute_command!('/bin/sh %s 2>&1' % Pathname.new(File.expand_path(script)))
30
30
  end
31
31
  end
32
32
  end
33
33
 
34
34
  def execute_command!(command)
35
- $logger.info "Executing #{description} '#{command}' locally"
36
- Open3.popen2e(command) do |stdin, stdout_stderr, wait_thr|
37
- while line = stdout_stderr.gets
35
+ $logger.info "Executing #{description} script '#{command}' locally"
36
+ Open3.popen2e(command) do |_, stdout_stderr, _|
37
+ while (line = stdout_stderr.gets)
38
38
  $logger.debug line.strip
39
39
  end
40
40
  end
@@ -0,0 +1,44 @@
1
+ module Percheron
2
+ module Actions
3
+ class Logs
4
+ include Base
5
+
6
+ def initialize(container, follow: false)
7
+ @container = container
8
+ @follow = follow
9
+ end
10
+
11
+ def execute!
12
+ $logger.debug "Showing logs on '#{container.name}' container"
13
+ display_logs!
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :container, :follow
19
+ alias_method :follow?, :follow
20
+
21
+ def options
22
+ {
23
+ stdout: true,
24
+ stderr: true,
25
+ timestamps: true,
26
+ tail: 100
27
+ }
28
+ end
29
+
30
+ def display_logs!
31
+ if follow?
32
+ opts = options.merge(follow: true)
33
+ container.docker_container.streaming_logs(opts) do |stream, chunk|
34
+ puts "#{stream}: #{chunk}"
35
+ end
36
+ else
37
+ puts container.docker_container.logs(options)
38
+ end
39
+ rescue Interrupt
40
+ nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -4,34 +4,45 @@ module Percheron
4
4
 
5
5
  include Base
6
6
 
7
- def initialize(container)
7
+ def initialize(container, delete_image: true)
8
8
  @container = container
9
+ @delete_image = delete_image
9
10
  end
10
11
 
11
12
  def execute!
12
13
  results = []
13
14
  results << stop!
14
- results << delete_container! if container.exists?
15
- results << delete_image! if container.image_exists?
15
+ results << delete_container!
16
+ results << delete_image!
16
17
  results.compact.empty? ? nil : container
17
18
  end
18
19
 
19
20
  private
20
21
 
21
- attr_reader :container
22
+ attr_reader :container, :delete_image
22
23
 
23
24
  def stop!
24
25
  Stop.new(container).execute!
25
26
  end
26
27
 
28
+ def delete_image?
29
+ delete_image && container.image_exists? && container.buildable?
30
+ end
31
+
27
32
  def delete_container!
33
+ return nil unless container.exists?
28
34
  $logger.info "Deleting '#{container.name}' container"
29
35
  container.docker_container.remove
36
+ rescue Docker::Error::ConflictError => e
37
+ $logger.error "Unable to delete '%s' container - %s" % [ container.name, e.inspect ]
30
38
  end
31
39
 
32
40
  def delete_image!
41
+ return nil unless delete_image?
33
42
  $logger.info "Deleting '#{container.image_name}' image"
34
43
  container.image.remove
44
+ rescue Docker::Error::ConflictError => e
45
+ $logger.error "Unable to delete '%s' image - %s" % [ container.image_name, e.inspect ]
35
46
  end
36
47
 
37
48
  end