Skip to content

Commit

Permalink
Enable using protected CNBs
Browse files Browse the repository at this point in the history
Co-authored-by: Johannes Dillmann <[email protected]>
Co-authored-by: Ralf Pannemans <[email protected]>
Co-authored-by: Nicolas Bender <[email protected]>
  • Loading branch information
4 people committed Jun 20, 2024
1 parent daffedc commit 035baf5
Show file tree
Hide file tree
Showing 21 changed files with 421 additions and 122 deletions.
4 changes: 3 additions & 1 deletion app/messages/app_manifest_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class AppManifestMessage < BaseMessage
sidecars
stack
timeout
cnb_credentials
]

HEALTH_CHECK_TYPE_MAPPING = { HealthCheckTypes::NONE => HealthCheckTypes::PROCESS }.freeze
Expand Down Expand Up @@ -296,7 +297,8 @@ def cnb_lifecycle_data
type: Lifecycles::CNB,
data: {
buildpacks: requested_buildpacks,
stack: @stack
stack: @stack,
credentials: @cnb_credentials
}.compact
}
end
Expand Down
13 changes: 12 additions & 1 deletion app/messages/buildpack_lifecycle_data_message.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module VCAP::CloudController
class BuildpackLifecycleDataMessage < BaseMessage
register_allowed_keys %i[buildpacks stack]
register_allowed_keys %i[buildpacks stack credentials]

validates_with NoAdditionalKeysValidator

Expand All @@ -13,7 +13,12 @@ class BuildpackLifecycleDataMessage < BaseMessage
array: true,
allow_nil: true

validates :credentials,
hash: true,
allow_nil: true

validate :buildpacks_content
validate :credentials_content

def buildpacks_content
return unless buildpacks.is_a?(Array)
Expand All @@ -31,5 +36,11 @@ def buildpacks_content
errors.add(:buildpacks, 'can only contain strings') if non_string
errors.add(:buildpacks, 'entries must be between 1 and 4096 characters') if length_error
end

def credentials_content
return unless credentials.is_a?(Hash)

errors.add(:credentials, 'credentials value must be a hash') if credentials.any? { |_, v| !v.is_a?(Hash) }
end
end
end
16 changes: 14 additions & 2 deletions app/models/runtime/cnb_lifecycle_data_model.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
require 'cloud_controller/diego/lifecycles/lifecycles'

require 'presenters/helpers/censorship'
module VCAP::CloudController
class CNBLifecycleDataModel < Sequel::Model(:cnb_lifecycle_data)
LIFECYCLE_TYPE = Lifecycles::CNB
set_field_as_encrypted :registry_credentials_json, salt: :credentials_salt, column: :encrypted_registry_credentials_json

many_to_one :droplet,
class: '::VCAP::CloudController::DropletModel',
Expand Down Expand Up @@ -72,7 +73,8 @@ def attributes_from_buildpack(buildpack_name)
def to_hash
{
buildpacks: buildpacks.map { |buildpack| CloudController::UrlSecretObfuscator.obfuscate(buildpack) },
stack: stack
stack: stack,
credentials: credentials && Presenters::Censorship::REDACTED_CREDENTIAL
}
end

Expand All @@ -81,5 +83,15 @@ def validate

errors.add(:lifecycle_data, 'Must be associated with an app OR a build+droplet, but not both')
end

def credentials
return unless registry_credentials_json

Oj.load(registry_credentials_json)
end

def credentials=(creds)
self.registry_credentials_json = Oj.dump(creds)
end
end
end
17 changes: 17 additions & 0 deletions db/migrations/20240619161000_add_cnb_lifecycle_credentials.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Sequel.migration do
up do
# rubocop:disable Migration/IncludeStringSize
add_column :cnb_lifecycle_data, :encrypted_registry_credentials_json, String, if_not_exists: true
# rubocop:enable Migration/IncludeStringSize
add_column :cnb_lifecycle_data, :credentials_salt, String, size: 255, if_not_exists: true
add_column :cnb_lifecycle_data, :encryption_key_label, String, size: 255, if_not_exists: true
add_column :cnb_lifecycle_data, :encryption_iterations, Integer, default: 2048, null: false, if_not_exists: true
end

down do
drop_column :cnb_lifecycle_data, :encrypted_registry_credentials_json, if_exists: true
drop_column :cnb_lifecycle_data, :credentials_salt, if_exists: true
drop_column :cnb_lifecycle_data, :encryption_key_label, if_exists: true
drop_column :cnb_lifecycle_data, :encryption_iterations, if_exists: true
end
end
43 changes: 43 additions & 0 deletions lib/cloud_controller/diego/buildpack/lifecycle_data.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module VCAP::CloudController
module Diego
module Buildpack
class LifecycleData
attr_accessor :app_bits_download_uri, :build_artifacts_cache_download_uri, :build_artifacts_cache_upload_uri,
:buildpacks, :app_bits_checksum, :droplet_upload_uri, :stack, :buildpack_cache_checksum

def message
message = {
app_bits_download_uri:,
build_artifacts_cache_upload_uri:,
droplet_upload_uri:,
buildpacks:,
stack:,
app_bits_checksum:
}
message[:build_artifacts_cache_download_uri] = build_artifacts_cache_download_uri if build_artifacts_cache_download_uri
message[:buildpack_cache_checksum] = buildpack_cache_checksum if buildpack_cache_checksum

schema.validate(message)
message
end

private

def schema
@schema ||= Membrane::SchemaParser.parse do
{
app_bits_download_uri: String,
optional(:build_artifacts_cache_download_uri) => String,
optional(:buildpack_cache_checksum) => String,
build_artifacts_cache_upload_uri: String,
droplet_upload_uri: String,
buildpacks: Array,
stack: String,
app_bits_checksum: Hash
}
end
end
end
end
end
end
6 changes: 5 additions & 1 deletion lib/cloud_controller/diego/buildpack/lifecycle_protocol.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'cloud_controller/diego/lifecycle_data'
require 'cloud_controller/diego/droplet_url_generator'
require 'cloud_controller/diego/lifecycle_protocol'
require 'cloud_controller/diego/buildpack/lifecycle_data'
require 'cloud_controller/diego/buildpack/staging_action_builder'

module VCAP
Expand All @@ -19,6 +19,10 @@ def task_action_builder(config, task)
def desired_lrp_builder(config, process)
DesiredLrpBuilder.new(config, builder_opts(process))
end

def new_lifecycle_data(_)
LifecycleData.new
end
end
end
end
Expand Down
46 changes: 46 additions & 0 deletions lib/cloud_controller/diego/cnb/lifecycle_data.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module VCAP::CloudController
module Diego
module CNB
class LifecycleData
attr_accessor :app_bits_download_uri, :build_artifacts_cache_download_uri, :build_artifacts_cache_upload_uri,
:buildpacks, :app_bits_checksum, :droplet_upload_uri, :stack, :buildpack_cache_checksum,
:credentials

def message
message = {
app_bits_download_uri:,
build_artifacts_cache_upload_uri:,
droplet_upload_uri:,
buildpacks:,
stack:,
app_bits_checksum:
}
message[:build_artifacts_cache_download_uri] = build_artifacts_cache_download_uri if build_artifacts_cache_download_uri
message[:buildpack_cache_checksum] = buildpack_cache_checksum if buildpack_cache_checksum
message[:credentials] = credentials if credentials

schema.validate(message)
message
end

private

def schema
@schema ||= Membrane::SchemaParser.parse do
{
app_bits_download_uri: String,
optional(:build_artifacts_cache_download_uri) => String,
optional(:buildpack_cache_checksum) => String,
build_artifacts_cache_upload_uri: String,
droplet_upload_uri: String,
buildpacks: Array,
stack: String,
app_bits_checksum: Hash,
optional(:credentials) => String
}
end
end
end
end
end
end
9 changes: 8 additions & 1 deletion lib/cloud_controller/diego/cnb/lifecycle_protocol.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'cloud_controller/diego/buildpack_entry_generator'
require 'cloud_controller/diego/droplet_url_generator'
require 'cloud_controller/diego/lifecycle_data'
require 'cloud_controller/diego/lifecycle_protocol'
require 'cloud_controller/diego/cnb/lifecycle_data'
require 'cloud_controller/diego/cnb/staging_action_builder'
require 'cloud_controller/diego/buildpack/task_action_builder'

Expand All @@ -21,6 +21,13 @@ def task_action_builder(config, task)
def desired_lrp_builder(config, process)
DesiredLrpBuilder.new(config, builder_opts(process))
end

def new_lifecycle_data(staging_details)
lifecycle_data = LifecycleData.new
lifecycle_data.credentials = staging_details.lifecycle.credentials

lifecycle_data
end
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/cloud_controller/diego/cnb/staging_action_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ def cached_dependencies
end

def task_environment_variables
[
env = [
::Diego::Bbs::Models::EnvironmentVariable.new(name: 'CNB_USER_ID', value: '2000'),
::Diego::Bbs::Models::EnvironmentVariable.new(name: 'CNB_GROUP_ID', value: '2000'),
::Diego::Bbs::Models::EnvironmentVariable.new(name: 'CNB_STACK_ID', value: lifecycle_stack),
::Diego::Bbs::Models::EnvironmentVariable.new(name: 'LANG', value: STAGING_DEFAULT_LANG)
]
env.push(::Diego::Bbs::Models::EnvironmentVariable.new(name: 'CNB_REGISTRY_CREDS', value: lifecycle_data[:credentials])) if lifecycle_data[:credentials]
env
end

private
Expand Down
41 changes: 0 additions & 41 deletions lib/cloud_controller/diego/lifecycle_data.rb

This file was deleted.

2 changes: 1 addition & 1 deletion lib/cloud_controller/diego/lifecycle_protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def initialize(blobstore_url_generator=::CloudController::DependencyLocator.inst

def lifecycle_data(staging_details)
stack = staging_details.lifecycle.staging_stack
lifecycle_data = Diego::LifecycleData.new
lifecycle_data = new_lifecycle_data(staging_details)
lifecycle_data.app_bits_download_uri = @blobstore_url_generator.package_download_url(staging_details.package)
lifecycle_data.app_bits_checksum = staging_details.package.checksum_info
lifecycle_data.buildpack_cache_checksum = staging_details.package.app.buildpack_cache_sha256_checksum
Expand Down
11 changes: 11 additions & 0 deletions lib/cloud_controller/diego/lifecycles/app_cnb_lifecycle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def create_lifecycle_data_model(app)
CNBLifecycleDataModel.create(
buildpacks:,
stack:,
credentials:,
app:
)
end
Expand All @@ -22,8 +23,18 @@ def errors
[]
end

def update_lifecycle_data_credentials(app)
return unless message.buildpack_data.requested?(:credentials)

app.lifecycle_data.credentials = message.buildpack_data.credentials
end

def type
Lifecycles::CNB
end

def credentials
message.buildpack_data.credentials
end
end
end
11 changes: 10 additions & 1 deletion lib/cloud_controller/diego/lifecycles/cnb_lifecycle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,27 @@ def create_lifecycle_data_model(build)
VCAP::CloudController::CNBLifecycleDataModel.create(
buildpacks: Array(buildpacks_to_use),
stack: staging_stack,
build: build
build: build,
credentials: credentials_to_use
)
end

def staging_environment_variables
{}
end

def credentials
Oj.dump(credentials_to_use)
end

private

def app_stack
@package.app.cnb_lifecycle_data.try(:stack)
end

def credentials_to_use
staging_message.buildpack_data.credentials || @package.app.lifecycle_data.credentials
end
end
end
26 changes: 26 additions & 0 deletions spec/unit/controllers/v3/apps_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,32 @@
end
end

context 'cnb' do
before do
VCAP::CloudController::FeatureFlag.make(name: 'diego_cnb', enabled: true, error_message: nil)
end

context 'when lifecycle data contains credentials' do
let(:request_body) do
{
name: 'some-name',
relationships: { space: { data: { guid: space.guid } } },
lifecycle: { type: 'cnb', data: { buildpacks: ['http://buildpack.com'], credentials: { registry: { user: 'password' } } } }
}
end

it 'returns a 201 and the app' do
post :create, params: request_body, as: :json

response_body = parsed_body
lifecycle_data = response_body['lifecycle']['data']

expect(response).to have_http_status :created
expect(lifecycle_data).to eq({ 'buildpacks' => ['http://buildpack.com'], 'stack' => 'default-stack-name', 'credentials' => '***' })
end
end
end

context 'when the space does not exist' do
before do
request_body[:relationships][:space][:data][:guid] = 'made-up'
Expand Down
Loading

0 comments on commit 035baf5

Please sign in to comment.