percheron 0.5.0 → 0.6.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.
- 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
@@ -1,56 +0,0 @@
|
|
1
|
-
module Percheron
|
2
|
-
module Container
|
3
|
-
module Actions
|
4
|
-
class Create
|
5
|
-
|
6
|
-
include Base
|
7
|
-
|
8
|
-
def initialize(container)
|
9
|
-
@container = container
|
10
|
-
end
|
11
|
-
|
12
|
-
def execute!
|
13
|
-
build!
|
14
|
-
insert_post_start_scripts!
|
15
|
-
create!
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
attr_reader :container
|
21
|
-
|
22
|
-
def create_opts
|
23
|
-
{
|
24
|
-
'name' => container.name,
|
25
|
-
'Image' => container.image_name,
|
26
|
-
'Hostname' => container.name,
|
27
|
-
'Env' => container.env,
|
28
|
-
'ExposedPorts' => container.exposed_ports,
|
29
|
-
'VolumesFrom' => container.volumes
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
|
-
def build!
|
34
|
-
unless container.image
|
35
|
-
$logger.debug "Creating '#{container.image_name}' image"
|
36
|
-
Container::Actions::Build.new(container).execute!
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def create!
|
41
|
-
$logger.debug "Creating '#{container.name}' container"
|
42
|
-
Docker::Container.create(create_opts)
|
43
|
-
end
|
44
|
-
|
45
|
-
def insert_post_start_scripts!
|
46
|
-
container.post_start_scripts.each do |script|
|
47
|
-
file = Pathname.new(File.expand_path(script, base_dir))
|
48
|
-
new_image = container.image.insert_local('localPath' => file.to_s, 'outputPath' => "/tmp/#{file.basename}")
|
49
|
-
new_image.tag(repo: container.name, tag: container.version.to_s, force: true)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
module Percheron
|
2
|
-
module Container
|
3
|
-
module Actions
|
4
|
-
class Recreate
|
5
|
-
|
6
|
-
include Base
|
7
|
-
|
8
|
-
def initialize(container)
|
9
|
-
@container = container
|
10
|
-
end
|
11
|
-
|
12
|
-
def execute!
|
13
|
-
unless temporary_container_exists?
|
14
|
-
create_image!
|
15
|
-
create_container!
|
16
|
-
rename!
|
17
|
-
else
|
18
|
-
$logger.warn "Not recreating '#{container.name}' container as '#{temporary_name}' because '#{temporary_name}' already exists"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
attr_reader :container
|
25
|
-
|
26
|
-
def create_opts
|
27
|
-
{
|
28
|
-
'name' => temporary_name,
|
29
|
-
'Image' => container.image_name,
|
30
|
-
'Hostname' => container.name,
|
31
|
-
'Env' => container.env,
|
32
|
-
'ExposedPorts' => container.exposed_ports,
|
33
|
-
'VolumesFrom' => container.volumes
|
34
|
-
}
|
35
|
-
end
|
36
|
-
|
37
|
-
def temporary_name
|
38
|
-
'%s_wip' % container.name
|
39
|
-
end
|
40
|
-
|
41
|
-
# FIXME: brittle
|
42
|
-
def temporary_container_exists?
|
43
|
-
temporary_container.info.empty?
|
44
|
-
rescue Docker::Error::NotFoundError
|
45
|
-
false
|
46
|
-
end
|
47
|
-
|
48
|
-
def create_image!
|
49
|
-
unless container.image
|
50
|
-
$logger.debug "Creating '#{container.image_name}' image"
|
51
|
-
Container::Actions::Build.new(container).execute!
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def create_container!
|
56
|
-
$logger.debug "Recreating '#{container.name}' container as '#{temporary_name}'"
|
57
|
-
Docker::Container.create(create_opts)
|
58
|
-
end
|
59
|
-
|
60
|
-
def rename!
|
61
|
-
save_current_running_state!
|
62
|
-
stop_current! if container.running?
|
63
|
-
rename_current_to_old!
|
64
|
-
rename_wip_to_current!
|
65
|
-
start_new! if container_was_running?
|
66
|
-
end
|
67
|
-
|
68
|
-
def rename_current_new_name
|
69
|
-
'%s_%s' % [ container.name, now_timestamp ]
|
70
|
-
end
|
71
|
-
|
72
|
-
def now_timestamp
|
73
|
-
Time.now.strftime('%Y%m%d%H%M%S')
|
74
|
-
end
|
75
|
-
|
76
|
-
def temporary_container
|
77
|
-
Docker::Container.get(temporary_name)
|
78
|
-
end
|
79
|
-
|
80
|
-
def save_current_running_state!
|
81
|
-
@container_was_running = container.running?
|
82
|
-
end
|
83
|
-
|
84
|
-
def stop_current!
|
85
|
-
Container::Actions::Stop.new(container).execute!
|
86
|
-
end
|
87
|
-
|
88
|
-
def rename_current_to_old!
|
89
|
-
$logger.debug "Renaming '#{container.name}' container to '#{rename_current_new_name}'"
|
90
|
-
container.docker_container.rename(rename_current_new_name)
|
91
|
-
end
|
92
|
-
|
93
|
-
def rename_wip_to_current!
|
94
|
-
# FIXME: brittle
|
95
|
-
$logger.debug "Renaming '#{temporary_name}' container to '#{container.name}'"
|
96
|
-
Docker::Container.get(temporary_name).rename(container.name)
|
97
|
-
end
|
98
|
-
|
99
|
-
def start_new!
|
100
|
-
container.start!
|
101
|
-
end
|
102
|
-
|
103
|
-
def container_was_running?
|
104
|
-
@container_was_running
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
module Percheron
|
2
|
-
module Container
|
3
|
-
module Actions
|
4
|
-
class Start
|
5
|
-
|
6
|
-
include Base
|
7
|
-
|
8
|
-
def initialize(container)
|
9
|
-
@container = container
|
10
|
-
end
|
11
|
-
|
12
|
-
def execute!
|
13
|
-
if container.exists?
|
14
|
-
start!
|
15
|
-
execute_post_start_scripts!
|
16
|
-
else
|
17
|
-
raise Errors::ContainerDoesNotExist.new
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
attr_reader :container
|
24
|
-
|
25
|
-
def start_opts
|
26
|
-
opts = container.ports.inject({}) do |all, p|
|
27
|
-
destination, source = p.split(':')
|
28
|
-
all[source] = [ { 'HostPort' => destination } ]
|
29
|
-
all
|
30
|
-
end
|
31
|
-
|
32
|
-
{
|
33
|
-
'PortBindings' => opts,
|
34
|
-
'Links' => container.links,
|
35
|
-
'Binds' => container.volumes
|
36
|
-
}
|
37
|
-
end
|
38
|
-
|
39
|
-
def start!
|
40
|
-
$logger.debug "Starting '#{container.name}'"
|
41
|
-
container.docker_container.start!(start_opts)
|
42
|
-
end
|
43
|
-
|
44
|
-
def execute_post_start_scripts!
|
45
|
-
container.post_start_scripts.each do |script|
|
46
|
-
in_working_directory(base_dir) do
|
47
|
-
file = Pathname.new(File.expand_path(script, base_dir))
|
48
|
-
command = '/bin/bash -x /tmp/%s 2>&1' % file.basename
|
49
|
-
$logger.debug "Executing POST create '#{command}' for '#{container.name}' container"
|
50
|
-
container.docker_container.exec(command.split(' '))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module Percheron
|
2
|
-
module Container
|
3
|
-
module Actions
|
4
|
-
class Stop
|
5
|
-
|
6
|
-
include Base
|
7
|
-
|
8
|
-
def initialize(container)
|
9
|
-
@container = container
|
10
|
-
end
|
11
|
-
|
12
|
-
def execute!
|
13
|
-
stop!
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
attr_reader :container
|
19
|
-
|
20
|
-
def stop!
|
21
|
-
if container.running?
|
22
|
-
$logger.debug "Stopping '#{container.name}'"
|
23
|
-
container.docker_container.stop!
|
24
|
-
else
|
25
|
-
$logger.debug "Not stopping '#{container.name}' container as it's not running"
|
26
|
-
raise Errors::ContainerNotRunning.new
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,193 +0,0 @@
|
|
1
|
-
module Percheron
|
2
|
-
module Container
|
3
|
-
class Main
|
4
|
-
|
5
|
-
extend Forwardable
|
6
|
-
extend ConfigDelegator
|
7
|
-
|
8
|
-
def_delegators :container_config, :name
|
9
|
-
|
10
|
-
def_config_item_with_default :container_config, false, :auto_recreate
|
11
|
-
def_config_item_with_default :container_config, [], :env, :ports, :volumes, :dependant_container_names, :pre_build_scripts, :post_start_scripts
|
12
|
-
|
13
|
-
alias_method :auto_recreate?, :auto_recreate
|
14
|
-
|
15
|
-
def initialize(config, stack, container_name)
|
16
|
-
@config = config
|
17
|
-
@stack = stack
|
18
|
-
@container_name = container_name
|
19
|
-
valid?
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
def id
|
24
|
-
exists? ? info.id[0...12] : 'N/A'
|
25
|
-
end
|
26
|
-
|
27
|
-
def image
|
28
|
-
Docker::Image.get(image_name)
|
29
|
-
rescue Docker::Error::NotFoundError
|
30
|
-
nil
|
31
|
-
end
|
32
|
-
|
33
|
-
def image_name
|
34
|
-
'%s:%s' % [ name, version.to_s ]
|
35
|
-
end
|
36
|
-
|
37
|
-
def version
|
38
|
-
Semantic::Version.new(container_config.version)
|
39
|
-
end
|
40
|
-
|
41
|
-
def built_version
|
42
|
-
exists? ? Semantic::Version.new(built_image_version) : nil
|
43
|
-
end
|
44
|
-
|
45
|
-
def dockerfile
|
46
|
-
container_config.dockerfile ? Pathname.new(File.expand_path(container_config.dockerfile, config.file_base_path)): nil
|
47
|
-
end
|
48
|
-
|
49
|
-
def exposed_ports
|
50
|
-
ports.inject({}) do |all, p|
|
51
|
-
all[p.split(':')[1]] = {}
|
52
|
-
all
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def links
|
57
|
-
dependant_container_names.map do |container_name|
|
58
|
-
'%s:%s' % [ container_name, container_name ]
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def docker_container
|
63
|
-
Docker::Container.get(name)
|
64
|
-
rescue Docker::Error::NotFoundError, Excon::Errors::SocketError
|
65
|
-
Container::Null.new
|
66
|
-
end
|
67
|
-
|
68
|
-
def stop!
|
69
|
-
Container::Actions::Stop.new(self).execute!
|
70
|
-
rescue Errors::ContainerNotRunning
|
71
|
-
$logger.debug "Container '#{name}' is not running"
|
72
|
-
end
|
73
|
-
|
74
|
-
def start!
|
75
|
-
start_dependant_containers!
|
76
|
-
create!
|
77
|
-
recreate!
|
78
|
-
Container::Actions::Start.new(self).execute!
|
79
|
-
end
|
80
|
-
|
81
|
-
def restart!
|
82
|
-
stop!
|
83
|
-
start!
|
84
|
-
end
|
85
|
-
|
86
|
-
def create!
|
87
|
-
unless exists?
|
88
|
-
$logger.debug "Container '#{name}' does not exist, creating"
|
89
|
-
Container::Actions::Create.new(self).execute!
|
90
|
-
set_dockerfile_md5!
|
91
|
-
else
|
92
|
-
$logger.debug "Not creating '#{name}' container as it already exists"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def recreate!(force_recreate: false, force_auto_recreate: false)
|
97
|
-
if exists?
|
98
|
-
if recreate?(force_recreate: force_recreate, force_auto_recreate: force_auto_recreate)
|
99
|
-
$logger.warn "Container '#{name}' exists and will be recreated"
|
100
|
-
Container::Actions::Recreate.new(self).execute!
|
101
|
-
set_dockerfile_md5!
|
102
|
-
else
|
103
|
-
if recreatable?
|
104
|
-
$logger.warn "Container '#{name}' MD5's do not match, consider recreating"
|
105
|
-
else
|
106
|
-
$logger.debug "Container '#{name}' does not need to be recreated"
|
107
|
-
end
|
108
|
-
end
|
109
|
-
else
|
110
|
-
$logger.warn "Not recreating '#{name}' container as it does not exist"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def recreatable?
|
115
|
-
!dockerfile_md5s_match?
|
116
|
-
end
|
117
|
-
|
118
|
-
def recreate?(force_recreate: false, force_auto_recreate: false)
|
119
|
-
(force_recreate || (recreatable? && versions_mismatch?)) && (force_auto_recreate || auto_recreate?)
|
120
|
-
end
|
121
|
-
|
122
|
-
def running?
|
123
|
-
exists? && info.State.Running
|
124
|
-
end
|
125
|
-
|
126
|
-
def exists?
|
127
|
-
!info.empty?
|
128
|
-
end
|
129
|
-
|
130
|
-
def valid?
|
131
|
-
Validators::Container.new(self).valid?
|
132
|
-
end
|
133
|
-
|
134
|
-
private
|
135
|
-
|
136
|
-
attr_reader :config, :stack, :container_name
|
137
|
-
|
138
|
-
def dockerfile_md5s_match?
|
139
|
-
stored_dockerfile_md5 == current_dockerfile_md5
|
140
|
-
end
|
141
|
-
|
142
|
-
def versions_mismatch?
|
143
|
-
version > built_version
|
144
|
-
end
|
145
|
-
|
146
|
-
def built_image_version
|
147
|
-
info.Config.Image.split(':')[1]
|
148
|
-
end
|
149
|
-
|
150
|
-
def stored_dockerfile_md5
|
151
|
-
dockerfile_md5 || current_dockerfile_md5
|
152
|
-
end
|
153
|
-
|
154
|
-
def metastore_key
|
155
|
-
@metastore_key ||= 'stacks.%s.containers.%s' % [ stack.name, name ]
|
156
|
-
end
|
157
|
-
|
158
|
-
def dockerfile_md5
|
159
|
-
$metastore.get("#{metastore_key}.dockerfile_md5")
|
160
|
-
end
|
161
|
-
|
162
|
-
def set_dockerfile_md5!
|
163
|
-
$logger.debug "Setting MD5 for '#{name}' container to #{current_dockerfile_md5}"
|
164
|
-
$metastore.set("#{metastore_key}.dockerfile_md5", current_dockerfile_md5)
|
165
|
-
end
|
166
|
-
|
167
|
-
def current_dockerfile_md5
|
168
|
-
Digest::MD5.file(dockerfile).hexdigest
|
169
|
-
end
|
170
|
-
|
171
|
-
def info
|
172
|
-
Hashie::Mash.new(docker_container.info)
|
173
|
-
end
|
174
|
-
|
175
|
-
def container_config
|
176
|
-
@container_config ||= stack.container_configs[container_name] || Hashie::Mash.new({})
|
177
|
-
end
|
178
|
-
|
179
|
-
def dependant_containers
|
180
|
-
dependant_container_names.map { |container_name| stack.containers[container_name] }
|
181
|
-
end
|
182
|
-
|
183
|
-
def start_dependant_containers!
|
184
|
-
dependant_containers.each do |container|
|
185
|
-
next if container.running?
|
186
|
-
$logger.debug "Container '#{container.name}' being started as it's a dependancy"
|
187
|
-
container.start!
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|