Skip to content

Commit 19ade00

Browse files
committed
minor refactors + move updater tests to seperate actions
1 parent d4a6bf7 commit 19ade00

File tree

10 files changed

+1019
-826
lines changed

10 files changed

+1019
-826
lines changed

lib/cloud_controller/deployment_updater/actions/canary.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ def call
2929
logger.info("paused-canary-deployment-for-#{deployment.guid}")
3030
end
3131
end
32-
33-
def instance_reporters
34-
CloudController::DependencyLocator.instance.instances_reporters
35-
end
3632
end
3733
end
3834
end

lib/cloud_controller/deployment_updater/actions/cancel.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def call
2424
prior_web_process = Calculators::FindInterimWebProcess.new(deployment).call || app.oldest_web_process
2525
prior_web_process.lock!
2626

27-
prior_web_process.update(instances: deployment.original_web_process_instance_count, type: ProcessTypes::WEB)
27+
prior_web_process.update(instances: deployment.original_web_process_instance_count)
2828

2929
CleanupWebProcesses.new(deployment, prior_web_process).call
3030

lib/cloud_controller/deployment_updater/actions/finalize.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ def initialize(deployment)
1212
end
1313

1414
def call
15-
promote_deploying_web_process
16-
1715
CleanupWebProcesses.new(deployment, deploying_web_process).call
1816

1917
update_non_web_processes
@@ -27,10 +25,6 @@ def call
2725

2826
private
2927

30-
def promote_deploying_web_process
31-
deploying_web_process.update(type: ProcessTypes::WEB)
32-
end
33-
3428
def update_non_web_processes
3529
return if deploying_web_process.revision.nil?
3630

lib/cloud_controller/deployment_updater/actions/scale.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
require 'cloud_controller/deployment_updater/actions/scale_down_old_process'
33
require 'cloud_controller/deployment_updater/actions/finalize'
44
require 'cloud_controller/deployment_updater/calculators/all_instances_routable'
5-
require 'cloud_controller/deployment_updater/calculators/instances_to_scale_up'
6-
require 'cloud_controller/deployment_updater/calculators/instances_to_scale_down'
75

86
module VCAP::CloudController
97
module DeploymentUpdater
@@ -26,9 +24,6 @@ def call
2624
oldest_web_process_with_instances.lock!
2725
deploying_web_process.lock!
2826

29-
instances_to_scale_up = Calculators::InstancesToScaleUp.new(deployment).call
30-
instances_to_scale_down = Calculators::InstancesToScaleDown.new(deployment, oldest_web_process_with_instances).call
31-
3227
deployment.update(
3328
last_healthy_at: Time.now,
3429
state: DeploymentModel::DEPLOYING_STATE,
@@ -50,6 +45,14 @@ def call
5045

5146
private
5247

48+
def instances_to_scale_down
49+
[(oldest_web_process_with_instances.instances - deployment.max_in_flight), 0].max
50+
end
51+
52+
def instances_to_scale_up
53+
[deploying_web_process.instances + deployment.max_in_flight, deployment.original_web_process_instance_count].min
54+
end
55+
5356
def oldest_web_process_with_instances
5457
@oldest_web_process_with_instances ||= app.web_processes.select { |process| process.instances > 0 }.min_by { |p| [p.created_at, p.id] }
5558
end

lib/cloud_controller/deployment_updater/calculators/instances_to_scale_down.rb

Lines changed: 0 additions & 18 deletions
This file was deleted.

lib/cloud_controller/deployment_updater/calculators/instances_to_scale_up.rb

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
require 'spec_helper'
2+
require 'cloud_controller/deployment_updater/actions/canary'
3+
4+
module VCAP::CloudController
5+
RSpec.describe DeploymentUpdater::Actions::Canary do
6+
subject(:canary_action) { DeploymentUpdater::Actions::Canary.new(deployment, logger) }
7+
let(:a_day_ago) { Time.now - 1.day }
8+
let(:an_hour_ago) { Time.now - 1.hour }
9+
let(:app) { AppModel.make(droplet: droplet, revisions_enabled: true) }
10+
let(:droplet) { DropletModel.make }
11+
let!(:web_process) do
12+
ProcessModel.make(
13+
instances: current_web_instances,
14+
created_at: a_day_ago,
15+
guid: 'guid-original',
16+
app: app
17+
)
18+
end
19+
let!(:route_mapping) { RouteMappingModel.make(app: web_process.app, process_type: web_process.type) }
20+
let!(:deploying_web_process) do
21+
ProcessModel.make(
22+
app: web_process.app,
23+
type: ProcessTypes::WEB,
24+
instances: current_deploying_instances,
25+
guid: 'guid-final',
26+
revision: revision,
27+
state: ProcessModel::STOPPED
28+
)
29+
end
30+
let(:revision) { RevisionModel.make(app: app, droplet: droplet, version: 300) }
31+
let!(:deploying_route_mapping) { RouteMappingModel.make(app: web_process.app, process_type: deploying_web_process.type) }
32+
let(:space) { web_process.space }
33+
let(:original_web_process_instance_count) { 6 }
34+
let(:current_web_instances) { 2 }
35+
36+
let(:state) { DeploymentModel::PREPAUSED_STATE }
37+
let(:current_deploying_instances) { 1 }
38+
39+
let(:deployment) do
40+
DeploymentModel.make(
41+
app: web_process.app,
42+
deploying_web_process: deploying_web_process,
43+
state: state,
44+
original_web_process_instance_count: original_web_process_instance_count,
45+
max_in_flight: 1
46+
)
47+
end
48+
49+
let(:diego_instances_reporter) { instance_double(Diego::InstancesReporter) }
50+
let(:all_instances_results) do
51+
{
52+
0 => { state: 'RUNNING', uptime: 50, since: 2, routable: true },
53+
1 => { state: 'RUNNING', uptime: 50, since: 2, routable: true },
54+
2 => { state: 'RUNNING', uptime: 50, since: 2, routable: true }
55+
}
56+
end
57+
let(:instances_reporters) { double(:instance_reporters) }
58+
let(:logger) { instance_double(Steno::Logger, info: nil, error: nil) }
59+
60+
before do
61+
allow(CloudController::DependencyLocator.instance).to receive(:instances_reporters).and_return(instances_reporters)
62+
allow(instances_reporters).to receive(:all_instances_for_app).and_return(all_instances_results)
63+
end
64+
65+
it 'locks the deployment' do
66+
allow(deployment).to receive(:lock!).and_call_original
67+
subject.call
68+
expect(deployment).to have_received(:lock!)
69+
end
70+
71+
context 'when the canary instance starts succesfully' do
72+
let(:all_instances_results) do
73+
{
74+
0 => { state: 'RUNNING', uptime: 50, since: 2, routable: true }
75+
}
76+
end
77+
78+
it 'pauses the deployment' do
79+
subject.call
80+
expect(deployment.state).to eq(DeploymentModel::PAUSED_STATE)
81+
expect(deployment.status_value).to eq(DeploymentModel::ACTIVE_STATUS_VALUE)
82+
expect(deployment.status_reason).to eq(DeploymentModel::PAUSED_STATUS_REASON)
83+
end
84+
85+
it 'updates last_healthy_at' do
86+
previous_last_healthy_at = deployment.last_healthy_at
87+
Timecop.travel(deployment.last_healthy_at + 10.seconds) do
88+
subject.call
89+
expect(deployment.reload.last_healthy_at).to be > previous_last_healthy_at
90+
end
91+
end
92+
93+
it 'does not alter the existing web processes' do
94+
expect do
95+
subject.call
96+
end.not_to(change do
97+
web_process.reload.instances
98+
end)
99+
end
100+
101+
it 'logs the canary is paused' do
102+
subject.call
103+
expect(logger).to have_received(:info).with(
104+
"paused-canary-deployment-for-#{deployment.guid}"
105+
)
106+
end
107+
end
108+
109+
context 'while the canary instance is still starting' do
110+
let(:all_instances_results) do
111+
{
112+
0 => { state: 'STARTING', uptime: 50, since: 2, routable: true }
113+
}
114+
end
115+
116+
it 'skips the deployment update' do
117+
subject.call
118+
expect(deployment.state).to eq(DeploymentModel::PREPAUSED_STATE)
119+
expect(deployment.status_value).to eq(DeploymentModel::ACTIVE_STATUS_VALUE)
120+
expect(deployment.status_reason).to eq(DeploymentModel::DEPLOYING_STATUS_REASON)
121+
end
122+
end
123+
124+
context 'when the canary is not routable routable' do
125+
let(:all_instances_results) do
126+
{
127+
0 => { state: 'RUNNING', uptime: 50, since: 2, routable: false }
128+
}
129+
end
130+
131+
it 'skips the deployment update' do
132+
subject.call
133+
expect(deployment.state).to eq(DeploymentModel::PREPAUSED_STATE)
134+
expect(deployment.status_value).to eq(DeploymentModel::ACTIVE_STATUS_VALUE)
135+
expect(deployment.status_reason).to eq(DeploymentModel::DEPLOYING_STATUS_REASON)
136+
end
137+
end
138+
139+
context 'when the canary instance is failing' do
140+
let(:all_instances_results) do
141+
{
142+
0 => { state: 'FAILING', uptime: 50, since: 2, routable: true }
143+
}
144+
end
145+
146+
it 'does not update the deployments last_healthy_at' do
147+
Timecop.travel(Time.now + 1.minute) do
148+
expect do
149+
subject.call
150+
end.not_to(change { deployment.reload.last_healthy_at })
151+
end
152+
end
153+
154+
it 'changes nothing' do
155+
previous_last_healthy_at = deployment.last_healthy_at
156+
subject.call
157+
expect(deployment.reload.last_healthy_at).to eq previous_last_healthy_at
158+
expect(deployment.state).to eq(DeploymentModel::PREPAUSED_STATE)
159+
expect(deployment.status_value).to eq(DeploymentModel::ACTIVE_STATUS_VALUE)
160+
expect(deployment.status_reason).to eq(DeploymentModel::DEPLOYING_STATUS_REASON)
161+
end
162+
end
163+
164+
context 'when there is an interim deployment that has been SUPERSEDED (CANCELED)' do
165+
let!(:interim_canceling_web_process) do
166+
ProcessModel.make(
167+
app: app,
168+
created_at: an_hour_ago,
169+
type: ProcessTypes::WEB,
170+
instances: 1,
171+
guid: 'guid-canceling'
172+
)
173+
end
174+
let!(:interim_canceled_superseded_deployment) do
175+
DeploymentModel.make(
176+
deploying_web_process: interim_canceling_web_process,
177+
state: 'CANCELED',
178+
status_reason: 'SUPERSEDED'
179+
)
180+
end
181+
182+
it 'scales the canceled web process to zero' do
183+
subject.call
184+
expect(interim_canceling_web_process.reload.instances).to eq(0)
185+
end
186+
end
187+
188+
context 'when this deployment got superseded' do
189+
before do
190+
deployment.update(state: 'DEPLOYED', status_reason: 'SUPERSEDED')
191+
192+
allow(deployment).to receive(:update).and_call_original
193+
end
194+
195+
it 'skips the deployment update' do
196+
subject.call
197+
expect(deployment).not_to have_received(:update)
198+
end
199+
end
200+
end
201+
end

0 commit comments

Comments
 (0)