kumo_dockercloud 1.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.
@@ -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