Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions app/models/runtime/app_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def around_save
rescue Sequel::UniqueConstraintViolation => e
raise e unless e.message.include?('apps_v3_space_guid_name_index')

errors.add(%i[space_guid name], :unique)
errors.add(%i[space_guid name], Sequel.lit("App with the name '#{name}' already exists."))
raise validation_failed_error
rescue Sequel::ForeignKeyConstraintViolation => e
raise e unless e.message.include?('fk_apps_droplet_guid')
Expand All @@ -99,8 +99,6 @@ def validate
validates_format APP_NAME_REGEX, :name
validate_environment_variables
validate_droplet_is_staged

validates_unique %i[space_guid name], message: Sequel.lit("App with the name '#{name}' already exists.")
end

def lifecycle_type
Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/domain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ def around_save

def validate
validates_presence :name
validates_unique :name, dataset: Domain.dataset
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with removing this validation is that now another one is triggering (name_overlaps?) which actually checks something else (e.g. "The domain name "foo.com" cannot be created because "bar.foo.com" is already reserved by another domain") and thus results in a wrong error message. There doesn't seem to be a test that verifies that the index-based validation is failing for domains.


validates_format CloudController::DomainDecorator::DOMAIN_REGEX, :name,
message: 'can contain multiple subdomains, each having only alphanumeric characters and hyphens of up to 63 characters, see RFC 1035.'
Expand Down
10 changes: 9 additions & 1 deletion app/models/runtime/feature_flag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,17 @@ class UndefinedFeatureFlagError < StandardError
export_attributes :name, :enabled, :error_message
import_attributes :name, :enabled, :error_message

def around_save
yield
rescue Sequel::UniqueConstraintViolation => e
raise e unless e.message.include?('feature_flags_name_index')

errors.add(:name, :unique)
raise validation_failed_error
end

def validate
validates_presence :name
validates_unique :name
validates_presence :enabled

validates_includes DEFAULT_FLAGS.keys.map(&:to_s), :name
Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/helpers/organization_role_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def around_save
end

def validate
validates_unique %i[organization_id user_id]
validates_presence :organization_id
validates_presence :user_id
end
Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/helpers/space_role_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def around_save
def validate
validates_presence :space_id
validates_presence :user_id
validates_unique %i[space_id user_id]
end
end
end
4 changes: 1 addition & 3 deletions app/models/runtime/isolation_segment_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,12 @@ def around_save
rescue Sequel::UniqueConstraintViolation => e
raise e unless e.message.include?('isolation_segment_name_unique_constraint')

errors.add(:name, :unique)
errors.add(:name, Sequel.lit('Isolation Segment names are case insensitive and must be unique'))
raise validation_failed_error
end

def validate
validates_format ISOLATION_SEGMENT_MODEL_REGEX, :name, message: Sequel.lit('Isolation Segment names can only contain non-blank unicode characters')

validates_unique [:name], message: Sequel.lit('Isolation Segment names are case insensitive and must be unique')
end

def is_shared_segment?
Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,6 @@ def around_save

def validate
validates_presence :name
validates_unique :name
validates_format ORG_NAME_REGEX, :name
validates_includes ORG_STATUS_VALUES, :status, allow_missing: true

Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/quota_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ def around_save

def validate
validates_presence :name
validates_unique :name
validates_presence :non_basic_services_allowed
validates_presence :total_services
validates_presence :total_routes
Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/space.rb
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ def around_save
def validate
validates_presence :name
validates_presence :organization
validates_unique %i[organization_id name]
validates_format SPACE_NAME_REGEX, :name

errors.add(:space_quota_definition, :invalid_organization) if space_quota_definition && space_quota_definition.organization_id != organization.id
Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/space_quota_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def validate
validates_presence :total_routes
validates_presence :memory_limit
validates_presence :organization
validates_unique %i[organization_id name]

validates_limit(:memory_limit, memory_limit)
validates_limit(:instance_memory_limit, instance_memory_limit)
Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/stack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ def around_save

def validate
validates_presence :name
validates_unique :name
validates_includes StackStates::VALID_STATES, :state, allow_missing: true
end

Expand Down
1 change: 0 additions & 1 deletion app/models/runtime/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ def around_save

def validate
validates_presence :guid
validates_unique :guid
end

def validate_organization(org)
Expand Down
3 changes: 1 addition & 2 deletions app/models/services/service_broker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def around_save
rescue Sequel::UniqueConstraintViolation => e
raise e unless e.message.include?('service_brokers_name_index')

errors.add(:name, :unique)
errors.add(:name, Sequel.lit('Name must be unique'))
raise validation_failed_error
end

Expand All @@ -34,7 +34,6 @@ def validate
validates_presence :broker_url
validates_presence :auth_username
validates_presence :auth_password
validates_unique :name, message: Sequel.lit('Name must be unique')
validates_url :broker_url
validates_url_no_basic_auth
end
Expand Down
10 changes: 9 additions & 1 deletion app/models/services/service_dashboard_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ module VCAP::CloudController
class ServiceDashboardClient < Sequel::Model
many_to_one :service_broker

def around_save
yield
rescue Sequel::UniqueConstraintViolation => e
raise e unless e.message.include?('s_d_clients_uaa_id_unique')

errors.add(:uaa_id, :unique)
raise validation_failed_error
end

def validate
validates_presence :uaa_id
validates_unique :uaa_id
end

class << self
Expand Down
10 changes: 9 additions & 1 deletion app/models/services/service_plan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,21 @@ def active?

alias_method :broker_provided_id, :unique_id

def around_save
yield
rescue Sequel::UniqueConstraintViolation => e
raise e unless e.message.include?('svc_plan_svc_id_name_index')

errors.add(%i[name service_id], Sequel.lit("Plan names must be unique within a service. Service #{service.try(:label)} already has a plan named #{name}"))
raise validation_failed_error
end

def validate
validates_presence :name, message: 'is required'
validates_presence :description, message: 'is required'
validates_presence :free, message: 'is required'
validates_presence :service, message: 'is required'
validates_presence :unique_id, message: 'is required'
validates_unique %i[service_id name], message: Sequel.lit("Plan names must be unique within a service. Service #{service.try(:label)} already has a plan named #{name}")
validate_private_broker_plan_not_public
end

Expand Down
10 changes: 9 additions & 1 deletion app/models/services/service_plan_visibility.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ class ServicePlanVisibility < Sequel::Model
import_attributes :service_plan_guid, :organization_guid
export_attributes :service_plan_guid, :organization_guid

def around_save
yield
rescue Sequel::UniqueConstraintViolation => e
raise e unless e.message.include?('spv_org_id_sp_id_index')

errors.add(%i[organization_id service_plan_id], :unique)
raise validation_failed_error
end

def validate
validates_presence :service_plan
validates_presence :organization
validates_unique %i[organization_id service_plan_id]
validate_plan_is_not_private
validate_plan_is_not_public
end
Expand Down
4 changes: 4 additions & 0 deletions lib/services/service_brokers/service_broker_registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def create
raise e
end
self
rescue Sequel::ValidationFailed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment that "errors have been added"...

nil
end

def update
Expand All @@ -50,6 +52,8 @@ def update
synchronize_services_and_plans!
end
self
rescue Sequel::ValidationFailed
nil
end

delegate :errors, to: :broker
Expand Down
7 changes: 3 additions & 4 deletions spec/support/shared_examples/models/domain_validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ module VCAP::CloudController

context "when there's another domain with the same name" do
it 'fails to validate' do
other_domain = described_class.make
other_domain.name = subject.name
expect(other_domain).not_to be_valid
expect(other_domain.errors[:name]).to include(:unique)
expect do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment in app/models/runtime/domain.rb - this now tests something else...

described_class.make(name: subject.name)
end.to raise_error(Sequel::ValidationFailed, /already reserved by another domain|unique/)
end
end

Expand Down
17 changes: 0 additions & 17 deletions spec/unit/actions/app_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,23 +200,6 @@ module VCAP::CloudController
end
end

context 'when creating apps concurrently' do
it 'ensures one creation is successful and the other fails due to name conflict' do
# First request, should succeed
expect do
app_create.create(message, lifecycle)
end.not_to raise_error

# Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation
allow_any_instance_of(AppModel).to receive(:validate).and_return(true)

# Second request, should fail with correct error
expect do
app_create.create(message, lifecycle)
end.to raise_error(CloudController::Errors::V3::ApiError)
end
end

describe 'stack state validation' do
let(:test_stack) { Stack.make(name: 'test-stack-for-validation') }
let(:lifecycle_request) { { type: 'buildpack', data: { buildpacks: [buildpack_identifier], stack: test_stack.name } } }
Expand Down
20 changes: 0 additions & 20 deletions spec/unit/actions/organization_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,26 +110,6 @@ module VCAP::CloudController
end.to raise_error(OrganizationCreate::Error, "Organization '#{name}' already exists.")
end
end

context 'when creating organizations concurrently' do
let(:name) { 'Alfredo' }

it 'ensures one creation is successful and the other fails due to name conflict' do
# First request, should succeed
message = VCAP::CloudController::OrganizationUpdateMessage.new(name:)
expect do
org_create.create(message)
end.not_to raise_error

# Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the complete context could be removed, we don't have to test concurrency if the database enforces uniqueness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented in 3e6166d

allow_any_instance_of(Organization).to receive(:validate).and_return(true)

# Second request, should fail with correct error
expect do
org_create.create(message)
end.to raise_error(OrganizationCreate::Error, "Organization 'Alfredo' already exists.")
end
end
end
end
end
Expand Down
17 changes: 0 additions & 17 deletions spec/unit/actions/service_broker_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,23 +132,6 @@ module CloudController
end
end
end

describe 'when creating a service broker with the same name concurrently' do
it 'ensures one creation is successful and the other fails due to name conflict' do
# First request, should succeed
expect do
action.create(message)
end.not_to raise_error

# Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the complete context could be removed, we don't have to test concurrency if the database enforces uniqueness.

But in this file I don't see a check for the uniqueness error, so this one should be added.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented in 3e6166d

allow_any_instance_of(ServiceBroker).to receive(:validate).and_return(true)

# Second request, should fail with correct error
expect do
action.create(message)
end.to raise_error(V3::ServiceBrokerCreate::InvalidServiceBroker)
end
end
end
end
end
20 changes: 0 additions & 20 deletions spec/unit/actions/space_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,26 +79,6 @@ module VCAP::CloudController
end.to raise_error(SpaceCreate::Error, 'Name must be unique per organization')
end
end

context 'when creating spaces concurrently' do
let(:name) { 'Rose' }

it 'ensures one creation is successful and the other fails due to name conflict' do
# First request, should succeed
message = VCAP::CloudController::SpaceCreateMessage.new(name:)
expect do
SpaceCreate.new(user_audit_info:).create(org, message)
end.not_to raise_error

# Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the complete context could be removed, we don't have to test concurrency if the database enforces uniqueness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented in 3e6166d

allow_any_instance_of(Space).to receive(:validate).and_return(true)

# Second request, should fail with correct error
expect do
SpaceCreate.new(user_audit_info:).create(org, message)
end.to raise_error(SpaceCreate::Error, 'Name must be unique per organization')
end
end
end
end
end
Expand Down
17 changes: 0 additions & 17 deletions spec/unit/actions/space_quotas_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,23 +202,6 @@ module VCAP::CloudController
end
end
end

context 'when creating space quota with the same name concurrently' do
it 'ensures one creation is successful and the other fails due to name conflict' do
# First request, should succeed
expect do
space_quotas_create.create(message, organization: org)
end.not_to raise_error

# Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the complete context could be removed, we don't have to test concurrency if the database enforces uniqueness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented in 3e6166d

allow_any_instance_of(SpaceQuotaDefinition).to receive(:validate).and_return(true)

# Second request, should fail with correct error
expect do
space_quotas_create.create(message, organization: org)
end.to raise_error(SpaceQuotasCreate::Error, "Space Quota 'my-name' already exists.")
end
end
end
end
end
20 changes: 0 additions & 20 deletions spec/unit/actions/stack_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,26 +119,6 @@ module VCAP::CloudController
end.to raise_error(StackCreate::Error, 'Name must be unique')
end
end

context 'when creating stack with the same name concurrently' do
let(:name) { 'Gaby' }

it 'ensures one creation is successful and the other fails due to name conflict' do
message = VCAP::CloudController::StackCreateMessage.new(name:)
# First request, should succeed
expect do
stack_create.create(message)
end.not_to raise_error

# Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the complete context could be removed, we don't have to test concurrency if the database enforces uniqueness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented in 3e6166d

allow_any_instance_of(Stack).to receive(:validate).and_return(true)

# Second request, should fail with correct error
expect do
stack_create.create(message)
end.to raise_error(StackCreate::Error, 'Name must be unique')
end
end
end
end
end
Loading
Loading