kumo_dockercloud 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/kumo_dockercloud.gemspec +1 -0
- data/lib/kumo_dockercloud/docker_cloud_api.rb +1 -0
- data/lib/kumo_dockercloud/errors.rb +2 -0
- data/lib/kumo_dockercloud/haproxy_command.rb +24 -0
- data/lib/kumo_dockercloud/haproxy_container.rb +51 -0
- data/lib/kumo_dockercloud/haproxy_event_handler.rb +25 -0
- data/lib/kumo_dockercloud/haproxy_service.rb +29 -0
- data/lib/kumo_dockercloud/service.rb +8 -4
- data/lib/kumo_dockercloud/stack.rb +24 -23
- data/lib/kumo_dockercloud/version.rb +1 -1
- data/lib/kumo_dockercloud.rb +4 -0
- data/spec/kumo_dockercloud/haproxy_command_spec.rb +45 -0
- data/spec/kumo_dockercloud/haproxy_container_spec.rb +84 -0
- data/spec/kumo_dockercloud/haproxy_event_handler_spec.rb +44 -0
- data/spec/kumo_dockercloud/haproxy_service_spec.rb +48 -0
- data/spec/kumo_dockercloud/stack_spec.rb +45 -55
- metadata +28 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e7a3083ac7e9265513cc75563a7b7e161e1a286
|
4
|
+
data.tar.gz: 42ae9a4b63c95fb00f8ee706cc5df18f1a81cb51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cb94285609c0da1c6197adf3548e089000d8d8c0cdee521e38de7ca770cf5c28e2fa2b5d4b39bbcda43fb6a185297d4d2cd3df36e5c10ba499f10709637a61a
|
7
|
+
data.tar.gz: 0736d09ec8b388a88e7f415c38c94241fff02739ee0065f4a356cf34bbcba2fae25bb5031e585e9bf5e0513cc7085d6b9fef5724580872407befdc1d4048e2a6
|
data/Gemfile
CHANGED
data/kumo_dockercloud.gemspec
CHANGED
@@ -26,5 +26,6 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency 'rake', '~> 10.0'
|
27
27
|
spec.add_development_dependency 'rspec', '~> 3.4'
|
28
28
|
spec.add_development_dependency 'webmock', '~> 1.22'
|
29
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.4'
|
29
30
|
spec.add_development_dependency 'rubocop', '~> 0.40'
|
30
31
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module KumoDockerCloud
|
2
2
|
class Error < RuntimeError; end
|
3
3
|
class ServiceDeployError < RuntimeError; end
|
4
|
+
class HaproxySocketError < RuntimeError; end
|
5
|
+
class HAProxyStateError < Error; end
|
4
6
|
class EnvironmentApplyError < RuntimeError; end
|
5
7
|
class StackCheckError < RuntimeError; end
|
6
8
|
class InvalidStackError < RuntimeError; end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module KumoDockerCloud
|
4
|
+
class HaproxyCommand
|
5
|
+
def initialize(container_id, dc_client)
|
6
|
+
@container_id = container_id
|
7
|
+
@dc_client = dc_client
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute(command)
|
11
|
+
cmd = %(sh -c "echo #{command} | nc -U /var/run/haproxy.stats")
|
12
|
+
api = DockerCloud::ContainerStreamAPI.new(@container_id, cmd, @dc_client.headers, @dc_client)
|
13
|
+
|
14
|
+
handler = KumoDockerCloud::HaproxyEventHandler.new
|
15
|
+
api.on(:open, &handler.on_open)
|
16
|
+
api.on(:message, &handler.on_message)
|
17
|
+
api.on(:error, &handler.on_error)
|
18
|
+
api.on(:close, &handler.on_close)
|
19
|
+
|
20
|
+
api.run!
|
21
|
+
handler.data
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module KumoDockerCloud
|
4
|
+
class HaproxyContainer
|
5
|
+
def initialize(container_id, client)
|
6
|
+
@container_id = container_id
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def disable_server(server_name)
|
11
|
+
haproxy_server_name = haproxy_server_name(server_name)
|
12
|
+
HaproxyCommand.new(@container_id, @client).execute("disable server #{haproxy_server_name}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def enable_server(server_name)
|
16
|
+
haproxy_server_name = haproxy_server_name(server_name)
|
17
|
+
HaproxyCommand.new(@container_id, @client).execute("enable server #{haproxy_server_name}")
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def stats
|
23
|
+
haproxy_command = HaproxyCommand.new(@container_id, @client)
|
24
|
+
command_output = ''
|
25
|
+
retry_counter = 0
|
26
|
+
while command_output.empty? && retry_counter < 3
|
27
|
+
command_output = haproxy_command.execute('show stat')
|
28
|
+
retry_counter += 1
|
29
|
+
end
|
30
|
+
raise HAProxyStateError.new("Could not get stats from HAProxy backend") if command_output.empty?
|
31
|
+
CSV.parse(command_output, headers: true)
|
32
|
+
end
|
33
|
+
|
34
|
+
def haproxy_server_name(server_name)
|
35
|
+
current_stats = stats
|
36
|
+
haproxy_server_record = current_stats.find { |stat| prefix_match? stat, server_name }
|
37
|
+
|
38
|
+
raise HAProxyStateError.new("Unable to map #{server_name} to a HAProxy backend, I saw #{ get_server_names(current_stats) }") unless haproxy_server_record
|
39
|
+
|
40
|
+
"#{haproxy_server_record['# pxname']}/#{haproxy_server_record['svname']}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def prefix_match?(stat_record, server_name)
|
44
|
+
stat_record["svname"].downcase.start_with? server_name.downcase.gsub('-', '_')
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_server_names(stats_object)
|
48
|
+
stats_object.select { |record| record['# pxname'] == 'default_service' }.map { |record| record['svname']}.join(', ')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module KumoDockerCloud
|
2
|
+
class HaproxyEventHandler
|
3
|
+
attr_accessor :data
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@data = ''
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_open
|
10
|
+
Proc.new { |_event| @data = '' }
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_message
|
14
|
+
Proc.new { |event| @data << JSON.parse(event.data)['output'] }
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_error
|
18
|
+
Proc.new { |event| raise HaproxySocketError.new(event.message) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_close
|
22
|
+
Proc.new { |_event| EventMachine.stop }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module KumoDockerCloud
|
2
|
+
class HaproxyService < Service
|
3
|
+
def initialize(stack_name)
|
4
|
+
super(stack_name, 'haproxy')
|
5
|
+
|
6
|
+
@client = docker_cloud_api.client
|
7
|
+
end
|
8
|
+
|
9
|
+
def disable_service(service)
|
10
|
+
service_to_disable = service.name
|
11
|
+
haproxy_containers = containers.map { |container| HaproxyContainer.new(container.uuid, @client) }
|
12
|
+
|
13
|
+
raise KumoDockerCloud::HAProxyStateError.new('Could not get instances of the haproxy container for this environment') if haproxy_containers.empty?
|
14
|
+
|
15
|
+
haproxy_threads = haproxy_containers.map { |haproxy_container| Thread.new { haproxy_container.disable_server(service_to_disable) } }
|
16
|
+
haproxy_threads.each(&:join)
|
17
|
+
end
|
18
|
+
|
19
|
+
def enable_service(service)
|
20
|
+
service_to_enable = service.name
|
21
|
+
haproxy_containers = containers.map { |container| HaproxyContainer.new(container.uuid, @client) }
|
22
|
+
|
23
|
+
raise KumoDockerCloud::HAProxyStateError.new('Could not get instances of the haproxy container for this environment') if haproxy_containers.empty?
|
24
|
+
|
25
|
+
haproxy_threads = haproxy_containers.map { |haproxy_container| Thread.new { haproxy_container.enable_server(service_to_enable) } }
|
26
|
+
haproxy_threads.each(&:join)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -30,6 +30,10 @@ module KumoDockerCloud
|
|
30
30
|
get_service.linked_to_service
|
31
31
|
end
|
32
32
|
|
33
|
+
def state
|
34
|
+
get_service.state
|
35
|
+
end
|
36
|
+
|
33
37
|
def set_link(service_to_link, link_internal_name)
|
34
38
|
linked_service = {
|
35
39
|
to_service: service_to_link.resource_uri,
|
@@ -52,6 +56,10 @@ module KumoDockerCloud
|
|
52
56
|
get_service.containers
|
53
57
|
end
|
54
58
|
|
59
|
+
def uuid
|
60
|
+
get_service.uuid
|
61
|
+
end
|
62
|
+
|
55
63
|
private
|
56
64
|
attr_reader :stack_name
|
57
65
|
|
@@ -75,10 +83,6 @@ module KumoDockerCloud
|
|
75
83
|
docker_cloud_api.service_by_stack_and_service_name(stack_name, name)
|
76
84
|
end
|
77
85
|
|
78
|
-
def uuid
|
79
|
-
get_service.uuid
|
80
|
-
end
|
81
|
-
|
82
86
|
def image_name
|
83
87
|
get_service.image_name.split(':').first
|
84
88
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
1
3
|
module KumoDockerCloud
|
2
4
|
class Stack
|
3
5
|
attr_reader :stack_name, :app_name, :options
|
@@ -18,37 +20,36 @@ module KumoDockerCloud
|
|
18
20
|
checker.verify(service)
|
19
21
|
end
|
20
22
|
|
23
|
+
def deploy_blue_green(service_names, version, checker = ServiceChecker.new)
|
24
|
+
haproxy_service = HaproxyService.new(@stack_name)
|
25
|
+
|
26
|
+
services = service_names.map { |name| Service.new(stack_name, name) }
|
27
|
+
ordered_deployment(services).each do |service|
|
28
|
+
begin
|
29
|
+
haproxy_service.disable_service(service) unless service.state == "Stopped"
|
30
|
+
service.deploy(version)
|
31
|
+
checker.verify(service)
|
32
|
+
haproxy_service.enable_service(service)
|
33
|
+
rescue HAProxyStateError => e
|
34
|
+
raise ServiceDeployError.new("Unable to place service #{service.name} into maintainance mode on HAProxy with message: #{e.message}")
|
35
|
+
rescue ServiceDeployError => e
|
36
|
+
haproxy_service.disable_service(service)
|
37
|
+
raise ServiceDeployError.new("Deployment or verification of service #{service.name} failed with message: #{e.message}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
21
42
|
def services
|
22
43
|
services = docker_cloud_api.services_by_stack_name(stack_name)
|
23
44
|
services.map { |service| Service.new(stack_name, service.name) }
|
24
45
|
end
|
25
46
|
|
26
|
-
|
27
|
-
service_names = options[:service_names]
|
28
|
-
version = options[:version]
|
29
|
-
checker = options[:checker] || ServiceChecker.new
|
30
|
-
switching_service_name = options[:switching_service_name]
|
31
|
-
|
32
|
-
validate_params(version, "Version")
|
33
|
-
validate_params(service_names, "Service names")
|
34
|
-
validate_params(switching_service_name, "Switching service name")
|
35
|
-
|
36
|
-
switching_service = Service.new(stack_name, switching_service_name)
|
37
|
-
link = switching_service.links.find { |link| service_names.include?(Service.service_by_resource_uri(link[:to_service]).name) }
|
38
|
-
active_service = Service.service_by_resource_uri(link[:to_service])
|
39
|
-
|
40
|
-
inactive_service_name = service_names.find { |name| name != active_service.name }
|
41
|
-
inactive_service = Service.new(stack_name, inactive_service_name)
|
42
|
-
|
43
|
-
inactive_service.deploy(version)
|
44
|
-
checker.verify(inactive_service)
|
47
|
+
private
|
45
48
|
|
46
|
-
|
47
|
-
|
49
|
+
def ordered_deployment(services)
|
50
|
+
services.sort { |service_a, service_b| service_b.state <=> service_a.state }
|
48
51
|
end
|
49
52
|
|
50
|
-
private
|
51
|
-
|
52
53
|
def validate_params(param_value, param_name)
|
53
54
|
raise KumoDockerCloud::Error.new("#{param_name} cannot be nil") unless param_value
|
54
55
|
raise KumoDockerCloud::Error.new("#{param_name} cannot be empty") if param_value.empty?
|
data/lib/kumo_dockercloud.rb
CHANGED
@@ -7,3 +7,7 @@ require 'kumo_dockercloud/service_checker'
|
|
7
7
|
require 'kumo_dockercloud/service_check'
|
8
8
|
require 'kumo_dockercloud/errors'
|
9
9
|
require 'kumo_dockercloud/console_jockey'
|
10
|
+
require 'kumo_dockercloud/haproxy_service'
|
11
|
+
require 'kumo_dockercloud/haproxy_container'
|
12
|
+
require 'kumo_dockercloud/haproxy_command'
|
13
|
+
require 'kumo_dockercloud/haproxy_event_handler'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KumoDockerCloud::HaproxyCommand do
|
4
|
+
describe '#execute' do
|
5
|
+
let(:command) { 'enable' }
|
6
|
+
let(:dc_client) { double(:dc_client, headers: nil)}
|
7
|
+
let(:container_id) { 'id' }
|
8
|
+
let(:api) { instance_double(DockerCloud::ContainerStreamAPI, on: nil, run!: nil)}
|
9
|
+
let(:cmd) { %(sh -c "echo #{command} | nc -U /var/run/haproxy.stats") }
|
10
|
+
let(:handler) { KumoDockerCloud::HaproxyEventHandler.new }
|
11
|
+
|
12
|
+
|
13
|
+
subject { described_class.new(container_id, dc_client).execute(command) }
|
14
|
+
|
15
|
+
it 'uses the ContainerStreamAPI with the passed in command' do
|
16
|
+
expect(DockerCloud::ContainerStreamAPI).to receive(:new).with(container_id, cmd, dc_client.headers, dc_client).and_return(api)
|
17
|
+
subject
|
18
|
+
end
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(DockerCloud::ContainerStreamAPI).to receive(:new).with(container_id, cmd, dc_client.headers, dc_client).and_return(api)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'configures the callback handlers' do
|
25
|
+
allow(KumoDockerCloud::HaproxyEventHandler).to receive(:new).and_return(handler)
|
26
|
+
|
27
|
+
expect(api).to receive(:on).with(:open)
|
28
|
+
expect(api).to receive(:on).with(:message)
|
29
|
+
expect(api).to receive(:on).with(:error)
|
30
|
+
expect(api).to receive(:on).with(:close)
|
31
|
+
subject
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'runs the event machine' do
|
35
|
+
expect(api).to receive(:run!)
|
36
|
+
subject
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns the data from the callback handler' do
|
40
|
+
allow(KumoDockerCloud::HaproxyEventHandler).to receive(:new).and_return(handler)
|
41
|
+
expect(handler).to receive(:data)
|
42
|
+
subject
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KumoDockerCloud::HaproxyContainer do
|
4
|
+
subject { KumoDockerCloud::HaproxyContainer.new('container-id', client) }
|
5
|
+
|
6
|
+
let(:client) { instance_double(DockerCloud::Client) }
|
7
|
+
let(:haproxy_command) { instance_double(KumoDockerCloud::HaproxyCommand, :haproxy_command, execute: nil )}
|
8
|
+
let(:csv_output) do <<EOF
|
9
|
+
# pxname,svname
|
10
|
+
default_frontend,FRONTEND
|
11
|
+
default_service,BLUE_SERVICE_1
|
12
|
+
default_service,GREEN_SERVICE
|
13
|
+
default_service,BACKEND
|
14
|
+
EOF
|
15
|
+
end
|
16
|
+
let(:haproxy_server_name) { 'default_service/BLUE_SERVICE_1' }
|
17
|
+
let(:server_name) { 'blue-service' }
|
18
|
+
let(:non_existant_server_name) { 'derpy-service' }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(KumoDockerCloud::HaproxyCommand).to receive(:new).and_return(haproxy_command)
|
22
|
+
allow(haproxy_command).to receive(:execute).with('show stat').and_return(csv_output)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#disable_server' do
|
26
|
+
it "runs disable server using HAProxy's name" do
|
27
|
+
expect(haproxy_command).to receive(:execute).with("disable server #{haproxy_server_name}")
|
28
|
+
subject.disable_server(server_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises an error if it is unable to map a server name to a haproxy name' do
|
32
|
+
expect { subject.disable_server(non_existant_server_name) }.to raise_error(
|
33
|
+
KumoDockerCloud::HAProxyStateError,
|
34
|
+
"Unable to map #{non_existant_server_name} to a HAProxy backend, I saw BLUE_SERVICE_1, GREEN_SERVICE, BACKEND"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'tries 3 times if it is unable to get stats from haproxy' do
|
39
|
+
expect(haproxy_command).to receive(:execute).with('show stat').exactly(3).times.and_return('')
|
40
|
+
expect { subject.disable_server(server_name) }.to raise_error(
|
41
|
+
KumoDockerCloud::HAProxyStateError,
|
42
|
+
"Could not get stats from HAProxy backend"
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'stops trying getting haproxy server name when it gets the haproxy record' do
|
47
|
+
expect(haproxy_command).to receive(:execute).with('show stat').once.ordered.and_return('')
|
48
|
+
expect(haproxy_command).to receive(:execute).with('show stat').once.ordered.and_return(csv_output)
|
49
|
+
|
50
|
+
expect(haproxy_command).to receive(:execute).with("disable server #{haproxy_server_name}").once.ordered
|
51
|
+
subject.disable_server(server_name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#enable_server' do
|
56
|
+
it "runs enable server using HAProxy's name" do
|
57
|
+
expect(haproxy_command).to receive(:execute).with("enable server #{haproxy_server_name}")
|
58
|
+
subject.enable_server(server_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'raises an error if it is unable to map a server name to a haproxy name' do
|
62
|
+
expect { subject.disable_server(non_existant_server_name) }.to raise_error(
|
63
|
+
KumoDockerCloud::HAProxyStateError,
|
64
|
+
"Unable to map #{non_existant_server_name} to a HAProxy backend, I saw BLUE_SERVICE_1, GREEN_SERVICE, BACKEND"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'tries 3 times if it is unable to get stats from haproxy' do
|
69
|
+
expect(haproxy_command).to receive(:execute).with('show stat').exactly(3).times.and_return('')
|
70
|
+
expect { subject.enable_server(server_name) }.to raise_error(
|
71
|
+
KumoDockerCloud::HAProxyStateError,
|
72
|
+
"Could not get stats from HAProxy backend"
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'stops trying getting haproxy server name when it gets the haproxy record' do
|
77
|
+
expect(haproxy_command).to receive(:execute).with('show stat').once.ordered.and_return('')
|
78
|
+
expect(haproxy_command).to receive(:execute).with('show stat').once.ordered.and_return(csv_output)
|
79
|
+
|
80
|
+
expect(haproxy_command).to receive(:execute).with("enable server #{haproxy_server_name}").once.ordered
|
81
|
+
subject.enable_server(server_name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KumoDockerCloud::HaproxyEventHandler do
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
describe '#on_open' do
|
7
|
+
it 'resets the handler state' do
|
8
|
+
subject.data = 'fred'
|
9
|
+
subject.on_open.call(nil)
|
10
|
+
expect(subject.data).to eq ''
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#on_message' do
|
15
|
+
let(:event) { double(:event, data: '{"output": "data"}')}
|
16
|
+
|
17
|
+
it 'parses json' do
|
18
|
+
expect(JSON).to receive(:parse).with(event.data).and_return({'output' => 'data'})
|
19
|
+
subject.on_message.call(event)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'accumulate the message to data' do
|
23
|
+
subject.on_message.call(event)
|
24
|
+
subject.on_message.call(event)
|
25
|
+
subject.on_message.call(event)
|
26
|
+
expect(subject.data).to eq('datadatadata')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#on_error' do
|
31
|
+
let(:event) { double(:event, message: 'woops!') }
|
32
|
+
|
33
|
+
it 'raises a HaproxySocketError with the correct message' do
|
34
|
+
expect { subject.on_error.call(event) }.to raise_error(KumoDockerCloud::HaproxySocketError, 'woops!')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#on_close' do
|
39
|
+
it 'closes the event machine' do
|
40
|
+
expect(EventMachine).to receive(:stop)
|
41
|
+
subject.on_close.call(nil)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KumoDockerCloud::HaproxyService do
|
4
|
+
let(:service_name) { 'the-service' }
|
5
|
+
let(:container_uuid) { 'i-am-the-container'}
|
6
|
+
let(:docker_cloud_container) { double(:docker_cloud_haproxy, uuid: container_uuid)}
|
7
|
+
let(:containers) { [docker_cloud_container] }
|
8
|
+
let(:docker_cloud_service) { double(:service, containers: containers) }
|
9
|
+
let(:haproxy_container) { instance_double(KumoDockerCloud::HaproxyContainer, :haproxy_container, disable_server: nil, enable_server: nil) }
|
10
|
+
let(:docker_cloud_client) { double }
|
11
|
+
let(:kumo_docker_cloud_api) { instance_double(KumoDockerCloud::DockerCloudApi, :docker_cloud_api, client: docker_cloud_client ) }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(KumoDockerCloud::DockerCloudApi).to receive(:new).and_return(kumo_docker_cloud_api)
|
15
|
+
allow(kumo_docker_cloud_api).to receive(:service_by_stack_and_service_name).with('stack_name', 'haproxy').and_return(docker_cloud_service)
|
16
|
+
allow(KumoDockerCloud::HaproxyContainer).to receive(:new).with(container_uuid, docker_cloud_client).and_return(haproxy_container)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#disable_service' do
|
20
|
+
subject { KumoDockerCloud::HaproxyService.new('stack_name').disable_service(service_to_disable) }
|
21
|
+
let(:service_to_disable) { instance_double(KumoDockerCloud::Service, :service_to_disable, name: service_name) }
|
22
|
+
|
23
|
+
it 'sends the disable_server message to all its containers' do
|
24
|
+
expect(haproxy_container).to receive(:disable_server).with(service_name)
|
25
|
+
subject
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'blows up when there are no instances of haproxy' do
|
29
|
+
allow(docker_cloud_service).to receive(:containers).and_return([])
|
30
|
+
expect { subject }.to raise_error(KumoDockerCloud::HAProxyStateError, 'Could not get instances of the haproxy container for this environment')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#enable_service' do
|
35
|
+
subject { KumoDockerCloud::HaproxyService.new('stack_name').enable_service(service_to_enable) }
|
36
|
+
let(:service_to_enable) { instance_double(KumoDockerCloud::Service, :service_to_enable, name: service_name) }
|
37
|
+
|
38
|
+
it 'sends the enable_server message to all its containers' do
|
39
|
+
expect(haproxy_container).to receive(:enable_server).with(service_name)
|
40
|
+
subject
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'blows up when there are no instances of haproxy' do
|
44
|
+
allow(docker_cloud_service).to receive(:containers).and_return([])
|
45
|
+
expect { subject }.to raise_error(KumoDockerCloud::HAProxyStateError, 'Could not get instances of the haproxy container for this environment')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -67,75 +67,65 @@ describe KumoDockerCloud::Stack do
|
|
67
67
|
end
|
68
68
|
|
69
69
|
describe '#deploy_blue_green' do
|
70
|
-
|
71
|
-
let(:
|
72
|
-
let(:
|
73
|
-
|
74
|
-
let(:
|
75
|
-
let(:
|
76
|
-
let(:
|
77
|
-
let(:linked_service_uri) { "active_uri" }
|
78
|
-
let(:switching_service) { instance_double(KumoDockerCloud::Service, links: [service_link], set_link: nil) }
|
79
|
-
let(:switching_service_internal_link_name) { "app" }
|
80
|
-
let(:deploy_options) do
|
81
|
-
{
|
82
|
-
service_names: [active_service.name, inactive_service.name],
|
83
|
-
version: version,
|
84
|
-
checker: checker,
|
85
|
-
switching_service_name: "switcher"
|
86
|
-
}
|
87
|
-
end
|
88
|
-
|
89
|
-
subject { described_class.new(app_name, environment_name).deploy_blue_green(deploy_options) }
|
70
|
+
subject { stack.deploy_blue_green(service_names, version, checker) }
|
71
|
+
let(:checker) { instance_double(KumoDockerCloud::ServiceChecker, :service_checker, verify: true) }
|
72
|
+
let(:service_names) { ['service-a', 'service-b'] }
|
73
|
+
let(:version) { 1 }
|
74
|
+
let(:service_a) { instance_double(KumoDockerCloud::Service, :service_a, state: 'Running', deploy: nil, name: 'service_a') }
|
75
|
+
let(:service_b) { instance_double(KumoDockerCloud::Service, :service_b, state: 'Running', deploy: nil, name: 'service_b') }
|
76
|
+
let(:haproxy) { instance_double(KumoDockerCloud::HaproxyService, :haproxy_svc, disable_service: nil, enable_service: nil) }
|
90
77
|
|
91
78
|
before do
|
92
|
-
allow(KumoDockerCloud::Service).to receive(:
|
93
|
-
|
94
|
-
allow(KumoDockerCloud::
|
95
|
-
.with(stack_name, active_service.name)
|
96
|
-
.and_return(active_service)
|
97
|
-
|
98
|
-
allow(KumoDockerCloud::Service).to receive(:new)
|
99
|
-
.with(stack_name, inactive_service.name)
|
100
|
-
.and_return(inactive_service)
|
101
|
-
|
102
|
-
allow(KumoDockerCloud::Service).to receive(:new)
|
103
|
-
.with(stack_name, deploy_options[:switching_service_name])
|
104
|
-
.and_return(switching_service)
|
79
|
+
allow(KumoDockerCloud::Service).to receive(:new).with(stack_name, 'service-a').and_return(service_a)
|
80
|
+
allow(KumoDockerCloud::Service).to receive(:new).with(stack_name, 'service-b').and_return(service_b)
|
81
|
+
allow(KumoDockerCloud::HaproxyService).to receive(:new).with(stack_name).and_return(haproxy)
|
105
82
|
end
|
106
83
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
84
|
+
it 'deploys to each service when both are active' do
|
85
|
+
expect(haproxy).to receive(:disable_service).with(service_a)
|
86
|
+
expect(haproxy).to receive(:disable_service).with(service_b)
|
87
|
+
expect(service_a).to receive(:deploy).with(version)
|
88
|
+
expect(service_b).to receive(:deploy).with(version)
|
89
|
+
subject
|
90
|
+
end
|
112
91
|
|
113
|
-
|
114
|
-
|
115
|
-
expect{ subject }.to raise_error(KumoDockerCloud::Error, "Service names cannot be nil")
|
116
|
-
end
|
92
|
+
it 'deploys to the stopped service first when one is inactive' do
|
93
|
+
allow(service_b).to receive(:state).and_return('Stopped')
|
117
94
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
95
|
+
expect(haproxy).to_not receive(:disable_service).with(service_b)
|
96
|
+
expect(service_b).to receive(:deploy).with(version).ordered
|
97
|
+
expect(haproxy).to receive(:disable_service).with(service_a).ordered
|
98
|
+
expect(service_a).to receive(:deploy).with(version).ordered
|
99
|
+
subject
|
122
100
|
end
|
123
101
|
|
124
|
-
it '
|
125
|
-
expect(
|
126
|
-
expect(checker).to receive(:verify).with(
|
127
|
-
expect(active_service).to_not receive(:deploy)
|
102
|
+
it 'runs the check on each service' do
|
103
|
+
expect(checker).to receive(:verify).with(service_a)
|
104
|
+
expect(checker).to receive(:verify).with(service_b)
|
128
105
|
subject
|
129
106
|
end
|
130
107
|
|
131
|
-
it '
|
132
|
-
expect(
|
108
|
+
it 're-enables each service in haproxy' do
|
109
|
+
expect(haproxy).to receive(:enable_service).with(service_a)
|
110
|
+
expect(haproxy).to receive(:enable_service).with(service_b)
|
133
111
|
subject
|
134
112
|
end
|
135
113
|
|
136
|
-
it '
|
137
|
-
|
138
|
-
|
114
|
+
it 'cancels deployment if the first deploy fails' do
|
115
|
+
allow(checker).to receive(:verify).with(service_a).and_raise(KumoDockerCloud::ServiceDeployError)
|
116
|
+
expect(service_b).to_not receive(:deploy)
|
117
|
+
expect { subject }.to raise_error(KumoDockerCloud::ServiceDeployError)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'raises an error if any attempt to place a service into maintainance mode fails' do
|
121
|
+
expect(haproxy).to receive(:disable_service).with(service_a).and_raise(KumoDockerCloud::HAProxyStateError, 'Something broke!')
|
122
|
+
expect { subject }.to raise_error(KumoDockerCloud::ServiceDeployError)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'attempts to place a service into maintainance mode if deployment fails because HAProxy will take it out of maintainance mode on deploy' do
|
126
|
+
expect(haproxy).to receive(:disable_service).with(service_a).twice
|
127
|
+
allow(checker).to receive(:verify).with(service_a).and_raise(KumoDockerCloud::ServiceDeployError)
|
128
|
+
expect { subject }.to raise_error(KumoDockerCloud::ServiceDeployError)
|
139
129
|
end
|
140
130
|
end
|
141
131
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kumo_dockercloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Redbubble
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-06-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: httpi
|
@@ -110,6 +110,20 @@ dependencies:
|
|
110
110
|
- - "~>"
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: '1.22'
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: pry-byebug
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '3.4'
|
120
|
+
type: :development
|
121
|
+
prerelease: false
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - "~>"
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '3.4'
|
113
127
|
- !ruby/object:Gem::Dependency
|
114
128
|
name: rubocop
|
115
129
|
requirement: !ruby/object:Gem::Requirement
|
@@ -149,6 +163,10 @@ files:
|
|
149
163
|
- lib/kumo_dockercloud/environment.rb
|
150
164
|
- lib/kumo_dockercloud/environment_config.rb
|
151
165
|
- lib/kumo_dockercloud/errors.rb
|
166
|
+
- lib/kumo_dockercloud/haproxy_command.rb
|
167
|
+
- lib/kumo_dockercloud/haproxy_container.rb
|
168
|
+
- lib/kumo_dockercloud/haproxy_event_handler.rb
|
169
|
+
- lib/kumo_dockercloud/haproxy_service.rb
|
152
170
|
- lib/kumo_dockercloud/service.rb
|
153
171
|
- lib/kumo_dockercloud/service_check.rb
|
154
172
|
- lib/kumo_dockercloud/service_checker.rb
|
@@ -169,6 +187,10 @@ files:
|
|
169
187
|
- spec/kumo_dockercloud/docker_cloud_api_spec.rb
|
170
188
|
- spec/kumo_dockercloud/environment_config_spec.rb
|
171
189
|
- spec/kumo_dockercloud/environment_spec.rb
|
190
|
+
- spec/kumo_dockercloud/haproxy_command_spec.rb
|
191
|
+
- spec/kumo_dockercloud/haproxy_container_spec.rb
|
192
|
+
- spec/kumo_dockercloud/haproxy_event_handler_spec.rb
|
193
|
+
- spec/kumo_dockercloud/haproxy_service_spec.rb
|
172
194
|
- spec/kumo_dockercloud/service_checker_spec.rb
|
173
195
|
- spec/kumo_dockercloud/service_spec.rb
|
174
196
|
- spec/kumo_dockercloud/stack_checker_spec.rb
|
@@ -210,6 +232,10 @@ test_files:
|
|
210
232
|
- spec/kumo_dockercloud/docker_cloud_api_spec.rb
|
211
233
|
- spec/kumo_dockercloud/environment_config_spec.rb
|
212
234
|
- spec/kumo_dockercloud/environment_spec.rb
|
235
|
+
- spec/kumo_dockercloud/haproxy_command_spec.rb
|
236
|
+
- spec/kumo_dockercloud/haproxy_container_spec.rb
|
237
|
+
- spec/kumo_dockercloud/haproxy_event_handler_spec.rb
|
238
|
+
- spec/kumo_dockercloud/haproxy_service_spec.rb
|
213
239
|
- spec/kumo_dockercloud/service_checker_spec.rb
|
214
240
|
- spec/kumo_dockercloud/service_spec.rb
|
215
241
|
- spec/kumo_dockercloud/stack_checker_spec.rb
|