percheron 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -3
- data/Guardfile +5 -0
- data/bin/percheron +8 -4
- data/lib/percheron.rb +3 -0
- data/lib/percheron/actions.rb +15 -0
- data/lib/percheron/actions/base.rb +50 -0
- data/lib/percheron/actions/build.rb +47 -0
- data/lib/percheron/actions/create.rb +109 -0
- data/lib/percheron/actions/exec.rb +51 -0
- data/lib/percheron/actions/purge.rb +38 -0
- data/lib/percheron/actions/recreate.rb +98 -0
- data/lib/percheron/actions/rename.rb +64 -0
- data/lib/percheron/actions/restart.rb +31 -0
- data/lib/percheron/actions/start.rb +46 -0
- data/lib/percheron/actions/stop.rb +27 -0
- data/lib/percheron/cli.rb +1 -0
- data/lib/percheron/cli/abstract_command.rb +7 -0
- data/lib/percheron/cli/create_command.rb +3 -2
- data/lib/percheron/cli/list_command.rb +2 -8
- data/lib/percheron/cli/main_command.rb +1 -0
- data/lib/percheron/cli/purge_command.rb +12 -0
- data/lib/percheron/cli/recreate_command.rb +9 -2
- data/lib/percheron/cli/restart_command.rb +3 -2
- data/lib/percheron/cli/start_command.rb +3 -2
- data/lib/percheron/cli/stop_command.rb +3 -2
- data/lib/percheron/container.rb +120 -5
- data/lib/percheron/errors.rb +0 -1
- data/lib/percheron/formatters/stack/table.rb +1 -1
- data/lib/percheron/null_container.rb +13 -0
- data/lib/percheron/oh_dear.rb +46 -0
- data/lib/percheron/stack.rb +90 -31
- data/lib/percheron/validators/container.rb +9 -1
- data/lib/percheron/version.rb +1 -1
- data/percheron.gemspec +2 -0
- metadata +45 -11
- data/lib/percheron/container/actions.rb +0 -15
- data/lib/percheron/container/actions/base.rb +0 -21
- data/lib/percheron/container/actions/build.rb +0 -58
- data/lib/percheron/container/actions/create.rb +0 -56
- data/lib/percheron/container/actions/recreate.rb +0 -110
- data/lib/percheron/container/actions/start.rb +0 -58
- data/lib/percheron/container/actions/stop.rb +0 -33
- data/lib/percheron/container/main.rb +0 -193
- data/lib/percheron/container/null.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57bb2c76f384e9d0ac932c72d1c607bac65f9bd8
|
4
|
+
data.tar.gz: 4cfa40df1e9fefad4238f6960e2d4570e59a5502
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d16f64f3aa1fb492bd825e92a9d622d812fe61da5d8d957a2dc8002a9d655d556f9df61efab2aa83a93a92bc3725353037b820e2d6f1991d5480785bae93c2c
|
7
|
+
data.tar.gz: 03d4ac7067cd65c0599850faa66af3e8a6f22ec7a7ef52f53bff795f8c0517a15a2ec79e4fce94338af9698800c6800c867b4bf2c6aaf657200089ed75781e18
|
data/Gemfile
CHANGED
@@ -3,10 +3,10 @@ source 'https://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
group :test do
|
6
|
-
gem 'codeclimate-test-reporter', '~> 0.1', require:
|
6
|
+
gem 'codeclimate-test-reporter', '~> 0.1', require: false
|
7
7
|
end
|
8
8
|
|
9
9
|
group :development do
|
10
|
-
gem 'pry-byebug', '~> 3.0'
|
11
|
-
gem 'awesome_print', '~> 1.0'
|
10
|
+
gem 'pry-byebug', '~> 3.0', require: false
|
11
|
+
gem 'awesome_print', '~> 1.0', require: false
|
12
12
|
end
|
data/Guardfile
ADDED
data/bin/percheron
CHANGED
@@ -10,10 +10,10 @@ require 'percheron/cli'
|
|
10
10
|
$metastore = Metastore::Cabinet.new(File.join(ENV['HOME'], '.percheron', 'metastore.yaml'))
|
11
11
|
$logger = Logger.new(STDOUT)
|
12
12
|
|
13
|
-
logger_level = Logger::
|
13
|
+
logger_level = Logger::INFO
|
14
14
|
|
15
|
-
if ENV['
|
16
|
-
logger_level = Logger::
|
15
|
+
if ENV['QUIET'] == 'true'
|
16
|
+
logger_level = Logger::WARN
|
17
17
|
end
|
18
18
|
|
19
19
|
if ENV['DEBUG'] == 'true' || ENV['DOCKER_DEBUG'] == 'true'
|
@@ -26,4 +26,8 @@ end
|
|
26
26
|
|
27
27
|
$logger.level = logger_level
|
28
28
|
|
29
|
-
|
29
|
+
begin
|
30
|
+
Percheron::CLI::MainCommand.run
|
31
|
+
rescue => e
|
32
|
+
puts Percheron::OhDear.new(e).generate
|
33
|
+
end
|
data/lib/percheron.rb
CHANGED
@@ -6,6 +6,7 @@ require 'naught'
|
|
6
6
|
require 'semantic'
|
7
7
|
require 'metastore'
|
8
8
|
|
9
|
+
require 'percheron/oh_dear'
|
9
10
|
require 'percheron/core_extensions'
|
10
11
|
require 'percheron/version'
|
11
12
|
require 'percheron/config'
|
@@ -15,6 +16,8 @@ require 'percheron/formatters'
|
|
15
16
|
require 'percheron/validators'
|
16
17
|
require 'percheron/stack'
|
17
18
|
require 'percheron/container'
|
19
|
+
require 'percheron/null_container'
|
20
|
+
require 'percheron/actions'
|
18
21
|
require 'percheron/docker_connection'
|
19
22
|
|
20
23
|
module Percheron
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'percheron/actions/base'
|
2
|
+
require 'percheron/actions/stop'
|
3
|
+
require 'percheron/actions/start'
|
4
|
+
require 'percheron/actions/restart'
|
5
|
+
require 'percheron/actions/create'
|
6
|
+
require 'percheron/actions/recreate'
|
7
|
+
require 'percheron/actions/rename'
|
8
|
+
require 'percheron/actions/build'
|
9
|
+
require 'percheron/actions/purge'
|
10
|
+
require 'percheron/actions/exec'
|
11
|
+
|
12
|
+
module Percheron
|
13
|
+
module Actions
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Actions
|
3
|
+
module Base
|
4
|
+
|
5
|
+
def base_dir
|
6
|
+
container.dockerfile.dirname.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def in_working_directory(new_dir)
|
10
|
+
old_dir = Dir.pwd
|
11
|
+
Dir.chdir(new_dir)
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
Dir.chdir(old_dir)
|
15
|
+
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
|
+
Stop.new(container).execute! if container.running?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_containers!(containers)
|
37
|
+
exec_on_containers!(containers) do |container|
|
38
|
+
Start.new(container, container.dependant_containers.values).execute! unless container.running?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def exec_on_containers!(containers)
|
43
|
+
containers.inject([]) do |all, container|
|
44
|
+
all << container if yield(container)
|
45
|
+
all
|
46
|
+
end.compact
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Actions
|
3
|
+
class Build
|
4
|
+
|
5
|
+
include Base
|
6
|
+
|
7
|
+
def initialize(container, nocache: false)
|
8
|
+
@container = container
|
9
|
+
@nocache = nocache
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute!
|
13
|
+
build!
|
14
|
+
container
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :container, :nocache
|
20
|
+
|
21
|
+
def build_opts
|
22
|
+
{
|
23
|
+
'dockerfile' => container.dockerfile.basename.to_s,
|
24
|
+
't' => container.image_name,
|
25
|
+
'forcerm' => true,
|
26
|
+
'nocache' => nocache
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def build!
|
31
|
+
in_working_directory(base_dir) do
|
32
|
+
execute_pre_build_scripts!
|
33
|
+
|
34
|
+
$logger.info "Building '#{container.image_name}'"
|
35
|
+
Docker::Image.build_from_dir(base_dir, build_opts) do |out|
|
36
|
+
$logger.debug '%s' % [ out.strip ]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def execute_pre_build_scripts!
|
42
|
+
Exec.new(container, container.dependant_containers.values, container.pre_build_scripts, 'PRE build').execute!
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Actions
|
3
|
+
class Create
|
4
|
+
|
5
|
+
include Base
|
6
|
+
|
7
|
+
def initialize(container, recreate: false)
|
8
|
+
@container = container
|
9
|
+
@recreate = recreate
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute!(opts={})
|
13
|
+
if recreate? || !container.exists?
|
14
|
+
create!(opts)
|
15
|
+
container
|
16
|
+
else
|
17
|
+
$logger.debug "Container '#{container.name}' already exists"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :container, :recreate
|
24
|
+
|
25
|
+
def base_options
|
26
|
+
{
|
27
|
+
'name' => container.name,
|
28
|
+
'Image' => container.image_name,
|
29
|
+
'Hostname' => container.name,
|
30
|
+
'Env' => container.env,
|
31
|
+
'ExposedPorts' => container.exposed_ports,
|
32
|
+
'HostConfig' => {
|
33
|
+
'PortBindings' => port_bindings,
|
34
|
+
'Links' => container.links,
|
35
|
+
'Binds' => container.volumes
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def host_config_options
|
41
|
+
{
|
42
|
+
'HostConfig' => {
|
43
|
+
'PortBindings' => port_bindings,
|
44
|
+
'Links' => container.links,
|
45
|
+
'Binds' => container.volumes
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def port_bindings
|
51
|
+
container.ports.inject({}) do |all, p|
|
52
|
+
destination, source = p.split(':')
|
53
|
+
all[source] = [ { 'HostPort' => destination } ]
|
54
|
+
all
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def recreate?
|
59
|
+
recreate
|
60
|
+
end
|
61
|
+
|
62
|
+
def create!(opts)
|
63
|
+
$logger.debug "Container '#{container.name}' does not exist, creating"
|
64
|
+
build_image!
|
65
|
+
insert_scripts!
|
66
|
+
create_container!(opts.fetch(:create, {}))
|
67
|
+
execute_post_create_scripts!
|
68
|
+
set_dockerfile_md5!
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_image!
|
72
|
+
unless container.image_exists?
|
73
|
+
$logger.info "Creating '#{container.image_name}' image"
|
74
|
+
Build.new(container).execute!
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_dockerfile_md5!
|
79
|
+
$logger.info "Setting MD5 for '#{container.name}' container to #{container.current_dockerfile_md5}"
|
80
|
+
$metastore.set("#{container.metastore_key}.dockerfile_md5", container.current_dockerfile_md5)
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_container!(opts)
|
84
|
+
options = base_options.merge(host_config_options).merge(opts)
|
85
|
+
|
86
|
+
$logger.info "Creating '%s' container" % options['name']
|
87
|
+
Docker::Container.create(options)
|
88
|
+
end
|
89
|
+
|
90
|
+
def insert_scripts!
|
91
|
+
insert_post_create_scripts!
|
92
|
+
insert_post_start_scripts!
|
93
|
+
end
|
94
|
+
|
95
|
+
def insert_post_create_scripts!
|
96
|
+
insert_files!(container.post_create_scripts)
|
97
|
+
end
|
98
|
+
|
99
|
+
def insert_post_start_scripts!
|
100
|
+
insert_files!(container.post_start_scripts)
|
101
|
+
end
|
102
|
+
|
103
|
+
def execute_post_create_scripts!
|
104
|
+
Exec.new(container, container.dependant_containers.values, container.post_create_scripts, 'POST create').execute!
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Actions
|
3
|
+
class Exec
|
4
|
+
|
5
|
+
include Base
|
6
|
+
|
7
|
+
def initialize(container, dependant_containers, scripts, description)
|
8
|
+
@container = container
|
9
|
+
@dependant_containers = dependant_containers
|
10
|
+
@scripts = scripts
|
11
|
+
@description = description
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute!
|
15
|
+
$logger.debug "Executing #{description} scripts '#{scripts.inspect}' on '#{container.name}'"
|
16
|
+
started_dependant_containers = start_containers!(dependant_containers)
|
17
|
+
execute_scripts_on_running_container!
|
18
|
+
stop_containers!(started_dependant_containers)
|
19
|
+
container
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :container, :dependant_containers, :scripts, :description
|
25
|
+
|
26
|
+
def execute_scripts_on_running_container!
|
27
|
+
container_running = container.running?
|
28
|
+
Start.new(container).execute! unless container_running
|
29
|
+
execute_scripts!
|
30
|
+
Stop.new(container).execute! unless container_running
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute_scripts!
|
34
|
+
scripts.each do |script|
|
35
|
+
in_working_directory(base_dir) do
|
36
|
+
file = Pathname.new(File.expand_path(script, base_dir))
|
37
|
+
execute_command!('/bin/bash -x /tmp/%s 2>&1' % file.basename)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def execute_command!(command)
|
43
|
+
$logger.info "Executing #{description} '#{command}' for '#{container.name}' container"
|
44
|
+
container.docker_container.exec(command.split(' ')) do |device, out|
|
45
|
+
$logger.debug '%s: %s' % [ device, out.strip ]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Actions
|
3
|
+
class Purge
|
4
|
+
|
5
|
+
include Base
|
6
|
+
|
7
|
+
def initialize(container)
|
8
|
+
@container = container
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute!
|
12
|
+
stop!
|
13
|
+
delete_container! if container.exists?
|
14
|
+
delete_image! if container.image_exists?
|
15
|
+
container
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :container
|
21
|
+
|
22
|
+
def stop!
|
23
|
+
Stop.new(container).execute!
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete_container!
|
27
|
+
$logger.info "Deleting '#{container.name}' container"
|
28
|
+
container.docker_container.remove
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete_image!
|
32
|
+
$logger.info "Deleting '#{container.image_name}' image"
|
33
|
+
container.image.remove
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Actions
|
3
|
+
class Recreate
|
4
|
+
|
5
|
+
include Base
|
6
|
+
|
7
|
+
def initialize(container, force_recreate: false, delete: false)
|
8
|
+
@container = container
|
9
|
+
@force_recreate = force_recreate
|
10
|
+
@delete = delete
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute!
|
14
|
+
if recreate?
|
15
|
+
recreate!
|
16
|
+
container
|
17
|
+
else
|
18
|
+
unless dockerfile_md5s_match?
|
19
|
+
$logger.warn "Container '#{container.name}' MD5's do not match, consider recreating (bump the version!)"
|
20
|
+
else
|
21
|
+
$logger.debug "Container '#{container.name}' does not need to be recreated"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :container, :force_recreate, :delete
|
29
|
+
|
30
|
+
alias_method :force_recreate?, :force_recreate
|
31
|
+
alias_method :delete?, :delete
|
32
|
+
|
33
|
+
def temporary_name
|
34
|
+
'%s_wip' % container.name
|
35
|
+
end
|
36
|
+
|
37
|
+
def stored_dockerfile_md5
|
38
|
+
container.dockerfile_md5 || container.current_dockerfile_md5
|
39
|
+
end
|
40
|
+
|
41
|
+
def temporary_container_exists?
|
42
|
+
!!Docker::Container.get(temporary_name)
|
43
|
+
rescue Docker::Error::NotFoundError
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def recreate?
|
48
|
+
force_recreate? || (!dockerfile_md5s_match? && versions_mismatch? && container.auto_recreate?)
|
49
|
+
end
|
50
|
+
|
51
|
+
def versions_mismatch?
|
52
|
+
container.version > container.built_version
|
53
|
+
end
|
54
|
+
|
55
|
+
def dockerfile_md5s_match?
|
56
|
+
stored_dockerfile_md5 == container.current_dockerfile_md5
|
57
|
+
end
|
58
|
+
|
59
|
+
def recreate!
|
60
|
+
$logger.debug "Container '#{container.name}' exists and will be recreated"
|
61
|
+
|
62
|
+
unless temporary_container_exists?
|
63
|
+
delete_container_and_image! if delete?
|
64
|
+
create_container!
|
65
|
+
rename_container!
|
66
|
+
else
|
67
|
+
$logger.debug "Not recreating '#{container.name}' container because temporary container '#{temporary_name}' already exists"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_container_and_image!
|
72
|
+
delete_container!
|
73
|
+
delete_image!
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete_container!
|
77
|
+
$logger.info "Deleting '#{container.name}' container"
|
78
|
+
stop_containers!([ container ])
|
79
|
+
container.docker_container.remove
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete_image!
|
83
|
+
$logger.info "Deleting '#{container.name}' image"
|
84
|
+
container.image.remove
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_container!
|
88
|
+
opts = { create: { 'name' => temporary_name } }
|
89
|
+
Create.new(container, recreate: true).execute!(opts)
|
90
|
+
end
|
91
|
+
|
92
|
+
def rename_container!
|
93
|
+
Rename.new(container, temporary_name, container.name).execute!
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|