kumo_dockercloud 1.2.0 → 2.0.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/lib/kumo_dockercloud/service.rb +29 -5
- data/lib/kumo_dockercloud/stack.rb +23 -0
- data/lib/kumo_dockercloud/stack_file.rb +1 -4
- data/lib/kumo_dockercloud/version.rb +1 -1
- data/spec/kumo_dockercloud/service_spec.rb +71 -9
- data/spec/kumo_dockercloud/stack_file_spec.rb +0 -31
- data/spec/kumo_dockercloud/stack_spec.rb +79 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f17aa8666415f9aca28a1bff56fd4f15008ac281
|
4
|
+
data.tar.gz: 69a0f07ebf17b9a59fdf426a7e567f620f862b88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4c7cec1ea8cce00a0ac8cd85375ec7ab965c44f02fd9516f0be9e0cb4598e9fbab4bb1d4034f406c4e845f71b76f27ca20e0f371b5c39a7ef4a7b9daba90fed
|
7
|
+
data.tar.gz: a06f463691c216e733f47d83f16713ce85706f5befff163f97bc11d8d52689ecdd72a04271afb328446a7aa009a9870cf4186f3c632751f3641d4a597dbe986d
|
@@ -2,6 +2,8 @@ require 'timeout'
|
|
2
2
|
|
3
3
|
module KumoDockerCloud
|
4
4
|
class Service
|
5
|
+
attr_reader :name
|
6
|
+
|
5
7
|
def initialize(stack_name, service_name)
|
6
8
|
@stack_name = stack_name
|
7
9
|
@name = service_name
|
@@ -35,11 +37,33 @@ module KumoDockerCloud
|
|
35
37
|
raise KumoDockerCloud::ServiceDeployError.new("One or more checks failed to pass within the timeout")
|
36
38
|
end
|
37
39
|
|
40
|
+
def links
|
41
|
+
get_service.linked_to_service.map { |service| KumoDockerCloud::Service.new(stack_name, service[:name]) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_link(service_to_link)
|
45
|
+
linked_service = {
|
46
|
+
to_service: service_to_link.resource_uri,
|
47
|
+
name: service_to_link.name,
|
48
|
+
from_service: resource_uri
|
49
|
+
}
|
50
|
+
|
51
|
+
docker_cloud_api.services.update(uuid, linked_to_service: [linked_service])
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
docker_cloud_api.services.stop(uuid)
|
56
|
+
end
|
57
|
+
|
58
|
+
def resource_uri
|
59
|
+
get_service.resource_uri
|
60
|
+
end
|
61
|
+
|
38
62
|
private
|
39
|
-
attr_reader :stack_name
|
63
|
+
attr_reader :stack_name
|
40
64
|
|
41
65
|
def containers
|
42
|
-
|
66
|
+
get_service.containers
|
43
67
|
end
|
44
68
|
|
45
69
|
def update_image(version)
|
@@ -58,16 +82,16 @@ module KumoDockerCloud
|
|
58
82
|
@docker_cloud_api ||= KumoDockerCloud::DockerCloudApi.new
|
59
83
|
end
|
60
84
|
|
61
|
-
def
|
85
|
+
def get_service
|
62
86
|
docker_cloud_api.service_by_stack_and_service_name(stack_name, name)
|
63
87
|
end
|
64
88
|
|
65
89
|
def uuid
|
66
|
-
|
90
|
+
get_service.uuid
|
67
91
|
end
|
68
92
|
|
69
93
|
def image_name
|
70
|
-
|
94
|
+
get_service.image_name.split(':').first
|
71
95
|
end
|
72
96
|
end
|
73
97
|
end
|
@@ -2,6 +2,7 @@ module KumoDockerCloud
|
|
2
2
|
class Stack
|
3
3
|
attr_reader :stack_name, :app_name, :options
|
4
4
|
|
5
|
+
#TODO delete options
|
5
6
|
def initialize(app_name, env_name, options = { contactable: true })
|
6
7
|
@app_name = app_name
|
7
8
|
@stack_name = "#{app_name}-#{env_name}"
|
@@ -17,6 +18,28 @@ module KumoDockerCloud
|
|
17
18
|
service.check(checks, check_timeout) if checks
|
18
19
|
end
|
19
20
|
|
21
|
+
def deploy_blue_green(options)
|
22
|
+
service_names = options[:service_names]
|
23
|
+
version = options[:version]
|
24
|
+
checks = options[:checks]
|
25
|
+
check_timeout = options[:check_timeout]
|
26
|
+
switching_service_name = options[:switching_service_name]
|
27
|
+
|
28
|
+
validate_params(version, "Version")
|
29
|
+
validate_params(service_names, "Service names")
|
30
|
+
validate_params(switching_service_name, "Switching service name")
|
31
|
+
|
32
|
+
services = service_names.map { |service_name| Service.new(stack_name, service_name) }
|
33
|
+
|
34
|
+
switching_service = Service.new(stack_name, switching_service_name)
|
35
|
+
green_service = switching_service.links.find { |linked_service| services.find { |service| service.name == linked_service.name } }
|
36
|
+
blue_service = services.find { |service| service.name != green_service.name }
|
37
|
+
|
38
|
+
deploy(blue_service.name, version, checks, check_timeout)
|
39
|
+
switching_service.set_link(blue_service)
|
40
|
+
green_service.stop
|
41
|
+
end
|
42
|
+
|
20
43
|
private
|
21
44
|
|
22
45
|
def validate_params(param_value, param_name)
|
@@ -5,14 +5,11 @@ module KumoDockerCloud
|
|
5
5
|
def self.create_from_template(stack_template, config, env_vars)
|
6
6
|
parsed = YAML.load(ERB.new(stack_template).result(config.get_binding))
|
7
7
|
|
8
|
-
parsed[config.app_name]['environment'] ||= {}
|
9
|
-
parsed[config.app_name]['environment'].merge!(config.plain_text_secrets)
|
10
|
-
parsed[config.app_name]['environment'].merge!(env_vars.fetch(config.app_name, {}))
|
11
|
-
|
12
8
|
converted_env_vars = make_all_root_level_keys_strings(env_vars)
|
13
9
|
|
14
10
|
env_vars.each do |key, _|
|
15
11
|
key_string = key.to_s
|
12
|
+
parsed[key_string]['environment'] ||= {}
|
16
13
|
parsed[key_string]['environment'].merge!(converted_env_vars.fetch(key_string))
|
17
14
|
end
|
18
15
|
|
@@ -4,32 +4,33 @@ require 'httpi'
|
|
4
4
|
describe KumoDockerCloud::Service do
|
5
5
|
let(:service_image) { "repository/docker_image_name:version" }
|
6
6
|
let(:service_uuid) { "i_am_a_unique_snowflower" }
|
7
|
-
let(:
|
8
|
-
let(:
|
7
|
+
let(:docker_cloud_service) { double(:service, uuid: service_uuid, image_name: service_image, containers: [], resource_uri: "api/v1/#{service_uuid}")}
|
8
|
+
let(:docker_cloud_services_api) { double(:services_api) }
|
9
|
+
let(:docker_cloud_api) { instance_double(KumoDockerCloud::DockerCloudApi, services: docker_cloud_services_api)}
|
9
10
|
|
10
11
|
subject { described_class.new('stack_name', 'service_name') }
|
11
12
|
|
12
13
|
before do
|
13
14
|
allow(KumoDockerCloud::DockerCloudApi).to receive(:new).and_return(docker_cloud_api)
|
14
15
|
|
15
|
-
allow(docker_cloud_api).to receive(:service_by_stack_and_service_name).with('stack_name', 'service_name').and_return(
|
16
|
+
allow(docker_cloud_api).to receive(:service_by_stack_and_service_name).with('stack_name', 'service_name').and_return(docker_cloud_service)
|
16
17
|
end
|
17
18
|
|
18
19
|
describe '#deploy' do
|
19
20
|
it 'runs the actual update and redeploy methods' do
|
20
|
-
expect(
|
21
|
-
expect(
|
21
|
+
expect(docker_cloud_services_api).to receive(:update).with(service_uuid, { image: service_image })
|
22
|
+
expect(docker_cloud_services_api).to receive(:redeploy).with(service_uuid)
|
22
23
|
subject.deploy('version')
|
23
24
|
end
|
24
25
|
|
25
26
|
it 'raises an appropriate exception when there is an error during image update' do
|
26
|
-
expect(
|
27
|
+
expect(docker_cloud_services_api).to receive(:update).and_raise(RestClient::InternalServerError)
|
27
28
|
expect { subject.deploy('version') }.to raise_error(KumoDockerCloud::ServiceDeployError, "Something went wrong during service update on Docker Cloud's end")
|
28
29
|
end
|
29
30
|
|
30
31
|
it 'raises an appropriate exception when there is an error during redployment' do
|
31
|
-
allow(
|
32
|
-
expect(
|
32
|
+
allow(docker_cloud_services_api).to receive(:update).with(service_uuid, { image: service_image })
|
33
|
+
expect(docker_cloud_services_api).to receive(:redeploy).and_raise(RestClient::InternalServerError)
|
33
34
|
expect { subject.deploy('version') }.to raise_error(KumoDockerCloud::ServiceDeployError, "Something went wrong during service update on Docker Cloud's end")
|
34
35
|
end
|
35
36
|
end
|
@@ -54,7 +55,7 @@ describe KumoDockerCloud::Service do
|
|
54
55
|
allow(http_lib).to receive(:get).with("http://whale1.test/site_status").and_return(200)
|
55
56
|
allow(http_lib).to receive(:get).with("http://whale2.test/site_status").and_return("timeout", "timeout", 200)
|
56
57
|
allow(whale1).to receive(:state).and_return("Starting", "Running")
|
57
|
-
allow(
|
58
|
+
allow(docker_cloud_service).to receive(:containers).and_return(containers)
|
58
59
|
end
|
59
60
|
|
60
61
|
it 'resolves to true if all the checks eventually pass' do
|
@@ -68,5 +69,66 @@ describe KumoDockerCloud::Service do
|
|
68
69
|
allow(whale1).to receive(:state).and_return("Starting")
|
69
70
|
expect { subject.check(checks, short_timeout) }.to raise_error(KumoDockerCloud::ServiceDeployError, "One or more checks failed to pass within the timeout")
|
70
71
|
end
|
72
|
+
|
73
|
+
describe "#links" do
|
74
|
+
let(:linked_service_uuid) { "i_am_the_db" }
|
75
|
+
let(:linked_service_name) { "db" }
|
76
|
+
let(:linked_to_service) do
|
77
|
+
{
|
78
|
+
from_service: service_uuid,
|
79
|
+
name: linked_service_name,
|
80
|
+
to_service: linked_service_uuid
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
before do
|
85
|
+
allow(docker_cloud_service).to receive(:linked_to_service).and_return([linked_to_service])
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns a list of KumoDockerCloud::Service object that are linked to from this service" do
|
89
|
+
links = subject.links
|
90
|
+
expect(links.first).to have_attributes(name: linked_service_name)
|
91
|
+
expect(links.size).to eq(1)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "returns an empty array if there are no links" do
|
95
|
+
allow(docker_cloud_service).to receive(:linked_to_service).and_return([])
|
96
|
+
expect(subject.links).to eq([])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#set_link" do
|
101
|
+
let(:linked_service_uuid) { "i_am_the_db" }
|
102
|
+
let(:linked_service_name) { "db" }
|
103
|
+
let(:linked_to_service) do
|
104
|
+
{
|
105
|
+
to_service: "api/v1/#{linked_service_uuid}",
|
106
|
+
name: linked_service_name,
|
107
|
+
from_service: "api/v1/#{service_uuid}"
|
108
|
+
}
|
109
|
+
end
|
110
|
+
let(:linked_service) { KumoDockerCloud::Service.new('stack_name', linked_service_name) }
|
111
|
+
let(:this_service) { double(:this_service, uuid: service_uuid, resource_uri: "api/v1/#{service_uuid}") }
|
112
|
+
let(:linked_service) { double(:linked_service, uuid: linked_service_uuid, resource_uri: "api/v1/#{linked_service_uuid}", name: linked_service_name) }
|
113
|
+
|
114
|
+
before do
|
115
|
+
allow(docker_cloud_api).to receive(:service_by_stack_and_service_name).with('stack_name', 'service_name').and_return(this_service)
|
116
|
+
allow(docker_cloud_api).to receive(:service_by_stack_and_service_name).with('stack_name', linked_service_name).and_return(linked_service)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "updates the link attribute" do
|
120
|
+
expect(docker_cloud_services_api).to receive(:update).with(service_uuid, { linked_to_service: [linked_to_service] })
|
121
|
+
|
122
|
+
subject.set_link(linked_service)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#stop" do
|
127
|
+
it "sends a request to stop the service" do
|
128
|
+
expect(docker_cloud_services_api).to receive(:stop).with(service_uuid)
|
129
|
+
|
130
|
+
subject.stop
|
131
|
+
end
|
132
|
+
end
|
71
133
|
end
|
72
134
|
end
|
@@ -32,8 +32,6 @@ describe KumoDockerCloud::StackFile do
|
|
32
32
|
expect(subject).to eq(app_name => {
|
33
33
|
'image' => 'a-thing',
|
34
34
|
'environment' => {
|
35
|
-
'TEST_ENV' => 'FAKE',
|
36
|
-
'MORE' => 'ANOTHER',
|
37
35
|
'KEY' => 'VALUE'
|
38
36
|
}
|
39
37
|
})
|
@@ -53,8 +51,6 @@ describe KumoDockerCloud::StackFile do
|
|
53
51
|
'image' => 'a-thing',
|
54
52
|
'environment' => {
|
55
53
|
'TEST' => 'thing',
|
56
|
-
'TEST_ENV' => 'FAKE',
|
57
|
-
'MORE' => 'ANOTHER',
|
58
54
|
'KEY' => 'VALUE'
|
59
55
|
}
|
60
56
|
})
|
@@ -72,37 +68,12 @@ describe KumoDockerCloud::StackFile do
|
|
72
68
|
expect(subject).to eq(app_name => {
|
73
69
|
'image' => 'a-thing',
|
74
70
|
'environment' => {
|
75
|
-
'TEST_ENV' => 'FAKE',
|
76
|
-
'MORE' => 'ANOTHER',
|
77
71
|
'KEY' => 'VALUE'
|
78
72
|
}
|
79
73
|
})
|
80
74
|
end
|
81
75
|
end
|
82
76
|
|
83
|
-
context 'no app name env var' do
|
84
|
-
let(:env_vars) do
|
85
|
-
{ }
|
86
|
-
end
|
87
|
-
|
88
|
-
let(:stack_template) do
|
89
|
-
<<-eos
|
90
|
-
application-stack-name:
|
91
|
-
image: a-thing
|
92
|
-
eos
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'should create the environment with secrets in it' do
|
96
|
-
expect(subject).to eq(app_name => {
|
97
|
-
'image' => 'a-thing',
|
98
|
-
'environment' => {
|
99
|
-
'TEST_ENV' => 'FAKE',
|
100
|
-
'MORE' => 'ANOTHER'
|
101
|
-
}
|
102
|
-
})
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
77
|
context 'with other services' do
|
107
78
|
let(:env_vars) do
|
108
79
|
{
|
@@ -130,8 +101,6 @@ describe KumoDockerCloud::StackFile do
|
|
130
101
|
app_name => {
|
131
102
|
'image' => 'a-thing',
|
132
103
|
'environment' => {
|
133
|
-
'TEST_ENV' => 'FAKE',
|
134
|
-
'MORE' => 'ANOTHER',
|
135
104
|
'KEY' => 'VALUE'
|
136
105
|
}},
|
137
106
|
'another_service' => {
|
@@ -60,4 +60,83 @@ describe KumoDockerCloud::Stack do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
end
|
63
|
+
|
64
|
+
describe '#deploy_blue_green' do
|
65
|
+
let(:service_a_uuid) { 'service_a_uuid' }
|
66
|
+
let(:service_a) { instance_double(KumoDockerCloud::Service, :service_a, uuid: uuid, name: "service-a") }
|
67
|
+
let(:service_b_uuid) { 'service_b_uuid' }
|
68
|
+
let(:service_b) { instance_double(KumoDockerCloud::Service, :service_b, uuid: uuid, name: "service-b") }
|
69
|
+
let(:nginx) { instance_double(KumoDockerCloud::Service, uuid: "nginx_uuid") }
|
70
|
+
let(:version) { "1" }
|
71
|
+
let(:deployment_checks) { [] }
|
72
|
+
let(:links) { [service_a] }
|
73
|
+
let(:check_timeout) { 120 }
|
74
|
+
let(:deploy_options) do
|
75
|
+
{
|
76
|
+
service_names: ["service-a", "service-b"],
|
77
|
+
version: version,
|
78
|
+
checks: deployment_checks,
|
79
|
+
check_timeout: check_timeout,
|
80
|
+
switching_service_name: "nginx"
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
subject { described_class.new(app_name, environment_name).deploy_blue_green(deploy_options) }
|
85
|
+
|
86
|
+
before do
|
87
|
+
allow(KumoDockerCloud::Service).to receive(:new)
|
88
|
+
.with(stack_name, "service-a")
|
89
|
+
.and_return(service_a)
|
90
|
+
|
91
|
+
allow(KumoDockerCloud::Service).to receive(:new)
|
92
|
+
.with(stack_name, "service-b")
|
93
|
+
.and_return(service_b)
|
94
|
+
|
95
|
+
allow(KumoDockerCloud::Service).to receive(:new)
|
96
|
+
.with(stack_name, "nginx")
|
97
|
+
.and_return(nginx)
|
98
|
+
|
99
|
+
allow(nginx).to receive(:links).and_return(links)
|
100
|
+
allow(nginx).to receive(:set_link).with(service_b)
|
101
|
+
|
102
|
+
allow(service_a).to receive(:stop)
|
103
|
+
|
104
|
+
allow(service_b).to receive(:deploy).with(version)
|
105
|
+
allow(service_b).to receive(:check).with(deployment_checks, check_timeout)
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when parameters are missing' do
|
109
|
+
it 'blow up when version is missing' do
|
110
|
+
deploy_options.delete(:version)
|
111
|
+
expect{ subject }.to raise_error(KumoDockerCloud::Error, "Version cannot be nil")
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'blow up when service_names are missing' do
|
115
|
+
deploy_options.delete(:service_names)
|
116
|
+
expect{ subject }.to raise_error(KumoDockerCloud::Error, "Service names cannot be nil")
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'blow up when switching_service_name is missing' do
|
120
|
+
deploy_options.delete(:switching_service_name)
|
121
|
+
expect{ subject }.to raise_error(KumoDockerCloud::Error, "Switching service name cannot be nil")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'deploys to the blue service only' do
|
126
|
+
expect(service_b).to receive(:deploy).with(version)
|
127
|
+
expect(service_b).to receive(:check).with(deployment_checks, check_timeout)
|
128
|
+
expect(service_a).to_not receive(:deploy)
|
129
|
+
subject
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'switches over to the blue service on a successful deployment' do
|
133
|
+
expect(nginx).to receive(:set_link).with(service_b)
|
134
|
+
subject
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'shuts down the previously green service' do
|
138
|
+
expect(service_a).to receive(:stop)
|
139
|
+
subject
|
140
|
+
end
|
141
|
+
end
|
63
142
|
end
|
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:
|
4
|
+
version: 2.0.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-04-
|
13
|
+
date: 2016-04-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: httpi
|