Skip to content

Commit 12cb8f1

Browse files
committed
[PoC] Implement RFC Improve Stack management
https://github.com/cloudfoundry/community/pull/1220/files
1 parent bb92b9e commit 12cb8f1

File tree

20 files changed

+343
-180
lines changed

20 files changed

+343
-180
lines changed

app/actions/build_create.rb

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def create_and_stage(package:, lifecycle:, metadata: nil, start_after_staging: f
4545
raise InvalidPackage.new('Cannot stage package whose state is not ready.') if package.state != PackageModel::READY_STATE
4646

4747
requested_buildpacks_disabled!(lifecycle)
48+
validate_stack!(lifecycle, package)
4849

4950
staging_details = get_staging_details(package, lifecycle)
5051
staging_details.start_after_staging = start_after_staging
@@ -74,11 +75,13 @@ def create_and_stage(package:, lifecycle:, metadata: nil, start_after_staging: f
7475

7576
Repositories::AppUsageEventRepository.new.create_from_build(build, 'STAGING_STARTED')
7677
app = package.app
77-
Repositories::BuildEventRepository.record_build_create(build,
78-
@user_audit_info,
79-
app.name,
80-
app.space_guid,
81-
app.organization_guid)
78+
Repositories::BuildEventRepository.record_build_create(
79+
build,
80+
@user_audit_info,
81+
app.name,
82+
app.space_guid,
83+
app.organization_guid
84+
)
8285
end
8386

8487
logger.info("build created: #{build.guid}")
@@ -93,6 +96,22 @@ def create_and_stage(package:, lifecycle:, metadata: nil, start_after_staging: f
9396

9497
private
9598

99+
def validate_stack!(lifecycle, package)
100+
return unless lifecycle.type == Lifecycles::BUILDPACK
101+
102+
stack = Stack.find(name: lifecycle.staging_stack)
103+
return unless stack
104+
105+
case stack.state
106+
when 'DEPRECATED'
107+
logger.warn("Stack '#{stack.name}' is deprecated. #{stack.description}")
108+
when 'LOCKED'
109+
raise CloudController::Errors::ApiError.new_from_details('StackLocked', stack.name, stack.description) if package.app.processes.empty?
110+
when 'DISABLED'
111+
raise CloudController::Errors::ApiError.new_from_details('StackDisabled', stack.name, stack.description)
112+
end
113+
end
114+
96115
def requested_buildpacks_disabled!(lifecycle)
97116
return if lifecycle.type == Lifecycles::DOCKER
98117

app/actions/stack_create.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ class Error < ::StandardError
66
def create(message)
77
stack = VCAP::CloudController::Stack.create(
88
name: message.name,
9-
description: message.description
9+
description: message.description,
10+
state: message.state
1011
)
1112

1213
MetadataUpdate.update(stack, message)

app/actions/stack_update.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ def initialize
99

1010
def update(stack, message)
1111
stack.db.transaction do
12+
stack.state = message.state if message.requested?(:state)
13+
stack.save
1214
MetadataUpdate.update(stack, message)
1315
end
1416
@logger.info("Finished updating metadata on stack #{stack.guid}")

app/controllers/v3/stacks_controller.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ def update
5555
stack = StackUpdate.new.update(stack, message)
5656

5757
render status: :ok, json: Presenters::V3::StackPresenter.new(stack)
58+
rescue StackUpdate::InvalidStack => e
59+
unprocessable! e
5860
end
5961

6062
def show_apps

app/messages/stack_create_message.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
module VCAP::CloudController
44
class StackCreateMessage < MetadataBaseMessage
5-
register_allowed_keys %i[name description]
5+
register_allowed_keys %i[name description state]
66

77
validates :name, presence: true, length: { maximum: 250 }
88
validates :description, length: { maximum: 250 }
9+
validates :state, inclusion: { in: %w[ACTIVE DEPRECATED LOCKED DISABLED],
10+
message: 'must be one of [ACTIVE, DEPRECATED, LOCKED, DISABLED]' }, allow_nil: true
911
end
1012
end

app/messages/stack_update_message.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
module VCAP::CloudController
44
class StackUpdateMessage < MetadataBaseMessage
5-
register_allowed_keys []
5+
register_allowed_keys [:state]
66

77
validates_with NoAdditionalKeysValidator
8+
9+
validates :state, inclusion: { in: %w[ACTIVE DEPRECATED LOCKED DISABLED],
10+
message: 'must be one of [ACTIVE, DEPRECATED, LOCKED, DISABLED]' }, allow_nil: true
811
end
912
end

app/models/runtime/stack.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class AppsStillPresentError < StandardError
2626

2727
plugin :serialization
2828

29-
export_attributes :name, :description, :build_rootfs_image, :run_rootfs_image
30-
import_attributes :name, :description, :build_rootfs_image, :run_rootfs_image
29+
export_attributes :name, :description, :build_rootfs_image, :run_rootfs_image, :state
30+
import_attributes :name, :description, :build_rootfs_image, :run_rootfs_image, :state
3131

3232
strip_attributes :name
3333

@@ -43,6 +43,7 @@ def around_save
4343
def validate
4444
validates_presence :name
4545
validates_unique :name
46+
validates_includes %w[ACTIVE DEPRECATED LOCKED DISABLED], :state, allow_missing: true
4647
end
4748

4849
def before_destroy
@@ -95,7 +96,7 @@ def self.populate_from_hash(hash)
9596
stack.set(hash)
9697
Steno.logger('cc.stack').warn('stack.populate.collision', hash) if stack.modified?
9798
else
98-
create(hash.slice('name', 'description', 'build_rootfs_image', 'run_rootfs_image'))
99+
create(hash.slice('name', 'description', 'build_rootfs_image', 'run_rootfs_image', 'state'))
99100
end
100101
end
101102
end

app/presenters/v3/stack_presenter.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def to_hash
1414
description: stack.description,
1515
run_rootfs_image: stack.run_rootfs_image,
1616
build_rootfs_image: stack.build_rootfs_image,
17+
state: stack.state,
1718
default: stack.default?,
1819
metadata: {
1920
labels: hashified_labels(stack.labels),
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Sequel.migration do
2+
change do
3+
alter_table(:stacks) do
4+
add_column :state, String, null: false, default: 'ACTIVE', size: 255
5+
end
6+
end
7+
end

devenv.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,27 @@ help_command() {
1818

1919
# Create a clean development environment
2020
create_command(){
21-
docker-compose -p "" down
21+
docker compose -p "" down
2222
docker buildx bake -f docker-compose.yml &
23-
docker-compose -p "" pull &
23+
docker compose -p "" pull &
2424
wait $(jobs -p)
25-
docker-compose -p "" up -d --build
25+
docker compose -p "" up -d --build
2626
./.devcontainer/scripts/setupDevelopmentEnvironment.sh
2727
}
2828

2929
# Start containers
3030
start_command(){
31-
docker-compose -p "" start
31+
docker compose -p "" start
3232
}
3333

3434
# Stop containers
3535
stop_command(){
36-
docker-compose -p "" stop
36+
docker compose -p "" stop
3737
}
3838

3939
# Remove containers
4040
destroy_command(){
41-
docker-compose -p "" down
41+
docker compose -p "" down
4242
}
4343

4444
# Call Setup IDEs Script
@@ -72,7 +72,7 @@ fi
7272
# Check Prerequisites
7373
export should_exit=0
7474
# Check Path Exists
75-
for p in docker docker-compose ruby bundle mysql psql yq; do
75+
for p in docker ruby bundle mysql psql yq; do
7676
if ! command -v "${p}" >/dev/null 2>&1; then
7777
echo "Error: Dependency \"$p\" is not installed" && export should_exit=1
7878
fi

0 commit comments

Comments
 (0)