app/models/runtime/app_model.rb
require 'cloud_controller/database_uri_generator'
require 'cloud_controller/serializer'
require 'models/helpers/process_types'
require 'hashdiff'
module VCAP::CloudController
class AppModel < Sequel::Model(:apps)
include Serializer
APP_NAME_REGEX = /\A[[:alnum:][:punct:][:print:]]+\Z/
many_to_many :routes, join_table: :route_mappings, left_key: :app_guid, left_primary_key: :guid, right_primary_key: :guid, right_key: :route_guid
one_to_many :route_mappings, class: 'VCAP::CloudController::RouteMappingModel', key: :app_guid, primary_key: :guid
one_to_many :service_bindings, key: :app_guid, primary_key: :guid
one_to_many :tasks, class: 'VCAP::CloudController::TaskModel', key: :app_guid, primary_key: :guid
many_to_one :space, class: 'VCAP::CloudController::Space', key: :space_guid, primary_key: :guid, without_guid_generation: true
one_through_one :organization, join_table: Space.table_name, left_key: :guid, left_primary_key: :space_guid, right_primary_key: :id, right_key: :organization_id
one_to_many :processes, class: 'VCAP::CloudController::ProcessModel', key: :app_guid, primary_key: :guid do |dataset|
dataset.order(Sequel.asc(:created_at), Sequel.asc(:id))
end
one_to_many :packages, class: 'VCAP::CloudController::PackageModel', key: :app_guid, primary_key: :guid
one_to_many :droplets, class: 'VCAP::CloudController::DropletModel', key: :app_guid, primary_key: :guid
one_to_many :builds, class: 'VCAP::CloudController::BuildModel', key: :app_guid, primary_key: :guid
one_to_many :deployments, class: 'VCAP::CloudController::DeploymentModel', key: :app_guid, primary_key: :guid
one_to_many :labels, class: 'VCAP::CloudController::AppLabelModel', key: :resource_guid, primary_key: :guid
one_to_many :annotations, class: 'VCAP::CloudController::AppAnnotationModel', key: :resource_guid, primary_key: :guid
one_to_many :revisions,
class: 'VCAP::CloudController::RevisionModel',
key: :app_guid,
primary_key: :guid,
order: [Sequel.asc(:created_at), Sequel.asc(:id)]
one_to_many :sidecars, class: 'VCAP::CloudController::SidecarModel', key: :app_guid, primary_key: :guid
many_to_one :droplet, class: 'VCAP::CloudController::DropletModel', key: :droplet_guid, primary_key: :guid, without_guid_generation: true
one_to_many :web_processes,
class: 'VCAP::CloudController::ProcessModel',
key: :app_guid,
primary_key: :guid,
conditions: { type: ProcessTypes::WEB } do |dataset|
dataset.order(Sequel.asc(:created_at), Sequel.asc(:id))
end
one_to_one :buildpack_lifecycle_data,
class: 'VCAP::CloudController::BuildpackLifecycleDataModel',
key: :app_guid,
primary_key: :guid
one_to_one :kpack_lifecycle_data,
class: 'VCAP::CloudController::KpackLifecycleDataModel',
key: :app_guid,
primary_key: :guid
one_to_one :cnb_lifecycle_data,
class: 'VCAP::CloudController::CNBLifecycleDataModel',
key: :app_guid,
primary_key: :guid
set_field_as_encrypted :environment_variables, column: :encrypted_environment_variables
serializes_via_json :environment_variables
add_association_dependencies buildpack_lifecycle_data: :destroy
add_association_dependencies kpack_lifecycle_data: :destroy
add_association_dependencies cnb_lifecycle_data: :destroy
add_association_dependencies labels: :destroy
add_association_dependencies annotations: :destroy
strip_attributes :name
plugin :after_initialize
def after_initialize
self.enable_ssh = Config.config.get(:default_app_ssh_access) if enable_ssh.nil?
end
def around_save
yield
rescue Sequel::UniqueConstraintViolation => e
raise e unless e.message.include?('apps_v3_space_guid_name_index')
errors.add(%i[space_guid name], :unique)
raise validation_failed_error
rescue Sequel::ForeignKeyConstraintViolation => e
raise e unless e.message.include?('fk_apps_droplet_guid')
errors.add(:droplet, :presence)
raise validation_failed_error
end
def validate
super
validates_presence :name
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
return BuildpackLifecycleDataModel::LIFECYCLE_TYPE if buildpack_lifecycle_data
return CNBLifecycleDataModel::LIFECYCLE_TYPE if cnb_lifecycle_data
DockerLifecycleDataModel::LIFECYCLE_TYPE
end
def lifecycle_data
return buildpack_lifecycle_data if buildpack_lifecycle_data
return cnb_lifecycle_data if cnb_lifecycle_data
DockerLifecycleDataModel.new
end
def current_package
droplet&.package
end
def database_uri
service_binding_uris = service_bindings.map do |binding|
binding.credentials['uri'] if binding.credentials.present?
end.compact
DatabaseUriGenerator.new(service_binding_uris).database_uri
end
def staging_in_progress?
builds_dataset.where(state: BuildModel::STAGING_STATE).any?
end
def docker?
lifecycle_type == DockerLifecycleDataModel::LIFECYCLE_TYPE
end
def buildpack?
lifecycle_type == BuildpackLifecycleDataModel::LIFECYCLE_TYPE
end
def cnb?
lifecycle_type == CNBLifecycleDataModel::LIFECYCLE_TYPE
end
def stopped?
desired_state == ProcessModel::STOPPED
end
def deploying?
deployments_dataset.where(state: DeploymentModel::PROGRESSING_STATES).any?
end
def self.user_visibility_filter(user)
space_guids = Space.join(:spaces_developers, space_id: :id, user_id: user.id).select(:spaces__guid).
union(Space.join(:spaces_managers, space_id: :id, user_id: user.id).select(:spaces__guid)).
union(Space.join(:spaces_auditors, space_id: :id, user_id: user.id).select(:spaces__guid)).
union(Space.join(:spaces_supporters, space_id: :id, user_id: user.id).select(:spaces__guid)).
union(Space.join(:organizations_managers, organization_id: :organization_id, user_id: user.id).select(:spaces__guid))
{
apps__guid: AppModel.where(space: space_guids.all).select(:guid)
}
end
def oldest_web_process
web_processes.first
end
def newest_web_process
web_processes.last
end
def latest_revision
reload.revisions.last if revisions_enabled
end
def commands_by_process_type
processes.
select { |p| p.type != ProcessTypes::WEB || p == newest_web_process }.
map { |p| [p.type, p.command] }.to_h
end
private
def validate_environment_variables
return unless environment_variables
VCAP::CloudController::Validators::EnvironmentVariablesValidator.
validate_each(self, :environment_variables, environment_variables)
end
def validate_droplet_is_staged
return unless droplet && droplet.state != DropletModel::STAGED_STATE
errors.add(:droplet, 'must be in staged state')
end
end
end