kumo_dockercloud 3.2.0 → 3.3.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 +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
|