SPEP Test Run: Create Security::PolicyScheduleTestRun model and database table
Summary
Create a new model Security::PolicyScheduleTestRun and corresponding database table to store test-run results for Scheduled Pipeline Execution Policies.
Background
This table will store the test-run records including the policy, project, user, pipeline reference, state, and any error messages.
Parent MR: !218585 (closed)
Implementation details
Database Migration
# db/migrate/20260113000000_create_security_policy_schedule_test_runs.rb
class CreateSecurityPolicyScheduleTestRuns < Gitlab::Database::Migration[2.3]
milestone '18.9'
disable_ddl_transaction!
def up
create_table :security_policy_schedule_test_runs do |t|
t.bigint :security_policy_id, null: false
t.bigint :project_id, null: false
t.bigint :user_id, null: false
t.bigint :pipeline_id
t.text :error_message, limit: 1000
t.timestamps_with_timezone null: false
end
add_index :security_policy_schedule_test_runs, [:security_policy_id, :project_id, :created_at],
name: 'idx_policy_schedule_test_runs_on_policy_project_created'
add_concurrent_foreign_key :security_policy_schedule_test_runs, :security_policies,
column: :security_policy_id, on_delete: :cascade
add_concurrent_foreign_key :security_policy_schedule_test_runs, :projects,
column: :project_id, on_delete: :cascade
add_concurrent_foreign_key :security_policy_schedule_test_runs, :users,
column: :user_id, on_delete: :cascade
end
def down
drop_table :security_policy_schedule_test_runs
end
end
# db/migrate/20260113000001_add_indexes_to_security_policy_schedule_test_runs.rb
class AddIndexesToSecurityPolicyScheduleTestRuns < Gitlab::Database::Migration[2.3]
milestone '18.9'
disable_ddl_transaction!
def up
add_concurrent_index :security_policy_schedule_test_runs, :user_id,
name: 'idx_security_policy_schedule_test_runs_on_user_id', if_not_exists: true
add_concurrent_index :security_policy_schedule_test_runs, :project_id,
name: 'idx_security_policy_schedule_test_runs_on_project_id', if_not_exists: true
add_concurrent_index :security_policy_schedule_test_runs, :pipeline_id,
name: 'idx_security_policy_schedule_test_runs_on_pipeline_id', if_not_exists: true
end
def down
remove_concurrent_index_by_name :security_policy_schedule_test_runs,
'idx_security_policy_schedule_test_runs_on_user_id', if_exists: true
remove_concurrent_index_by_name :security_policy_schedule_test_runs,
'idx_security_policy_schedule_test_runs_on_project_id', if_exists: true
remove_concurrent_index_by_name :security_policy_schedule_test_runs,
'idx_security_policy_schedule_test_runs_on_pipeline_id', if_exists: true
end
end
# db/migrate/20260115000000_add_state_to_security_policy_schedule_test_runs.rb
class AddStateToSecurityPolicyScheduleTestRuns < Gitlab::Database::Migration[2.3]
milestone '18.9'
def change
add_column :security_policy_schedule_test_runs, :state, :integer, default: 0, null: false
end
end
Model
# ee/app/models/security/policy_schedule_test_run.rb
module Security
class PolicyScheduleTestRun < ApplicationRecord
self.table_name = 'security_policy_schedule_test_runs'
belongs_to :security_policy, class_name: 'Security::Policy'
belongs_to :project
belongs_to :user
belongs_to :pipeline, class_name: 'Ci::Pipeline', optional: true
validates :security_policy, :project, :user, presence: true
validate :security_policy_is_pipeline_execution_schedule_policy
validate :user_is_project_owner
enum :state, { running: 0, complete: 1, failed: 2 }, default: :running
scope :for_policy, ->(policy) { where(security_policy: policy) }
scope :for_project, ->(project) { where(project: project) }
scope :recent, -> { where(created_at: 1.hour.ago..) }
delegate :started_at, :finished_at, :duration, to: :pipeline, allow_nil: true
def pending?
pipeline.nil? || pipeline.created?
end
def running?
pipeline&.running? || pipeline&.pending?
end
def success?
pipeline&.success?
end
def failed?
pipeline&.failed? || error_message.present?
end
def completed?
pipeline&.complete?
end
def duration_seconds
duration&.to_i
end
private
def security_policy_is_pipeline_execution_schedule_policy
return unless security_policy
return if security_policy.type_pipeline_execution_schedule_policy?
errors.add(:security_policy, _('must be a pipeline_execution_schedule_policy'))
end
def user_is_project_owner
return unless user && project
return if project.owner == user || user.can?(:owner_access, project)
errors.add(:user, _('must be a project owner'))
end
end
end
Additional files
# db/docs/security_policy_schedule_test_runs.yml
---
table_name: security_policy_schedule_test_runs
classes:
- Security::PolicyScheduleTestRun
feature_categories:
- security_policy_management
description: Stores test-run results for Scheduled Pipeline Execution Policies
introduced_by_url:
milestone: '18.9'
gitlab_schema: gitlab_main_org
sharding_key:
project_id: projects
table_size: small
# config/gitlab_loose_foreign_keys.yml (add to existing file)
security_policy_schedule_test_runs:
- table: p_ci_pipelines
column: pipeline_id
on_delete: async_nullify
worker_class: LooseForeignKeys::CiPipelinesBuildsCleanupCronWorker
-
db/docs/security_policy_schedule_test_runs.yml- Database dictionary entry -
config/gitlab_loose_foreign_keys.yml- Add loose FK for pipeline_id