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
@@ -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
|