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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -3
  3. data/Guardfile +5 -0
  4. data/bin/percheron +8 -4
  5. data/lib/percheron.rb +3 -0
  6. data/lib/percheron/actions.rb +15 -0
  7. data/lib/percheron/actions/base.rb +50 -0
  8. data/lib/percheron/actions/build.rb +47 -0
  9. data/lib/percheron/actions/create.rb +109 -0
  10. data/lib/percheron/actions/exec.rb +51 -0
  11. data/lib/percheron/actions/purge.rb +38 -0
  12. data/lib/percheron/actions/recreate.rb +98 -0
  13. data/lib/percheron/actions/rename.rb +64 -0
  14. data/lib/percheron/actions/restart.rb +31 -0
  15. data/lib/percheron/actions/start.rb +46 -0
  16. data/lib/percheron/actions/stop.rb +27 -0
  17. data/lib/percheron/cli.rb +1 -0
  18. data/lib/percheron/cli/abstract_command.rb +7 -0
  19. data/lib/percheron/cli/create_command.rb +3 -2
  20. data/lib/percheron/cli/list_command.rb +2 -8
  21. data/lib/percheron/cli/main_command.rb +1 -0
  22. data/lib/percheron/cli/purge_command.rb +12 -0
  23. data/lib/percheron/cli/recreate_command.rb +9 -2
  24. data/lib/percheron/cli/restart_command.rb +3 -2
  25. data/lib/percheron/cli/start_command.rb +3 -2
  26. data/lib/percheron/cli/stop_command.rb +3 -2
  27. data/lib/percheron/container.rb +120 -5
  28. data/lib/percheron/errors.rb +0 -1
  29. data/lib/percheron/formatters/stack/table.rb +1 -1
  30. data/lib/percheron/null_container.rb +13 -0
  31. data/lib/percheron/oh_dear.rb +46 -0
  32. data/lib/percheron/stack.rb +90 -31
  33. data/lib/percheron/validators/container.rb +9 -1
  34. data/lib/percheron/version.rb +1 -1
  35. data/percheron.gemspec +2 -0
  36. metadata +45 -11
  37. data/lib/percheron/container/actions.rb +0 -15
  38. data/lib/percheron/container/actions/base.rb +0 -21
  39. data/lib/percheron/container/actions/build.rb +0 -58
  40. data/lib/percheron/container/actions/create.rb +0 -56
  41. data/lib/percheron/container/actions/recreate.rb +0 -110
  42. data/lib/percheron/container/actions/start.rb +0 -58
  43. data/lib/percheron/container/actions/stop.rb +0 -33
  44. data/lib/percheron/container/main.rb +0 -193
  45. 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