percheron 0.7.16 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -4
  3. data/CHANGELOG.md +8 -0
  4. data/Guardfile +1 -1
  5. data/lib/percheron/actions/build.rb +14 -7
  6. data/lib/percheron/actions/create.rb +50 -39
  7. data/lib/percheron/actions/exec.rb +8 -9
  8. data/lib/percheron/actions/exec_local.rb +1 -2
  9. data/lib/percheron/actions/purge.rb +18 -13
  10. data/lib/percheron/actions/restart.rb +1 -2
  11. data/lib/percheron/actions/shell.rb +4 -4
  12. data/lib/percheron/actions/start.rb +5 -6
  13. data/lib/percheron/actions/stop.rb +0 -1
  14. data/lib/percheron/actions.rb +0 -1
  15. data/lib/percheron/commands/abstract.rb +11 -6
  16. data/lib/percheron/commands/build.rb +3 -2
  17. data/lib/percheron/commands/console.rb +0 -5
  18. data/lib/percheron/commands/create.rb +7 -2
  19. data/lib/percheron/commands/logs.rb +1 -1
  20. data/lib/percheron/commands/main.rb +3 -4
  21. data/lib/percheron/commands/purge.rb +11 -2
  22. data/lib/percheron/commands/restart.rb +1 -1
  23. data/lib/percheron/commands/shell.rb +1 -1
  24. data/lib/percheron/commands/start.rb +1 -1
  25. data/lib/percheron/commands/stop.rb +1 -1
  26. data/lib/percheron/commands.rb +0 -1
  27. data/lib/percheron/config.rb +8 -5
  28. data/lib/percheron/errors.rb +1 -0
  29. data/lib/percheron/graph.rb +3 -4
  30. data/lib/percheron/logger.rb +1 -1
  31. data/lib/percheron/stack.rb +44 -41
  32. data/lib/percheron/unit/image_helper.rb +33 -0
  33. data/lib/percheron/unit.rb +18 -35
  34. data/lib/percheron/version.rb +1 -1
  35. data/lib/percheron.rb +1 -0
  36. data/percheron.gemspec +1 -1
  37. metadata +5 -6
  38. data/lib/percheron/actions/recreate.rb +0 -51
  39. data/lib/percheron/commands/recreate.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b3358819570477f97ff3f4c1b21b689a7007390
4
- data.tar.gz: 04f82dae87c48a51b0ebff986bf4bab889036c7b
3
+ metadata.gz: 4c4858cd5f4ec9b26b7976179c56f074a1cb5570
4
+ data.tar.gz: 050b74b700b9c127766e5454eba7aea97c85ba7c
5
5
  SHA512:
6
- metadata.gz: 4cafe93d3bddf33b7098cdae3aa87afbe4a4a27d78f65b3e3a36c96808cd9867bbd2881a3a6499f7f334d97a1f8565402129117c93b32cbe32b04a44432005d0
7
- data.tar.gz: 502ca1ee96c79cda8f55b347cb1b3ba30f61645374540a057143ea4b3b6ff0cca0ce06ab450f982e812a315ecd5798b9cd12ca6e1875a5fe4550af62485a8e85
6
+ metadata.gz: 9865aad4f0d56c8e2607bd74dc579443d7c645afc20c1362b855ad867c09475080d5588ffcdf89f86ed89e5cf7ab0e3791b2f85cc8e91565b54fda597198ecb0
7
+ data.tar.gz: 438581d49d52687a2d06cf5d6bd538b3f1bf0df2dfe7b5dab655e1b2bd5a87f62fa5a194f559a3b41dfdf5bebeb32f54aadc24c0a4be3bdc5f6f510ee7bd31ef
data/.rubocop.yml CHANGED
@@ -19,10 +19,6 @@ Metrics/LineLength:
19
19
  Style/GlobalVars:
20
20
  Enabled: false
21
21
 
22
- Style/RegexpLiteral:
23
- EnforcedStyle: mixed
24
- AllowInnerSlashes: false
25
-
26
22
  Metrics/ClassLength:
27
23
  CountComments: false
28
24
  Max: 150
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## v0.8.0 / 2015-08-19
2
+
3
+ * Hostname is the full `<stack_name>_<name>` by default
4
+ * Support for defining restart policy for each unit
5
+ * Support privileged mode
6
+ * Tidied up (re)create, (re)build and log commands
7
+ * Improved logging and error handling
8
+
1
9
  ## v0.7.16 / 2015-08-07
2
10
 
3
11
  * Improved graph look
data/Guardfile CHANGED
@@ -1,5 +1,5 @@
1
1
  guard :rspec, cmd: 'bundle exec rspec' do
2
2
  watch(%r{^spec\/.+_spec\.rb$})
3
3
  watch(%r{^lib\/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
4
- watch('spec/spec_helper.rb') { 'spec' }
4
+ watch('spec/spec_helper.rb') { 'spec' }
5
5
  end
@@ -1,12 +1,11 @@
1
1
  module Percheron
2
2
  module Actions
3
3
  class Build
4
-
5
4
  include Base
6
5
 
7
- def initialize(unit, nocache: false, forcerm: false, exec_scripts: true)
6
+ def initialize(unit, usecache: true, forcerm: false, exec_scripts: true)
8
7
  @unit = unit
9
- @nocache = nocache
8
+ @usecache = usecache
10
9
  @forcerm = forcerm
11
10
  @exec_scripts = exec_scripts
12
11
  end
@@ -22,15 +21,17 @@ module Percheron
22
21
 
23
22
  private
24
23
 
25
- attr_reader :unit, :nocache, :forcerm, :exec_scripts
24
+ attr_reader :unit, :usecache, :forcerm, :exec_scripts
25
+ alias_method :usecache?, :usecache
26
+ alias_method :forcerm?, :forcerm
26
27
  alias_method :exec_scripts?, :exec_scripts
27
28
 
28
29
  def options
29
30
  {
30
31
  'dockerfile' => dockerfile,
31
32
  't' => unit.image_name,
32
- 'forcerm' => forcerm,
33
- 'nocache' => nocache
33
+ 'forcerm' => forcerm?,
34
+ 'nocache' => !usecache?
34
35
  }
35
36
  end
36
37
 
@@ -67,13 +68,19 @@ module Percheron
67
68
  execute_pre_build_scripts!
68
69
  $logger.info "Building '#{unit.image_name}' image"
69
70
  Connection.perform(Docker::Image, :build_from_dir, base_dir, options) do |out|
70
- $logger.debug '%s' % [ out.strip ]
71
+ $logger.info '%s' % [ extract_content(out) ]
71
72
  end
72
73
  end
73
74
  ensure
74
75
  remove_temp_dockerfile!
75
76
  end
76
77
 
78
+ def extract_content(out)
79
+ json = JSON.parse(out)
80
+ return '' unless json['stream']
81
+ json['stream'].strip
82
+ end
83
+
77
84
  def execute_pre_build_scripts!
78
85
  return nil if !exec_scripts? && unit.pre_build_scripts.empty?
79
86
  ExecLocal.new(unit, unit.pre_build_scripts, 'PRE build').execute!
@@ -1,36 +1,32 @@
1
1
  module Percheron
2
2
  module Actions
3
3
  class Create
4
-
5
4
  include Base
6
5
 
7
- def initialize(unit, start: false, cmd: false, exec_scripts: true)
6
+ def initialize(unit, build: true, start: false, force: false, cmd: false)
8
7
  @unit = unit
8
+ @build = build
9
9
  @start = start
10
- @exec_scripts = exec_scripts
11
- @cmd = cmd
12
- @unit_image_existed = unit.image_exists?
10
+ @force = force
11
+ @cmd = (cmd || unit.start_args)
13
12
  end
14
13
 
15
14
  def execute!
16
15
  results = []
17
- if unit.exists?
18
- $logger.debug "Unit '#{unit.display_name}' already exists"
19
- else
20
- results << create!
21
- end
16
+ results << build_or_pull_image!
17
+ results << create!
22
18
  results.compact.empty? ? nil : unit
23
19
  end
24
20
 
25
21
  private
26
22
 
27
- attr_reader :unit, :start, :exec_scripts, :unit_image_existed
23
+ attr_reader :unit, :build, :start, :force, :cmd
24
+ alias_method :build?, :build
28
25
  alias_method :start?, :start
29
- alias_method :exec_scripts?, :exec_scripts
30
- alias_method :unit_image_existed?, :unit_image_existed
26
+ alias_method :force?, :force
31
27
 
32
- def cmd
33
- @cmd ||= (@cmd || unit.start_args)
28
+ def create?
29
+ unit.startable? && (!unit.exists? || force)
34
30
  end
35
31
 
36
32
  def base_options
@@ -46,19 +42,25 @@ module Percheron
46
42
  end
47
43
 
48
44
  def host_config_options
49
- config = {
50
- 'HostConfig' => {
45
+ {
46
+ 'HostConfig' => {
51
47
  'PortBindings' => port_bindings,
52
48
  'Links' => unit.links,
53
- 'Binds' => unit.volumes
49
+ 'Binds' => unit.volumes,
50
+ 'RestartPolicy' => unit.restart_policy,
51
+ 'Privileged' => unit.privileged
54
52
  }
55
53
  }
56
- config['Dns'] = unit.dns unless unit.dns.empty?
57
- config
54
+ end
55
+
56
+ def host_config_dns_options
57
+ unit.dns.empty? ? {} : { 'HostConfig' => { 'Dns' => unit.dns } }
58
58
  end
59
59
 
60
60
  def options
61
- @options ||= base_options.merge(host_config_options)
61
+ @options ||= begin
62
+ base_options.merge(host_config_options).merge(host_config_dns_options)
63
+ end
62
64
  end
63
65
 
64
66
  def port_bindings
@@ -68,49 +70,58 @@ module Percheron
68
70
  end
69
71
  end
70
72
 
71
- def create!
73
+ def build_or_pull_image!
72
74
  unit.buildable? ? build_image! : pull_image!
73
- return unless unit.startable?
74
- insert_scripts!
75
- create_unit!
76
- update_dockerfile_md5!
77
- start!
75
+ end
76
+
77
+ def create!
78
+ if create?
79
+ create_unit!
80
+ update_dockerfile_md5!
81
+ start_and_insert_scripts! if start?
82
+ else
83
+ $logger.warn("Unit '#{unit.display_name}' already exists (--force to overwrite)")
84
+ end
85
+ rescue Errors::DockerContainerCannotDelete => e
86
+ $logger.error "Unable to delete '%s' unit - %s" % [ unit.name, e.inspect ]
78
87
  end
79
88
 
80
89
  def build_image!
81
- Build.new(unit).execute! unless unit.image_exists?
90
+ Build.new(unit).execute! if build?
82
91
  end
83
92
 
84
- # FIXME: move this
85
93
  def pull_image!
86
94
  return nil if unit.image_exists?
87
95
  $logger.info "Pulling '#{unit.image_name}' image"
88
96
  Connection.perform(Docker::Image, :create, fromImage: unit.image_name) do |out|
89
- $logger.debug JSON.parse(out)
97
+ $logger.info JSON.parse(out)
90
98
  end
91
99
  end
92
100
 
101
+ def delete_unit!
102
+ $logger.info "Deleting '#{unit.display_name}' unit"
103
+ unit.container.remove(force: force?)
104
+ rescue Docker::Error::ConflictError => e
105
+ raise(Errors::DockerContainerCannotDelete.new, e)
106
+ end
107
+
93
108
  def create_unit!
109
+ delete_unit! if force?
94
110
  $logger.info "Creating '#{unit.display_name}' unit"
95
111
  Connection.perform(Docker::Container, :create, options)
96
112
  end
97
113
 
98
- def start!
99
- return nil if !unit.startable? || !start?
114
+ def start_and_insert_scripts!
100
115
  Start.new(unit).execute!
116
+ insert_post_start_scripts!
101
117
  end
102
118
 
103
119
  def update_dockerfile_md5!
104
120
  unit.update_dockerfile_md5!
105
121
  end
106
122
 
107
- def insert_scripts!
108
- return nil if unit_image_existed?
109
- insert_files!(unit.post_start_scripts)
110
- end
111
-
112
- def insert_files!(files)
113
- files.each { |file| insert_file!(file) }
123
+ def insert_post_start_scripts!
124
+ unit.post_start_scripts.each { |file| insert_file!(file) }
114
125
  end
115
126
 
116
127
  def insert_file!(file)
@@ -1,12 +1,11 @@
1
1
  module Percheron
2
2
  module Actions
3
3
  class Exec
4
-
5
4
  include Base
6
5
 
7
- def initialize(unit, dependant_units, scripts, description)
6
+ def initialize(unit, needed_units, scripts, description)
8
7
  @unit = unit
9
- @dependant_units = dependant_units
8
+ @needed_units = needed_units
10
9
  @scripts = scripts
11
10
  @description = description
12
11
  end
@@ -19,13 +18,13 @@ module Percheron
19
18
 
20
19
  private
21
20
 
22
- attr_reader :unit, :dependant_units, :scripts, :description
21
+ attr_reader :unit, :needed_units, :scripts, :description
23
22
 
24
23
  def exec!
25
24
  results = []
26
- started_dependant_units = start_units!(dependant_units)
25
+ started_needed_units = start_units!(needed_units)
27
26
  results << execute_scripts_on_running_unit!
28
- results << stop_units!(started_dependant_units)
27
+ results << stop_units!(started_needed_units)
29
28
  results
30
29
  end
31
30
 
@@ -34,7 +33,7 @@ module Percheron
34
33
  Start.new(unit, exec_scripts: false).execute! unless unit_running
35
34
  execute_scripts!
36
35
  commit_and_tag_new_image!
37
- Stop.new(unit).execute! unless unit_running
36
+ Stop.new(unit).execute! unless unit_running
38
37
  end
39
38
 
40
39
  def commit_and_tag_new_image!
@@ -67,8 +66,8 @@ module Percheron
67
66
  def start_units!(units, scripts: true)
68
67
  exec_on_units!(units) do |unit|
69
68
  next if unit.running?
70
- units = unit.startable_dependant_units.values
71
- Start.new(unit, dependant_units: units, exec_scripts: scripts).execute!
69
+ units = unit.startable_needed_units.values
70
+ Start.new(unit, needed_units: units, exec_scripts: scripts).execute!
72
71
  end
73
72
  end
74
73
 
@@ -3,7 +3,6 @@ require 'open3'
3
3
  module Percheron
4
4
  module Actions
5
5
  class ExecLocal
6
-
7
6
  include Base
8
7
 
9
8
  def initialize(unit, scripts, description)
@@ -26,7 +25,7 @@ module Percheron
26
25
  $logger.debug "Executing #{description} scripts '#{scripts.inspect}' locally"
27
26
  scripts.each do |script|
28
27
  in_working_directory(base_dir) do
29
- execute_command!('/bin/sh -x %s 2>&1' % Pathname.new(File.expand_path(script)))
28
+ execute_command!('/bin/sh -x %s 2>&1' % [ Pathname.new(File.expand_path(script)) ])
30
29
  end
31
30
  end
32
31
  end
@@ -1,7 +1,6 @@
1
1
  module Percheron
2
2
  module Actions
3
3
  class Purge
4
-
5
4
  include Base
6
5
 
7
6
  def initialize(unit, force: false)
@@ -12,8 +11,8 @@ module Percheron
12
11
  def execute!
13
12
  results = []
14
13
  results << stop!
15
- results << delete_unit!
16
- results << delete_image!
14
+ results << delete_unit! if delete_unit?
15
+ results << delete_image! if delete_image?
17
16
  results.compact.empty? ? nil : unit
18
17
  end
19
18
 
@@ -25,26 +24,32 @@ module Percheron
25
24
  Stop.new(unit).execute!
26
25
  end
27
26
 
27
+ def delete_unit?
28
+ unit.exists?
29
+ end
30
+
28
31
  def delete_image?
29
32
  unit.image_exists? && unit.buildable?
30
33
  end
31
34
 
35
+ def opts
36
+ { force: force }
37
+ end
38
+
32
39
  def delete_unit!
33
- return nil unless unit.exists?
34
- $logger.info "Deleting '#{unit.display_name}' unit"
35
- unit.container.remove(force: force)
36
- rescue Docker::Error::ConflictError => e
37
- $logger.error "Unable to delete '%s' unit - %s" % [ unit.name, e.inspect ]
40
+ delete!('unit', unit.display_name) { unit.container.remove(opts) }
38
41
  end
39
42
 
40
43
  def delete_image!
41
- return nil unless delete_image?
42
- $logger.info "Deleting '#{unit.image_name}' image"
43
- unit.image.remove(force: force)
44
- rescue Docker::Error::ConflictError => e
45
- $logger.error "Unable to delete '%s' image - %s" % [ unit.image_name, e.inspect ]
44
+ delete!('image', unit.image_name) { unit.image.remove(opts) }
46
45
  end
47
46
 
47
+ def delete!(type, value)
48
+ $logger.info("Deleting '%s' %s" % [ value, type ])
49
+ yield
50
+ rescue Docker::Error::ConflictError => e
51
+ $logger.error("Unable to delete '%s' %s - %s" % [ value, type, e.inspect ])
52
+ end
48
53
  end
49
54
  end
50
55
  end
@@ -1,7 +1,6 @@
1
1
  module Percheron
2
2
  module Actions
3
3
  class Restart
4
-
5
4
  include Base
6
5
 
7
6
  def initialize(unit)
@@ -24,7 +23,7 @@ module Percheron
24
23
  end
25
24
 
26
25
  def start!
27
- opts = { dependant_units: unit.startable_dependant_units.values }
26
+ opts = { needed_units: unit.startable_needed_units.values }
28
27
  Start.new(unit, opts).execute!
29
28
  end
30
29
 
@@ -6,9 +6,9 @@ module Percheron
6
6
  DEFAULT_COMMAND = '/bin/sh'
7
7
  DOCKER_CLIENT = 'docker'
8
8
 
9
- def initialize(unit, command: DEFAULT_COMMAND)
9
+ def initialize(unit, raw_command: DEFAULT_COMMAND)
10
10
  @unit = unit
11
- @command = command
11
+ @raw_command = raw_command
12
12
  end
13
13
 
14
14
  def execute!
@@ -17,14 +17,14 @@ module Percheron
17
17
 
18
18
  private
19
19
 
20
- attr_reader :unit
20
+ attr_reader :unit, :raw_command
21
21
 
22
22
  def valid?
23
23
  Validators::DockerClient.new.valid?
24
24
  end
25
25
 
26
26
  def command
27
- "sh -c '%s'" % @command
27
+ "sh -c '%s'" % [ raw_command ]
28
28
  end
29
29
 
30
30
  def exec!
@@ -1,12 +1,11 @@
1
1
  module Percheron
2
2
  module Actions
3
3
  class Start
4
-
5
4
  include Base
6
5
 
7
- def initialize(unit, dependant_units: [], cmd: false, exec_scripts: true)
6
+ def initialize(unit, needed_units: [], cmd: false, exec_scripts: true)
8
7
  @unit = unit
9
- @dependant_units = dependant_units
8
+ @needed_units = needed_units
10
9
  @cmd = cmd
11
10
  @exec_scripts = exec_scripts
12
11
  end
@@ -23,7 +22,7 @@ module Percheron
23
22
 
24
23
  private
25
24
 
26
- attr_reader :unit, :dependant_units, :cmd, :exec_scripts
25
+ attr_reader :unit, :needed_units, :cmd, :exec_scripts
27
26
 
28
27
  def exec_scripts?
29
28
  !unit.post_start_scripts.empty? && exec_scripts
@@ -31,7 +30,7 @@ module Percheron
31
30
 
32
31
  def create!
33
32
  return nil if unit.exists?
34
- Create.new(unit, cmd: cmd, exec_scripts: exec_scripts).execute!
33
+ Create.new(unit, cmd: cmd).execute!
35
34
  end
36
35
 
37
36
  def start!
@@ -42,7 +41,7 @@ module Percheron
42
41
 
43
42
  def execute_post_start_scripts!
44
43
  scripts = unit.post_start_scripts
45
- Exec.new(unit, dependant_units, scripts, 'POST start').execute! if exec_scripts?
44
+ Exec.new(unit, needed_units, scripts, 'POST start').execute! if exec_scripts?
46
45
  end
47
46
 
48
47
  end
@@ -1,7 +1,6 @@
1
1
  module Percheron
2
2
  module Actions
3
3
  class Stop
4
-
5
4
  include Base
6
5
 
7
6
  def initialize(unit)
@@ -3,7 +3,6 @@ require 'percheron/actions/stop'
3
3
  require 'percheron/actions/start'
4
4
  require 'percheron/actions/restart'
5
5
  require 'percheron/actions/create'
6
- require 'percheron/actions/recreate'
7
6
  require 'percheron/actions/build'
8
7
  require 'percheron/actions/purge'
9
8
  require 'percheron/actions/exec'
@@ -17,16 +17,24 @@ module Percheron
17
17
  end
18
18
  end
19
19
 
20
- def self.default_create_parameters!
21
- default_parameters!
22
- option('--start', :flag, 'Start unit', default: false)
20
+ def runit
21
+ yield
22
+ rescue Docker::Error::UnexpectedResponseError => e
23
+ $logger.error('')
24
+ $logger.error('An exception occurred :(')
25
+ $logger.error('')
26
+ $logger.error(e.inspect)
23
27
  end
24
28
 
25
29
  def execute
26
30
  stack.valid?
31
+ rescue Errno::ENOENT, Errors::ConfigFileInvalid, Errors::StackInvalid => e
32
+ signal_usage_error(e.message)
33
+ exit(1)
27
34
  rescue => e
28
35
  puts "%s\n\n%s\n\n" % [ e.inspect, e.backtrace.join("\n") ]
29
36
  signal_usage_error(e.message)
37
+ exit(1)
30
38
  end
31
39
 
32
40
  def stack
@@ -40,9 +48,6 @@ module Percheron
40
48
  Percheron::Connection.load!(c)
41
49
  end
42
50
  end
43
- rescue Errors::ConfigFileInvalid => e
44
- $logger.error e.inspect
45
- exit(1)
46
51
  end
47
52
  end
48
53
  end
@@ -3,11 +3,12 @@ module Percheron
3
3
  class Build < Abstract
4
4
 
5
5
  default_parameters!
6
- option('--forcerm', :flag, 'force removal of intermediate containers', default: false)
6
+ option('--usecache', :flag, 'Use image cache', default: true)
7
+ option('--forcerm', :flag, 'Force removal of intermediate containers', default: false)
7
8
 
8
9
  def execute
9
10
  super
10
- stack.build!(unit_names: unit_names, forcerm: forcerm?)
11
+ runit { stack.build!(unit_names: unit_names, usecache: usecache?, forcerm: forcerm?) }
11
12
  end
12
13
  end
13
14
  end
@@ -32,11 +32,6 @@ module Percheron
32
32
  nil
33
33
  end
34
34
 
35
- def recreate(unit_names, start: false)
36
- stack.create!(unit_names: [ *unit_names ], start: start)
37
- nil
38
- end
39
-
40
35
  def start(unit_names)
41
36
  stack.start!(unit_names: [ *unit_names ])
42
37
  nil
@@ -2,11 +2,16 @@ module Percheron
2
2
  module Commands
3
3
  class Create < Abstract
4
4
 
5
- default_create_parameters!
5
+ default_parameters!
6
+ option('--start', :flag, '(Re)start unit once created', default: true)
7
+ option('--build', :flag, '(Re)build image', default: true)
8
+ option('--deep', :flag, 'Include needed units', default: false)
9
+ option('--force', :flag, 'Force unit (re)creation', default: false)
6
10
 
7
11
  def execute
8
12
  super
9
- stack.create!(unit_names: unit_names, start: start?)
13
+ opts = { unit_names: unit_names, build: build?, start: start?, deep: deep?, force: force? }
14
+ runit { stack.create!(opts) }
10
15
  end
11
16
  end
12
17
  end
@@ -4,7 +4,7 @@ module Percheron
4
4
 
5
5
  parameter('STACK_NAME', 'stack name', required: true)
6
6
  parameter('UNIT_NAME', 'unit name', required: true)
7
- option('--follow', :flag, 'follow the logs', default: false)
7
+ option([ '-f', '-t', '--follow', '--tail' ], :flag, 'Follow the logs', default: false)
8
8
 
9
9
  def execute
10
10
  super
@@ -2,13 +2,12 @@ module Percheron
2
2
  module Commands
3
3
  class Main < Abstract
4
4
  subcommand %w(list status), 'List stacks and its units', List
5
- subcommand 'console', 'Start a pry console session', Console
5
+ subcommand 'console', '', Console
6
6
  subcommand 'start', 'Start a stack', Start
7
7
  subcommand 'stop', 'Stop a stack', Stop
8
8
  subcommand 'restart', 'Restart a stack', Restart
9
- subcommand 'build', 'Build images for a stack', Build
10
- subcommand 'create', 'Build images and create units for a stack', Create
11
- subcommand 'recreate', 'Recreate a stack', Recreate
9
+ subcommand %w(build rebuild), '(Re)build image(s) for a stack', Build
10
+ subcommand %w(create recreate), '(Re)build image(s) and (re)create units for a stack', Create
12
11
  subcommand 'purge', 'Purge a stack', Purge
13
12
  subcommand 'shell', 'Shell into a unit', Shell
14
13
  subcommand 'logs', 'Show logs for a unit', Logs
@@ -3,12 +3,21 @@ module Percheron
3
3
  class Purge < Abstract
4
4
 
5
5
  default_parameters!
6
- option([ '-f', '--force' ], :flag, 'Force unit/image removal', default: false)
6
+ option('--yes', :flag, 'Yes, purge image / unit', default: false)
7
+ option('--force', :flag, 'Force image / unit removal', default: false)
7
8
 
8
9
  def execute
9
10
  super
10
- stack.purge!(unit_names: unit_names, force: force?)
11
+ runit { stack.purge!(unit_names: unit_names, force: force?) if yes? || confirm? }
11
12
  end
13
+
14
+ private
15
+
16
+ def confirm?
17
+ ask('Are you sure? (y|n) ') do |q|
18
+ q.validate = /y(es)?|n(o)?/i
19
+ end.match(/y(es)?/i)
20
+ end
12
21
  end
13
22
  end
14
23
  end
@@ -6,7 +6,7 @@ module Percheron
6
6
 
7
7
  def execute
8
8
  super
9
- stack.restart!(unit_names: unit_names)
9
+ runit { stack.restart!(unit_names: unit_names) }
10
10
  end
11
11
  end
12
12
  end
@@ -8,7 +8,7 @@ module Percheron
8
8
 
9
9
  def execute
10
10
  super
11
- stack.shell!(unit_name, command: command)
11
+ stack.shell!(unit_name, raw_command: command)
12
12
  rescue Errors::DockerClientInvalid => e
13
13
  signal_usage_error(e.message)
14
14
  end
@@ -6,7 +6,7 @@ module Percheron
6
6
 
7
7
  def execute
8
8
  super
9
- stack.start!(unit_names: unit_names)
9
+ runit { stack.start!(unit_names: unit_names) }
10
10
  end
11
11
  end
12
12
  end
@@ -6,7 +6,7 @@ module Percheron
6
6
 
7
7
  def execute
8
8
  super
9
- stack.stop!(unit_names: unit_names)
9
+ runit { stack.stop!(unit_names: unit_names) }
10
10
  end
11
11
  end
12
12
  end
@@ -9,7 +9,6 @@ require 'percheron/commands/purge'
9
9
  require 'percheron/commands/console'
10
10
  require 'percheron/commands/create'
11
11
  require 'percheron/commands/build'
12
- require 'percheron/commands/recreate'
13
12
  require 'percheron/commands/shell'
14
13
  require 'percheron/commands/logs'
15
14
  require 'percheron/commands/graph'
@@ -78,7 +78,8 @@ module Percheron
78
78
  @stacks = @yaml_contents = @raw_contents = @contents = nil
79
79
  end
80
80
 
81
- def process_stacks! # FIXME: bugs here :(
81
+ # FIXME: bugs here :(
82
+ def process_stacks!
82
83
  stacks_by_name = contents.stacks.to_hash_by_key(:name)
83
84
  scanned = scan_unit_configs(stacks_by_name)
84
85
  stacks_by_name.each do |_, stack|
@@ -103,14 +104,15 @@ module Percheron
103
104
  end
104
105
 
105
106
  def replace_scanned(all, config, scanned)
106
- match = config.fetch(:dependant_unit_names, [])
107
+ match = config.fetch(:needed_unit_names, [])
107
108
  unless (match & scanned.keys).empty?
108
- config.dependant_unit_names = match.map { |v| scanned[v] }.flatten
109
+ config.needed_unit_names = match.map { |v| scanned[v] }.flatten
109
110
  end
110
111
  all[config.name] = config
111
112
  end
112
113
 
113
- def scan_unit_configs(stacks_by_name) # FIXME
114
+ # FIXME
115
+ def scan_unit_configs(stacks_by_name)
114
116
  all = {}
115
117
  stacks_by_name.each do |_, stack|
116
118
  stack.fetch(:units, []).each do |unit_config|
@@ -123,7 +125,8 @@ module Percheron
123
125
  all
124
126
  end
125
127
 
126
- def expand_unit_config(unit_config, new_unit_names) # FIXME
128
+ # FIXME
129
+ def expand_unit_config(unit_config, new_unit_names)
127
130
  new_unit_names.each_with_object({}) do |new_name, all|
128
131
  temp_unit_config = unit_config.dup
129
132
  temp_unit_config.delete(:instances)
@@ -5,5 +5,6 @@ module Percheron
5
5
  class UnitInvalid < StandardError; end
6
6
  class UnitDoesNotExist < StandardError; end
7
7
  class DockerClientInvalid < StandardError; end
8
+ class DockerContainerCannotDelete < StandardError; end
8
9
  end
9
10
  end
@@ -87,7 +87,7 @@ module Percheron
87
87
  unit.ports.each do |ports|
88
88
  label << '<font point-size="11">p: %s, i: %s</font>' % ports.split(':')
89
89
  end
90
- { shape: shape, label: '<%s>' % label.join('<br/>'), fontname: 'arial' }
90
+ { shape: shape, label: '<%s>' % [ label.join('<br/>') ], fontname: 'arial' }
91
91
  end
92
92
 
93
93
  def pseudo_node_opts(unit)
@@ -96,8 +96,8 @@ module Percheron
96
96
 
97
97
  def add_links
98
98
  units.each do |name, unit|
99
- unit.dependant_units.each do |dependant_name, dependant_unit|
100
- graph.add_edges(nodes[name], nodes[dependant_name], node_link_opts(dependant_unit))
99
+ unit.needed_units.each do |needed_name, needed_unit|
100
+ graph.add_edges(nodes[name], nodes[needed_name], node_link_opts(needed_unit))
101
101
  end
102
102
  end
103
103
  end
@@ -108,6 +108,5 @@ module Percheron
108
108
  color = unit.startable? ? 'black' : 'gray'
109
109
  { dir: direction, style: style, color: color }
110
110
  end
111
-
112
111
  end
113
112
  end
@@ -6,7 +6,7 @@ logger_level = Logger::INFO
6
6
  logger_level = Logger::WARN if ENV['QUIET'] == 'true'
7
7
 
8
8
  # :nocov:
9
- if ENV['DEBUG'] == 'true' || ENV['DOCKER_DEBUG'] == 'true'
9
+ if [ ENV['DEBUG'], ENV['DOCKER_DEBUG'] ].include?('true')
10
10
  logger_level = Logger::DEBUG
11
11
  Docker.logger = $logger if ENV['DOCKER_DEBUG'] == 'true'
12
12
  end
@@ -22,7 +22,7 @@ module Percheron
22
22
  end
23
23
 
24
24
  def metastore_key
25
- @metastore_key ||= 'stacks.%s' % name
25
+ @metastore_key ||= 'stacks.%s' % [ name ]
26
26
  end
27
27
 
28
28
  def unit_configs
@@ -30,7 +30,7 @@ module Percheron
30
30
  end
31
31
 
32
32
  def units(unit_names = [])
33
- unit_names = !unit_names.empty? ? unit_names : filter_unit_names
33
+ unit_names = unit_names.empty? ? stack_units.keys : unit_names
34
34
  unit_names.each_with_object({}) do |unit_name, all|
35
35
  all[unit_name] = unit_from_name(unit_name)
36
36
  end
@@ -38,11 +38,11 @@ module Percheron
38
38
 
39
39
  def graph!(file)
40
40
  Graph.new(self).save!(file)
41
- $logger.info "Saved '%s'" % file
41
+ $logger.info "Saved '%s'" % [ file ]
42
42
  end
43
43
 
44
- def shell!(unit_name, command: Percheron::Actions::Shell::DEFAULT_COMMAND)
45
- Actions::Shell.new(unit_from_name(unit_name), command: command).execute!
44
+ def shell!(unit_name, raw_command: Percheron::Actions::Shell::DEFAULT_COMMAND)
45
+ Actions::Shell.new(unit_from_name(unit_name), raw_command: raw_command).execute!
46
46
  end
47
47
 
48
48
  def logs!(unit_name, follow: false)
@@ -50,50 +50,53 @@ module Percheron
50
50
  end
51
51
 
52
52
  def stop!(unit_names: [])
53
+ unit_names = stack_units.keys if unit_names.empty?
53
54
  execute!(Actions::Stop, filter_unit_names(unit_names).reverse)
54
55
  end
55
56
 
56
- # FIXME: bug when non-startable unit specified, all units started
57
57
  def start!(unit_names: [])
58
- unit_names = dependant_units_for(unit_names)
59
- exec_on_dependant_units_for(unit_names) do |unit|
60
- dependant_units = unit.startable_dependant_units.values
61
- Actions::Start.new(unit, dependant_units: dependant_units).execute!
58
+ unit_names = stack_units.keys if unit_names.empty?
59
+ unit_names = needed_units_for(unit_names)
60
+ exec_on_needed_units_for(unit_names) do |unit|
61
+ needed_units = unit.startable_needed_units.values
62
+ Actions::Start.new(unit, needed_units: needed_units).execute!
62
63
  end
63
64
  nil
64
65
  end
65
66
 
66
67
  def restart!(unit_names: [])
68
+ unit_names = stack_units.keys if unit_names.empty?
67
69
  execute!(Actions::Restart, filter_unit_names(unit_names))
68
70
  end
69
71
 
70
- def build!(unit_names: [], forcerm: false)
71
- unit_names = dependant_units_for(unit_names)
72
- exec_on_dependant_units_for(unit_names) do |unit|
73
- Actions::Build.new(unit, forcerm: forcerm).execute!
72
+ def build!(unit_names: [], usecache: true, forcerm: false)
73
+ unit_names = stack_units.keys if unit_names.empty?
74
+ unit_names = needed_units_for(unit_names)
75
+ exec_on_needed_units_for(unit_names) do |unit|
76
+ Actions::Build.new(unit, usecache: usecache, forcerm: forcerm).execute!
74
77
  end
75
78
  nil
76
79
  end
77
80
 
78
- def create!(unit_names: [], start: false)
79
- execute!(Actions::Create, dependant_units_for(unit_names), start: start)
80
- end
81
-
82
- def recreate!(unit_names: [], start: false, force: false)
83
- execute!(Actions::Recreate, filter_unit_names(unit_names), start: start, force: force)
81
+ def create!(unit_names: [], build: true, start: false, deep: false, force: false)
82
+ opts = { build: build, start: start, force: force }
83
+ unit_names = if deep
84
+ unit_names = stack_units.keys if unit_names.empty?
85
+ needed_units_for(unit_names)
86
+ else
87
+ filter_unit_names(unit_names)
88
+ end
89
+ execute!(Actions::Create, unit_names, opts)
84
90
  end
85
91
 
86
92
  def purge!(unit_names: [], force: false)
93
+ unit_names = stack_units.keys if unit_names.empty?
87
94
  execute!(Actions::Purge, filter_unit_names(unit_names).reverse, force: force)
88
95
  end
89
96
 
90
97
  def execute!(klass, unit_names, args=nil)
91
- exec_on_dependant_units_for(unit_names) do |unit|
92
- if args
93
- klass.new(unit, args).execute!
94
- else
95
- klass.new(unit).execute!
96
- end
98
+ exec_on_needed_units_for(unit_names) do |unit|
99
+ args ? klass.new(unit, args).execute! : klass.new(unit).execute!
97
100
  end
98
101
  nil
99
102
  end
@@ -110,20 +113,20 @@ module Percheron
110
113
  @stack_config ||= (config.stacks[stack_name] || Hashie::Mash.new({}))
111
114
  end
112
115
 
113
- # FIXME: yuck
114
- # rubocop:disable Style/Next
116
+ def stack_units
117
+ @stack_units ||= stack_config.fetch('units', {})
118
+ end
119
+
115
120
  def filter_unit_names(unit_names = [])
116
- stack_config.fetch('units', {}).map do |unit_name, unit_config|
117
- if unit_names.empty? || unit_names.include?(unit_name) ||
118
- (unit_config.pseudo_name &&
119
- unit_names.include?(unit_config.pseudo_name))
121
+ stack_units.map do |unit_name, unit_config|
122
+ if unit_names.include?(unit_name) ||
123
+ (unit_config.pseudo_name && unit_names.include?(unit_config.pseudo_name))
120
124
  unit_config.name
121
125
  end
122
- end.compact
126
+ end.compact.uniq
123
127
  end
124
- # rubocop:enable Style/Next
125
128
 
126
- def exec_on_dependant_units_for(unit_names)
129
+ def exec_on_needed_units_for(unit_names)
127
130
  exec_on_units(unit_names) do |unit|
128
131
  $logger.debug "Processing '#{unit.display_name}' unit"
129
132
  yield(unit)
@@ -135,21 +138,21 @@ module Percheron
135
138
  units(unit_names).each { |_, unit| yield(unit) }
136
139
  end
137
140
 
138
- def dependant_units_for(unit_names)
141
+ def needed_units_for(unit_names)
139
142
  list = []
140
143
  unit_names = filter_unit_names(unit_names)
141
- units = all_units_and_dependants(unit_names)
142
- units.each do |unit_name, dependant_unit_names|
143
- list += dependant_unit_names unless dependant_unit_names.empty?
144
+ units = all_units_and_neededs(unit_names)
145
+ units.each do |unit_name, needed_unit_names|
146
+ list += needed_unit_names unless needed_unit_names.empty?
144
147
  list << unit_name
145
148
  end
146
149
  list.uniq
147
150
  end
148
151
 
149
- def all_units_and_dependants(unit_names)
152
+ def all_units_and_neededs(unit_names)
150
153
  all_units = units
151
154
  units = unit_names.each_with_object({}) do |unit_name, all|
152
- all[unit_name] = all_units[unit_name].dependant_unit_names
155
+ all[unit_name] = all_units[unit_name].needed_unit_names
153
156
  end
154
157
  units.sort { |x, y| x[1].length <=> y[1].length } # FIXME
155
158
  end
@@ -0,0 +1,33 @@
1
+ module Percheron
2
+ class Unit
3
+ module ImageHelper
4
+ def image_name
5
+ '%s:%s' % [ image_repo, image_version.to_s ] if image_repo && image_version
6
+ end
7
+
8
+ def image_repo
9
+ if !buildable?
10
+ unit_config.docker_image.split(':')[0]
11
+ elsif pseudo?
12
+ pseudo_full_name
13
+ else
14
+ full_name
15
+ end
16
+ end
17
+
18
+ def image_version
19
+ if buildable?
20
+ unit_config.version
21
+ elsif !unit_config.docker_image.nil?
22
+ unit_config.docker_image.split(':')[1] || 'latest'
23
+ else
24
+ fail Errors::UnitInvalid, 'Cannot determine image version'
25
+ end
26
+ end
27
+
28
+ def image_exists?
29
+ image.nil? ? false : true
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,13 +1,15 @@
1
+ require 'percheron/unit/image_helper'
2
+
1
3
  module Percheron
2
4
  class Unit
3
5
 
4
6
  extend Forwardable
5
7
  extend ConfigDelegator
8
+ include Unit::ImageHelper
6
9
 
7
10
  def_delegators :unit_config, :name, :pseudo_name, :docker_image
8
- def_config_item_with_default :unit_config, [], :env, :ports, :volumes,
9
- :dependant_unit_names, :pre_build_scripts,
10
- :post_start_scripts, :start_args, :dns
11
+ def_config_item_with_default :unit_config, [], :env, :ports, :volumes, :needed_unit_names,
12
+ :pre_build_scripts, :post_start_scripts, :start_args, :dns
11
13
  def_config_item_with_default :unit_config, true, :startable
12
14
 
13
15
  def initialize(config, stack, unit_name)
@@ -18,14 +20,14 @@ module Percheron
18
20
  self
19
21
  end
20
22
 
21
- def dependant_units
22
- dependant_unit_names.each_with_object({}) do |unit_name, all|
23
+ def needed_units
24
+ needed_unit_names.each_with_object({}) do |unit_name, all|
23
25
  all[unit_name] = stack.units[unit_name]
24
26
  end
25
27
  end
26
28
 
27
- def startable_dependant_units
28
- dependant_units.select { |_, unit| unit.startable? }
29
+ def startable_needed_units
30
+ needed_units.select { |_, unit| unit.startable? }
29
31
  end
30
32
 
31
33
  def metastore_key
@@ -37,31 +39,19 @@ module Percheron
37
39
  end
38
40
 
39
41
  def hostname
40
- unit_config.fetch('hostname', name)
41
- end
42
-
43
- def image_name
44
- '%s:%s' % [ image_repo, image_version.to_s ] if image_repo && image_version
42
+ unit_config.fetch('hostname', full_name)
45
43
  end
46
44
 
47
- def image_repo
48
- if !buildable?
49
- unit_config.docker_image.split(':')[0]
50
- elsif pseudo?
51
- pseudo_full_name
52
- else
53
- full_name
45
+ def restart_policy
46
+ @restart_policy ||= begin
47
+ name = unit_config.fetch('restart_policy', 'always')
48
+ max_retry_count = unit_config.fetch('restart_policy_retry_count', 0)
49
+ { 'Name' => name, 'MaximumRetryCount' => max_retry_count }
54
50
  end
55
51
  end
56
52
 
57
- def image_version
58
- if buildable?
59
- unit_config.version
60
- elsif !unit_config.docker_image.nil?
61
- unit_config.docker_image.split(':')[1] || 'latest'
62
- else
63
- fail Errors::UnitInvalid, 'Cannot determine image version'
64
- end
53
+ def privileged
54
+ unit_config.fetch('privileged', false)
65
55
  end
66
56
 
67
57
  def full_name
@@ -95,9 +85,7 @@ module Percheron
95
85
  end
96
86
 
97
87
  def links
98
- startable_dependant_units.map do |_, unit|
99
- '%s:%s' % [ unit.full_name, unit.full_name ]
100
- end
88
+ startable_needed_units.map { |_, unit| '%s:%s' % [ unit.full_name, unit.full_name ] }
101
89
  end
102
90
 
103
91
  def container
@@ -141,10 +129,6 @@ module Percheron
141
129
  !info.empty?
142
130
  end
143
131
 
144
- def image_exists?
145
- image.nil? ? false : true
146
- end
147
-
148
132
  def buildable?
149
133
  !dockerfile.nil? && unit_config.docker_image.nil?
150
134
  end
@@ -178,6 +162,5 @@ module Percheron
178
162
  def info
179
163
  Hashie::Mash.new(container.info)
180
164
  end
181
-
182
165
  end
183
166
  end
@@ -1,3 +1,3 @@
1
1
  module Percheron
2
- VERSION = '0.7.16'
2
+ VERSION = '0.8.0'
3
3
  end
data/lib/percheron.rb CHANGED
@@ -7,6 +7,7 @@ require 'semantic'
7
7
  require 'metastore'
8
8
  require 'liquid'
9
9
  require 'singleton'
10
+ require 'securerandom'
10
11
 
11
12
  require 'percheron/oh_dear'
12
13
  require 'percheron/core_extensions'
data/percheron.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency 'pry-byebug', '~> 3.2.0'
37
37
  spec.add_development_dependency 'rake', '~> 10.4.0'
38
38
  spec.add_development_dependency 'rspec', '~> 3.3.0'
39
- spec.add_development_dependency 'rubocop', '~> 0.32.0'
39
+ spec.add_development_dependency 'rubocop', '~> 0.33.0'
40
40
  spec.add_development_dependency 'simplecov', '~> 0.10.0'
41
41
  spec.add_development_dependency 'timecop', '~> 0.8.0'
42
42
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: percheron
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.16
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ash McKenzie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-07 00:00:00.000000000 Z
11
+ date: 2015-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: clamp
@@ -254,14 +254,14 @@ dependencies:
254
254
  requirements:
255
255
  - - "~>"
256
256
  - !ruby/object:Gem::Version
257
- version: 0.32.0
257
+ version: 0.33.0
258
258
  type: :development
259
259
  prerelease: false
260
260
  version_requirements: !ruby/object:Gem::Requirement
261
261
  requirements:
262
262
  - - "~>"
263
263
  - !ruby/object:Gem::Version
264
- version: 0.32.0
264
+ version: 0.33.0
265
265
  - !ruby/object:Gem::Dependency
266
266
  name: simplecov
267
267
  requirement: !ruby/object:Gem::Requirement
@@ -322,7 +322,6 @@ files:
322
322
  - lib/percheron/actions/exec_local.rb
323
323
  - lib/percheron/actions/logs.rb
324
324
  - lib/percheron/actions/purge.rb
325
- - lib/percheron/actions/recreate.rb
326
325
  - lib/percheron/actions/restart.rb
327
326
  - lib/percheron/actions/shell.rb
328
327
  - lib/percheron/actions/start.rb
@@ -337,7 +336,6 @@ files:
337
336
  - lib/percheron/commands/logs.rb
338
337
  - lib/percheron/commands/main.rb
339
338
  - lib/percheron/commands/purge.rb
340
- - lib/percheron/commands/recreate.rb
341
339
  - lib/percheron/commands/restart.rb
342
340
  - lib/percheron/commands/shell.rb
343
341
  - lib/percheron/commands/start.rb
@@ -358,6 +356,7 @@ files:
358
356
  - lib/percheron/oh_dear.rb
359
357
  - lib/percheron/stack.rb
360
358
  - lib/percheron/unit.rb
359
+ - lib/percheron/unit/image_helper.rb
361
360
  - lib/percheron/validators.rb
362
361
  - lib/percheron/validators/config.rb
363
362
  - lib/percheron/validators/docker_client.rb
@@ -1,51 +0,0 @@
1
- module Percheron
2
- module Actions
3
- class Recreate
4
-
5
- include Base
6
-
7
- def initialize(unit, start: false, force: false)
8
- @unit = unit
9
- @start = start
10
- @force = force
11
- end
12
-
13
- def execute!
14
- results = []
15
- if recreate? || force?
16
- results << recreate!
17
- results << start!
18
- else
19
- inform!
20
- end
21
- results.compact.empty? ? nil : unit
22
- end
23
-
24
- private
25
-
26
- attr_reader :unit, :start, :force
27
- alias_method :start?, :start
28
- alias_method :force?, :force
29
-
30
- def recreate?
31
- !unit.versions_match? || !unit.dockerfile_md5s_match?
32
- end
33
-
34
- def inform!
35
- return nil unless unit.dockerfile_md5s_match?
36
- $logger.info "Unit '#{unit.display_name}' - No Dockerfile changes or version bump"
37
- end
38
-
39
- def recreate!
40
- $logger.debug "Unit '#{unit.display_name}' exists but will be recreated"
41
- Purge.new(unit).execute!
42
- Create.new(unit).execute!
43
- end
44
-
45
- def start!
46
- Start.new(unit).execute! if start?
47
- end
48
-
49
- end
50
- end
51
- end
@@ -1,14 +0,0 @@
1
- module Percheron
2
- module Commands
3
- class Recreate < Abstract
4
-
5
- default_create_parameters!
6
- option([ '-f', '--force' ], :flag, 'Force purge and create', default: false)
7
-
8
- def execute
9
- super
10
- stack.recreate!(unit_names: unit_names, start: start?, force: force?)
11
- end
12
- end
13
- end
14
- end