Skip to content

Commit 80a458d

Browse files
committed
WIP deployment updater refactor
1 parent d84cd86 commit 80a458d

File tree

8 files changed

+333
-194
lines changed

8 files changed

+333
-194
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
require 'cloud_controller/deployment_updater/actions/scale_down_superseded'
2+
require 'cloud_controller/deployment_updater/calculators/all_instances_routable'
3+
4+
module VCAP::CloudController
5+
module DeploymentUpdater
6+
module Actions
7+
class Canary
8+
attr_reader :deployment, :logger
9+
10+
def initialize(deployment, logger)
11+
@deployment = deployment
12+
@logger = logger
13+
end
14+
15+
def call
16+
deployment.db.transaction do
17+
deployment.lock!
18+
return unless deployment.state == DeploymentModel::PREPAUSED_STATE
19+
20+
ScaleDownSuperseded.new(deployment).call
21+
22+
if Calculators::AllInstancesRoutable.new(deployment, logger).call
23+
deployment.update(
24+
last_healthy_at: Time.now,
25+
state: DeploymentModel::PAUSED_STATE,
26+
status_value: DeploymentModel::ACTIVE_STATUS_VALUE,
27+
status_reason: DeploymentModel::PAUSED_STATUS_REASON
28+
)
29+
logger.info("paused-canary-deployment-for-#{deployment.guid}")
30+
end
31+
end
32+
end
33+
34+
def instance_reporters
35+
CloudController::DependencyLocator.instance.instances_reporters
36+
end
37+
end
38+
end
39+
end
40+
end
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
module VCAP::CloudController
2+
module DeploymentUpdater
3+
module Actions
4+
class Cancel
5+
attr_reader :deployment, :logger, :app, :deploying_web_process
6+
7+
def initialize(deployment, logger)
8+
@deployment = deployment
9+
@logger = logger
10+
@app = deployment.app
11+
@deploying_web_process = deployment.deploying_web_process
12+
end
13+
14+
def call
15+
deployment.db.transaction do
16+
app.lock!
17+
return unless deployment.lock!.state == DeploymentModel::CANCELING_STATE
18+
19+
deploying_web_process.lock!
20+
21+
prior_web_process = interim_web_process || app.oldest_web_process
22+
prior_web_process.lock!
23+
24+
prior_web_process.update(instances: deployment.original_web_process_instance_count, type: ProcessTypes::WEB)
25+
26+
cleanup_web_processes_except(prior_web_process)
27+
28+
deployment.update(
29+
state: DeploymentModel::CANCELED_STATE,
30+
status_value: DeploymentModel::FINALIZED_STATUS_VALUE,
31+
status_reason: DeploymentModel::CANCELED_STATUS_REASON
32+
)
33+
end
34+
end
35+
36+
private
37+
38+
def cleanup_web_processes_except(protected_process)
39+
app.web_processes.
40+
reject { |p| p.guid == protected_process.guid }.
41+
map(&:destroy)
42+
end
43+
44+
def interim_web_process
45+
# Find newest interim web process that (a) belongs to a SUPERSEDED (DEPLOYED) deployment and (b) has at least
46+
# one running instance.
47+
app.web_processes_dataset.
48+
qualify.
49+
join(:deployments, deploying_web_process_guid: :guid).
50+
where(deployments__state: DeploymentModel::DEPLOYED_STATE).
51+
where(deployments__status_reason: DeploymentModel::SUPERSEDED_STATUS_REASON).
52+
order(Sequel.desc(:created_at), Sequel.desc(:id)).
53+
find { |p| running_instance?(p) }
54+
end
55+
56+
def running_instance?(process)
57+
instances = instance_reporters.all_instances_for_app(process)
58+
instances.any? { |_, val| val[:state] == VCAP::CloudController::Diego::LRP_RUNNING }
59+
rescue CloudController::Errors::ApiError # the instances_reporter re-raises InstancesUnavailable as ApiError
60+
false
61+
end
62+
63+
def instance_reporters
64+
CloudController::DependencyLocator.instance.instances_reporters
65+
end
66+
end
67+
end
68+
end
69+
end
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
module VCAP::CloudController
2+
module DeploymentUpdater
3+
module Actions
4+
class Finalize
5+
attr_reader :deployment, :deploying_web_process, :app
6+
7+
def initialize(deployment)
8+
@deployment = deployment
9+
@app = deployment.app
10+
@deploying_web_process = deployment.deploying_web_process
11+
end
12+
13+
def call
14+
promote_deploying_web_process
15+
16+
cleanup_web_processes_except(deploying_web_process)
17+
18+
update_non_web_processes
19+
restart_non_web_processes
20+
deployment.update(
21+
state: DeploymentModel::DEPLOYED_STATE,
22+
status_value: DeploymentModel::FINALIZED_STATUS_VALUE,
23+
status_reason: DeploymentModel::DEPLOYED_STATUS_REASON
24+
)
25+
end
26+
27+
private
28+
29+
def promote_deploying_web_process
30+
deploying_web_process.update(type: ProcessTypes::WEB)
31+
end
32+
33+
def cleanup_web_processes_except(protected_process)
34+
app.web_processes.
35+
reject { |p| p.guid == protected_process.guid }.
36+
map(&:destroy)
37+
end
38+
39+
def update_non_web_processes
40+
return if deploying_web_process.revision.nil?
41+
42+
app.processes.reject(&:web?).each do |process|
43+
process.update(command: deploying_web_process.revision.commands_by_process_type[process.type])
44+
end
45+
end
46+
47+
def restart_non_web_processes
48+
app.processes.reject(&:web?).each do |process|
49+
VCAP::CloudController::ProcessRestart.restart(
50+
process: process,
51+
config: Config.config,
52+
stop_in_runtime: true,
53+
revision: deploying_web_process.revision
54+
)
55+
end
56+
end
57+
end
58+
end
59+
end
60+
end
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
require 'cloud_controller/deployment_updater/actions/scale_down_superseded'
2+
require 'cloud_controller/deployment_updater/actions/scale_down_old_process'
3+
require 'cloud_controller/deployment_updater/actions/finalize'
4+
require 'cloud_controller/deployment_updater/calculators/all_instances_routable'
5+
6+
module VCAP::CloudController
7+
module DeploymentUpdater
8+
module Actions
9+
class Scale
10+
attr_reader :deployment, :logger, :app
11+
12+
def initialize(deployment, logger)
13+
@deployment = deployment
14+
@logger = logger
15+
@app = deployment.app
16+
end
17+
18+
def call
19+
deployment.db.transaction do
20+
app.lock!
21+
22+
return unless deployment.lock!.state == DeploymentModel::DEPLOYING_STATE
23+
24+
ScaleDownSuperseded.new(deployment).call
25+
26+
oldest_web_process_with_instances.lock!
27+
deploying_web_process.lock!
28+
29+
return unless Calculators::AllInstancesRoutable.new(deployment, logger).call
30+
31+
deployment.update(
32+
last_healthy_at: Time.now,
33+
state: DeploymentModel::DEPLOYING_STATE,
34+
status_value: DeploymentModel::ACTIVE_STATUS_VALUE,
35+
status_reason: DeploymentModel::DEPLOYING_STATUS_REASON
36+
)
37+
38+
if deploying_web_process.instances >= deployment.original_web_process_instance_count
39+
Finalize.new(deployment).call
40+
return
41+
end
42+
43+
ScaleDownOldProcess.new(deployment).call
44+
45+
instances_to_scale = [deploying_web_process.instances + deployment.max_in_flight, deployment.original_web_process_instance_count].min
46+
deploying_web_process.update(instances: instances_to_scale)
47+
end
48+
end
49+
50+
private
51+
52+
def oldest_web_process_with_instances
53+
@oldest_web_process_with_instances ||= app.web_processes.select { |process| process.instances > 0 }.min_by { |p| [p.created_at, p.id] }
54+
end
55+
56+
def deploying_web_process
57+
@deploying_web_process ||= deployment.deploying_web_process
58+
end
59+
end
60+
end
61+
end
62+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module VCAP::CloudController
2+
module DeploymentUpdater
3+
module Actions
4+
class ScaleDownOldProcess
5+
attr_reader :deployment, :app
6+
7+
def initialize(deployment)
8+
@deployment = deployment
9+
@app = deployment.app
10+
end
11+
12+
def call
13+
process = oldest_web_process_with_instances
14+
15+
if process.instances <= deployment.max_in_flight && is_interim_process?(process)
16+
process.destroy
17+
return
18+
end
19+
20+
process.update(instances: [(process.instances - deployment.max_in_flight), 0].max)
21+
end
22+
23+
private
24+
25+
def oldest_web_process_with_instances
26+
@oldest_web_process_with_instances ||= app.web_processes.select { |process| process.instances > 0 }.min_by { |p| [p.created_at, p.id] }
27+
end
28+
29+
def is_original_web_process?(process)
30+
process == app.oldest_web_process
31+
end
32+
33+
def is_interim_process?(process)
34+
!is_original_web_process?(process)
35+
end
36+
end
37+
end
38+
end
39+
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module VCAP::CloudController
2+
module DeploymentUpdater
3+
module Actions
4+
class ScaleDownSuperseded
5+
attr_reader :deployment
6+
7+
def initialize(deployment)
8+
@deployment = deployment
9+
end
10+
11+
def call
12+
superseded_processes.each { |p| p.lock!.update(instances: 0) }
13+
end
14+
15+
private
16+
17+
def superseded_processes
18+
# Find interim web processes that (a) belong to a SUPERSEDED (CANCELED) deployment and (b) have instances
19+
# and scale them to zero.
20+
deployment.app.web_processes_dataset.
21+
qualify.
22+
join(:deployments, deploying_web_process_guid: :guid).
23+
where(deployments__state: DeploymentModel::CANCELED_STATE).
24+
where(deployments__status_reason: DeploymentModel::SUPERSEDED_STATUS_REASON).
25+
where(Sequel[:processes__instances] > 0)
26+
end
27+
end
28+
end
29+
end
30+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module VCAP::CloudController
2+
module DeploymentUpdater
3+
module Calculators
4+
class AllInstancesRoutable
5+
attr_reader :deployment, :logger
6+
7+
def initialize(deployment, logger)
8+
@deployment = deployment
9+
@logger = logger
10+
end
11+
12+
def call
13+
instances = instance_reporters.all_instances_for_app(deployment.deploying_web_process)
14+
instances.all? { |_, val| val[:state] == VCAP::CloudController::Diego::LRP_RUNNING && val[:routable] }
15+
rescue CloudController::Errors::ApiError # the instances_reporter re-raises InstancesUnavailable as ApiError
16+
logger.info("skipping-deployment-update-for-#{deployment.guid}")
17+
false
18+
end
19+
20+
def instance_reporters
21+
CloudController::DependencyLocator.instance.instances_reporters
22+
end
23+
end
24+
end
25+
end
26+
end

0 commit comments

Comments
 (0)