percheron 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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