kumo_dockercloud 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,109 @@
1
+ require 'kumo_dockercloud/environment_config'
2
+ require 'kumo_dockercloud/docker_cloud_api'
3
+
4
+ describe KumoDockerCloud::EnvironmentConfig do
5
+ let(:env_name) { 'test' }
6
+ let(:config_path) { File.join(__dir__, '../fixtures/config') }
7
+ subject(:instance) { described_class.new(app_name: 'application-stack-name', env_name: env_name, config_path: config_path) }
8
+
9
+ let(:docker_cloud_api) { instance_double('KumoDockerCloud::DockerCloudApi') }
10
+
11
+ describe '#get_binding' do
12
+ let(:services_for_stack) { [] }
13
+
14
+ # Ruby binding can only be tested by
15
+ # evaluating strings against the binding
16
+ subject { eval(string, instance.get_binding) }
17
+
18
+ describe '@env_name' do
19
+ let(:string) { '@env_name' }
20
+
21
+ it 'is taken from the given parameter' do
22
+ expect(subject).to eq env_name
23
+ end
24
+ end
25
+
26
+ describe '#plain_text_secrets' do
27
+ context 'with no encrypted secrets' do
28
+ let(:secrets_data) do
29
+ {
30
+ 'testkey' => 'someval',
31
+ 'moretest' => 'otherval'
32
+ }
33
+ end
34
+
35
+ let(:string) { 'plain_text_secrets' }
36
+
37
+ it do
38
+ expect(subject).to eq secrets_data
39
+ end
40
+ end
41
+
42
+ context 'with some encrypted secrets' do
43
+ let(:env_name) { 'test_encrypted' }
44
+ let(:plain_value) { 'otherval' }
45
+
46
+ let(:kms) { double('KumoKi::KMS') }
47
+
48
+ let(:plain_data) do
49
+ {
50
+ 'testkey' => 'someval',
51
+ 'enctest' => plain_value
52
+ }
53
+ end
54
+
55
+ let(:string) { 'plain_text_secrets' }
56
+
57
+ before do
58
+ allow(KumoKi::KMS).to receive(:new).and_return(kms)
59
+ allow(kms).to receive(:decrypt).with('ZW5jcnlwdGVkIGJpbmFyeSBkYXRh').and_return(plain_value)
60
+ end
61
+
62
+ it do
63
+ expect(subject).to eq plain_data
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'with API mocked' do
69
+ before do
70
+ allow(KumoDockerCloud::DockerCloudApi).to receive(:new).and_return docker_cloud_api
71
+ allow(docker_cloud_api).to receive(:services_by_stack_name).with("application-stack-name-#{env_name}").and_return(services_for_stack)
72
+ end
73
+
74
+ describe '#image_name' do
75
+ let(:string) { 'image_name' }
76
+
77
+ context 'when there is a service for the given stack' do
78
+ let(:image_name) { 'redbubble/application-stack-name:1234' }
79
+ let(:services_for_stack) { [ double(DockerCloud::Service, image_name: image_name) ] }
80
+
81
+ it 'uses the pre-exisiting image name' do
82
+ expect(subject).to eq image_name
83
+ end
84
+ end
85
+
86
+ context 'when there is no service for the given stack' do
87
+ let(:services_for_stack) { [] }
88
+
89
+ it { expect(subject).to eq 'redbubble/application-stack-name:master' }
90
+ end
91
+ end
92
+
93
+ describe '#image_tag' do
94
+ let(:services_for_stack) { [ double(DockerCloud::Service, image_name: image_name)] }
95
+
96
+ subject { instance.image_tag }
97
+
98
+ context "when the image name is 'redbubble/assete-wala:latest'" do
99
+ let(:image_name) { 'redbubble/application-stack-name:latest' }
100
+ it { expect(subject).to eq 'latest' }
101
+ end
102
+ context "when the image name is 'some-registry/mything:9999'" do
103
+ let(:image_name) { 'some-registry/mything:9999' }
104
+ it { expect(subject).to eq '9999' }
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,95 @@
1
+ require 'kumo_dockercloud/environment'
2
+ require 'kumo_dockercloud/environment_config'
3
+
4
+ describe KumoDockerCloud::Environment do
5
+ let(:env_vars) { {app_name => {'KEY' => 'VALUE'}} }
6
+ let(:app_name) { 'application-stack-name' }
7
+ let(:config) { KumoDockerCloud::EnvironmentConfig.new(app_name: app_name, env_name: 'test', config_path: 'a path') }
8
+
9
+ let(:stack_file) {
10
+ {
11
+ 'application-stack-name': {
12
+ image: 'a-thing',
13
+ environment: {
14
+ TEST_ENV: 'FAKE',
15
+ MORE: 'ANOTHER',
16
+ KEY: 'VALUE'
17
+ }
18
+ }
19
+ }
20
+ }
21
+
22
+ let(:stack_template_path) { File.join(__dir__, '../fixtures/stack.yml.erb') }
23
+
24
+ let(:params) { {name: 'test', env_vars: env_vars, app_name: app_name, config_path: 'a path', stack_template_path: stack_template_path} }
25
+
26
+ subject(:env) { described_class.new(params) }
27
+
28
+ before do
29
+ allow(KumoDockerCloud::EnvironmentConfig).to receive(:new).and_return(config)
30
+ allow(KumoDockerCloud::StackFile).to receive(:create_from_template).and_return(stack_file)
31
+ end
32
+
33
+ describe "#apply" do
34
+ subject { env.apply }
35
+
36
+ let(:full_stack_name) { "#{app_name}-test" }
37
+
38
+ before do
39
+ allow(config).to receive(:image_tag).and_return('latest')
40
+ allow(env).to receive(:evaluate_command).and_return app_name
41
+ allow(env).to receive(:run_command)
42
+ docker_cloud_api = double("DockerCloudApi", stack_by_name: {"#{full_stack_name}": 'stack stuff'})
43
+ allow(KumoDockerCloud::DockerCloudApi).to receive(:new).and_return docker_cloud_api
44
+ allow_any_instance_of(KumoDockerCloud::StateValidator).to receive(:wait_for_state)
45
+ end
46
+
47
+ it "writes a stack file" do
48
+ expect_any_instance_of(Tempfile).to receive(:write).with(stack_file.to_yaml)
49
+
50
+ subject
51
+ end
52
+
53
+ it 'runs the stack command' do
54
+ expect(env).to receive(:run_command).with(%r{^docker-cloud stack create -f .* -n #{full_stack_name}$})
55
+
56
+ subject
57
+ end
58
+
59
+ it 'runs the redeploy command' do
60
+ expect(env).to receive(:run_command).with("docker-cloud stack redeploy #{full_stack_name}")
61
+
62
+ subject
63
+ end
64
+
65
+ describe "waiting for running" do
66
+
67
+ let(:state_validator) { double(KumoDockerCloud::StateValidator, wait_for_state: nil) }
68
+
69
+ before do
70
+ allow(KumoDockerCloud::StateValidator).to receive(:new).exactly(3).times.and_return(state_validator)
71
+ end
72
+
73
+ it "makes sure it waits until it's running" do
74
+ expect(state_validator).to receive(:wait_for_state).with(anything, 120).exactly(3).times
75
+ subject
76
+ end
77
+
78
+ context "setting a different timeout value" do
79
+ let(:params) { {name: 'test', env_vars: env_vars, app_name: app_name, config_path: 'a path', stack_template_path: stack_template_path, timeout: 240} }
80
+
81
+ it "sends the timeout value to the StateValidator" do
82
+ expect(state_validator).to receive(:wait_for_state).with(anything, 240).exactly(3).times
83
+ subject
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ it 'uses the StackFile class' do
90
+ expect(KumoDockerCloud::StackFile).to receive(:create_from_template).with(File.read(stack_template_path), config, env_vars)
91
+
92
+ subject
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,168 @@
1
+ require_relative '../../lib/kumo_dockercloud/environment_config'
2
+ require_relative '../../lib/kumo_dockercloud/stack_file'
3
+
4
+ describe KumoDockerCloud::StackFile do
5
+
6
+ let(:app_name) { 'application-stack-name' }
7
+ let(:config) { KumoDockerCloud::EnvironmentConfig.new(app_name: app_name, env_name: 'test', config_path: 'a path') }
8
+ let(:env_vars) { {app_name => {'KEY' => 'VALUE'}} }
9
+ let(:plain_text_secrets) do
10
+ {
11
+ 'TEST_ENV' => 'FAKE',
12
+ 'MORE' => 'ANOTHER'
13
+ }
14
+ end
15
+
16
+ before do
17
+ allow(KumoDockerCloud::EnvironmentConfig).to receive(:new).and_return(config)
18
+ allow(config).to receive(:plain_text_secrets).and_return(plain_text_secrets)
19
+ end
20
+
21
+ describe '.create_from_template' do
22
+ subject { described_class.create_from_template(stack_template, config, env_vars) }
23
+
24
+ let(:stack_template) do
25
+ <<-eos
26
+ application-stack-name:
27
+ image: a-thing
28
+ eos
29
+ end
30
+
31
+ it 'adds environment variables to stack config' do
32
+ expect(subject).to eq(app_name => {
33
+ 'image' => 'a-thing',
34
+ 'environment' => {
35
+ 'TEST_ENV' => 'FAKE',
36
+ 'MORE' => 'ANOTHER',
37
+ 'KEY' => 'VALUE'
38
+ }
39
+ })
40
+ end
41
+
42
+ context 'with some existing environment' do
43
+ let(:stack_template) do
44
+ <<-eos
45
+ application-stack-name:
46
+ image: a-thing
47
+ environment:
48
+ TEST: thing
49
+ eos
50
+ end
51
+ it 'should add new secrets to the environment' do
52
+ expect(subject).to eq(app_name => {
53
+ 'image' => 'a-thing',
54
+ 'environment' => {
55
+ 'TEST' => 'thing',
56
+ 'TEST_ENV' => 'FAKE',
57
+ 'MORE' => 'ANOTHER',
58
+ 'KEY' => 'VALUE'
59
+ }
60
+ })
61
+ end
62
+ end
63
+
64
+ context 'without any existing environment' do
65
+ let(:stack_template) do
66
+ <<-eos
67
+ application-stack-name:
68
+ image: a-thing
69
+ eos
70
+ end
71
+ it 'should create the environment with secrets in it' do
72
+ expect(subject).to eq(app_name => {
73
+ 'image' => 'a-thing',
74
+ 'environment' => {
75
+ 'TEST_ENV' => 'FAKE',
76
+ 'MORE' => 'ANOTHER',
77
+ 'KEY' => 'VALUE'
78
+ }
79
+ })
80
+ end
81
+ end
82
+
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
+ context 'with other services' do
107
+ let(:env_vars) do
108
+ {
109
+ app_name => {'KEY' => 'VALUE'},
110
+ "another_service" => {'KEY2' => 'VALUE2'}
111
+ }
112
+ end
113
+
114
+ let(:stack_template) do
115
+ <<-eos
116
+ application-stack-name:
117
+ image: a-thing
118
+ another_service:
119
+ image: another
120
+ environment:
121
+ KEY: thing
122
+ KEY2: OLD_VALUE
123
+ third_service:
124
+ image: third
125
+ eos
126
+ end
127
+
128
+ let(:expected_stack_file) do
129
+ {
130
+ app_name => {
131
+ 'image' => 'a-thing',
132
+ 'environment' => {
133
+ 'TEST_ENV' => 'FAKE',
134
+ 'MORE' => 'ANOTHER',
135
+ 'KEY' => 'VALUE'
136
+ }},
137
+ 'another_service' => {
138
+ 'image' => 'another',
139
+ 'environment' => {
140
+ 'KEY' => 'thing',
141
+ 'KEY2' => 'VALUE2',
142
+ }
143
+ },
144
+ 'third_service' => {
145
+ 'image' => 'third'
146
+ }
147
+ }
148
+ end
149
+
150
+ it 'should create the environment with variables for other services' do
151
+ expect(subject).to eq(expected_stack_file)
152
+ end
153
+
154
+ context 'the key is a symbol' do
155
+ let(:env_vars) do
156
+ {
157
+ app_name => {'KEY' => 'VALUE'},
158
+ another_service: {'KEY2' => 'VALUE2'}
159
+ }
160
+ end
161
+
162
+ it 'handles symbols the same way as strings' do
163
+ expect(subject).to eq(expected_stack_file)
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe KumoDockerCloud::Stack do
4
+ describe '#deploy' do
5
+ subject { described_class.new(app_name, environment_name).deploy(app_version) }
6
+
7
+ let(:app_name) { 'test_app' }
8
+ let(:environment_name) { 'environment' }
9
+ let(:app_version) { '1' }
10
+ let(:uuid) { 'foo' }
11
+ let(:client) { instance_double(DockerCloud::Client, stacks: stacks, services: service_api) }
12
+ let(:service_api) { instance_double(DockerCloud::ServiceAPI) }
13
+ let(:stacks) { double('stacks', all: [stack]) }
14
+ let(:stack) { instance_double(DockerCloud::Stack, name: "#{app_name}-#{environment_name}", services: [service]) }
15
+ let(:service) { instance_double(DockerCloud::Service, uuid: uuid, containers: []) }
16
+ let(:state_validator) { instance_double(KumoDockerCloud::StateValidator, wait_for_state: nil) }
17
+
18
+ before do
19
+ allow(::DockerCloud::Client).to receive(:new).and_return(client)
20
+ allow(KumoDockerCloud::StateValidator).to receive(:new).and_return(state_validator)
21
+ end
22
+
23
+ it 'uses the service api to update the image and redeploy' do
24
+ expect(service_api).to receive(:update).with(uuid, {image: "redbubble/#{app_name}:#{app_version}"})
25
+ expect(service_api).to receive(:redeploy).with(uuid)
26
+ subject
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe KumoDockerCloud::StateValidator do
5
+ describe '#wait_for_state' do
6
+ subject { state_validator.wait_for_state('done', 1) }
7
+ let(:state_validator) { described_class.new(state_provider) }
8
+ let(:state_provider) { double('state_provider', call: service) }
9
+ let(:service) { { state: service_state, name: 'service name' } }
10
+
11
+ context 'the right state' do
12
+ let(:service_state) { 'done' }
13
+
14
+ it 'succeeds immediately if the state is right' do
15
+ subject
16
+ end
17
+ end
18
+
19
+ context 'the wrong state' do
20
+ let(:service_state) { 'not quite done' }
21
+
22
+ it 'fails after the timeout value is reached' do
23
+ expect { subject }.to raise_error(Timeout::Error)
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'kumo_dockercloud'
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kumo_dockercloud
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Redbubble
8
+ - Delivery
9
+ - Engineering
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2016-04-05 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: httpi
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '2.4'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '2.4'
29
+ - !ruby/object:Gem::Dependency
30
+ name: docker_cloud
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '0.1'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '0.1'
43
+ - !ruby/object:Gem::Dependency
44
+ name: kumo_ki
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '1.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: bundler
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '1.6'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '1.6'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rake
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '10.0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '10.0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: rspec
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '3.4'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '3.4'
99
+ - !ruby/object:Gem::Dependency
100
+ name: webmock
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '1.22'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '1.22'
113
+ description: Use to create Redbubble environments on the DockerCloud platform
114
+ email:
115
+ - delivery-engineering@redbubble.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".buildkite/pipeline.yml"
121
+ - ".gitignore"
122
+ - ".rspec"
123
+ - ".ruby-version"
124
+ - ".travis.yml"
125
+ - Gemfile
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - kumo_dockercloud.gemspec
130
+ - lib/kumo_dockercloud.rb
131
+ - lib/kumo_dockercloud/deployment.rb
132
+ - lib/kumo_dockercloud/docker_cloud_api.rb
133
+ - lib/kumo_dockercloud/environment.rb
134
+ - lib/kumo_dockercloud/environment_config.rb
135
+ - lib/kumo_dockercloud/stack.rb
136
+ - lib/kumo_dockercloud/stack_file.rb
137
+ - lib/kumo_dockercloud/state_validator.rb
138
+ - lib/kumo_dockercloud/version.rb
139
+ - script/build.sh
140
+ - script/release-gem
141
+ - script/unit_test.sh
142
+ - spec/fixtures/config/test.yml
143
+ - spec/fixtures/config/test_encrypted_secrets.yml
144
+ - spec/fixtures/config/test_secrets.yml
145
+ - spec/fixtures/stack.yml.erb
146
+ - spec/kumo_docker_cloud_spec.rb
147
+ - spec/kumo_dockercloud/docker_cloud_api_spec.rb
148
+ - spec/kumo_dockercloud/environment_config_spec.rb
149
+ - spec/kumo_dockercloud/environment_spec.rb
150
+ - spec/kumo_dockercloud/stack_file_spec.rb
151
+ - spec/kumo_dockercloud/stack_spec.rb
152
+ - spec/kumo_dockercloud/state_validator_spec.rb
153
+ - spec/spec_helper.rb
154
+ homepage: ''
155
+ licenses:
156
+ - mit
157
+ metadata: {}
158
+ post_install_message:
159
+ rdoc_options: []
160
+ require_paths:
161
+ - lib
162
+ required_ruby_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ requirements: []
173
+ rubyforge_project:
174
+ rubygems_version: 2.4.5.1
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: Use to create Redbubble environments on the DockerCloud platform
178
+ test_files:
179
+ - spec/fixtures/config/test.yml
180
+ - spec/fixtures/config/test_encrypted_secrets.yml
181
+ - spec/fixtures/config/test_secrets.yml
182
+ - spec/fixtures/stack.yml.erb
183
+ - spec/kumo_docker_cloud_spec.rb
184
+ - spec/kumo_dockercloud/docker_cloud_api_spec.rb
185
+ - spec/kumo_dockercloud/environment_config_spec.rb
186
+ - spec/kumo_dockercloud/environment_spec.rb
187
+ - spec/kumo_dockercloud/stack_file_spec.rb
188
+ - spec/kumo_dockercloud/stack_spec.rb
189
+ - spec/kumo_dockercloud/state_validator_spec.rb
190
+ - spec/spec_helper.rb