Commit 016af097 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 00b8ecb7
...@@ -11,6 +11,8 @@ module Clusters ...@@ -11,6 +11,8 @@ module Clusters
self.table_name = 'clusters_applications_knative' self.table_name = 'clusters_applications_knative'
has_one :serverless_domain_cluster, class_name: 'Serverless::DomainCluster', foreign_key: 'clusters_applications_knative_id', inverse_of: :knative
include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus include ::Clusters::Concerns::ApplicationStatus
include ::Clusters::Concerns::ApplicationVersion include ::Clusters::Concerns::ApplicationVersion
......
...@@ -217,6 +217,23 @@ class Deployment < ApplicationRecord ...@@ -217,6 +217,23 @@ class Deployment < ApplicationRecord
SQL SQL
end end
# Changes the status of a deployment and triggers the correspinding state
# machine events.
def update_status(status)
case status
when 'running'
run
when 'success'
succeed
when 'failed'
drop
when 'canceled'
cancel
else
raise ArgumentError, "The status #{status.inspect} is invalid"
end
end
private private
def ref_path def ref_path
......
...@@ -6,6 +6,7 @@ class PagesDomain < ApplicationRecord ...@@ -6,6 +6,7 @@ class PagesDomain < ApplicationRecord
SSL_RENEWAL_THRESHOLD = 30.days.freeze SSL_RENEWAL_THRESHOLD = 30.days.freeze
enum certificate_source: { user_provided: 0, gitlab_provided: 1 }, _prefix: :certificate enum certificate_source: { user_provided: 0, gitlab_provided: 1 }, _prefix: :certificate
enum domain_type: { instance: 0, group: 1, project: 2 }, _prefix: :domain_type
belongs_to :project belongs_to :project
has_many :acme_orders, class_name: "PagesDomainAcmeOrder" has_many :acme_orders, class_name: "PagesDomainAcmeOrder"
...@@ -25,6 +26,8 @@ class PagesDomain < ApplicationRecord ...@@ -25,6 +26,8 @@ class PagesDomain < ApplicationRecord
validate :validate_intermediates, if: ->(domain) { domain.certificate.present? && domain.certificate_changed? } validate :validate_intermediates, if: ->(domain) { domain.certificate.present? && domain.certificate_changed? }
default_value_for(:auto_ssl_enabled, allow_nil: false) { ::Gitlab::LetsEncrypt.enabled? } default_value_for(:auto_ssl_enabled, allow_nil: false) { ::Gitlab::LetsEncrypt.enabled? }
default_value_for :domain_type, allow_nil: false, value: :project
default_value_for :wildcard, allow_nil: false, value: false
attr_encrypted :key, attr_encrypted :key,
mode: :per_attribute_iv_and_salt, mode: :per_attribute_iv_and_salt,
...@@ -217,6 +220,8 @@ class PagesDomain < ApplicationRecord ...@@ -217,6 +220,8 @@ class PagesDomain < ApplicationRecord
# rubocop: disable CodeReuse/ServiceClass # rubocop: disable CodeReuse/ServiceClass
def update_daemon def update_daemon
return if domain_type_instance?
::Projects::UpdatePagesConfigurationService.new(project).execute ::Projects::UpdatePagesConfigurationService.new(project).execute
end end
# rubocop: enable CodeReuse/ServiceClass # rubocop: enable CodeReuse/ServiceClass
......
# frozen_string_literal: true
module Serverless
class DomainCluster < ApplicationRecord
self.table_name = 'serverless_domain_cluster'
belongs_to :pages_domain
belongs_to :knative, class_name: 'Clusters::Applications::Knative', foreign_key: 'clusters_applications_knative_id'
belongs_to :creator, class_name: 'User', optional: true
validates :pages_domain, :knative, :uuid, presence: true
validates :uuid, uniqueness: true, length: { is: 14 }
end
end
...@@ -110,6 +110,10 @@ class User < ApplicationRecord ...@@ -110,6 +110,10 @@ class User < ApplicationRecord
through: :group_members, through: :group_members,
source: :group source: :group
alias_attribute :masters_groups, :maintainers_groups alias_attribute :masters_groups, :maintainers_groups
has_many :reporter_developer_maintainer_owned_groups,
-> { where(members: { access_level: [Gitlab::Access::REPORTER, Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
through: :group_members,
source: :group
# Projects # Projects
has_many :groups_projects, through: :groups, source: :projects has_many :groups_projects, through: :groups, source: :projects
......
...@@ -11,15 +11,17 @@ module Deployments ...@@ -11,15 +11,17 @@ module Deployments
end end
def execute def execute
create_deployment.tap do |deployment| environment.deployments.build(deployment_attributes).tap do |deployment|
AfterCreateService.new(deployment).execute if deployment.persisted? # Deployment#change_status already saves the model, so we only need to
# call #save ourselves if no status is provided.
if (status = params[:status])
deployment.update_status(status)
else
deployment.save
end
end end
end end
def create_deployment
environment.deployments.create(deployment_attributes)
end
def deployment_attributes def deployment_attributes
# We use explicit parameters here so we never by accident allow parameters # We use explicit parameters here so we never by accident allow parameters
# to be set that one should not be able to set (e.g. the row ID). # to be set that one should not be able to set (e.g. the row ID).
...@@ -31,8 +33,7 @@ module Deployments ...@@ -31,8 +33,7 @@ module Deployments
tag: params[:tag], tag: params[:tag],
sha: params[:sha], sha: params[:sha],
user: current_user, user: current_user,
on_stop: params[:on_stop], on_stop: params[:on_stop]
status: params[:status]
} }
end end
end end
......
...@@ -10,22 +10,7 @@ module Deployments ...@@ -10,22 +10,7 @@ module Deployments
end end
def execute def execute
# A regular update() does not trigger the state machine transitions, which deployment.update_status(params[:status])
# we need to ensure merge requests are linked when changing the status to
# success. To work around this we use this case statment, using the right
# event methods to trigger the transition hooks.
case params[:status]
when 'running'
deployment.run
when 'success'
deployment.succeed
when 'failed'
deployment.drop
when 'canceled'
deployment.cancel
else
false
end
end end
end end
end end
---
title: Create data model for serverless domains
merge_request: 19835
author:
type: added
---
title: Refactor the Deployment model so state machine events are used by both CI and the API
merge_request: 20474
author:
type: fixed
# frozen_string_literal: true
class CreateServerlessDomainCluster < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
create_table :serverless_domain_cluster, id: false, primary_key: :uuid do |t|
t.references :pages_domain, null: false, foreign_key: { on_delete: :cascade }
t.references :clusters_applications_knative, null: false,
foreign_key: { to_table: :clusters_applications_knative, on_delete: :cascade },
index: { name: :idx_serverless_domain_cluster_on_clusters_applications_knative, unique: true }
t.references :creator, name: :created_by, foreign_key: { to_table: :users, on_delete: :nullify }
t.timestamps_with_timezone null: false
t.string :uuid, null: false, limit: 14, primary_key: true
end
end
end
# frozen_string_literal: true
class AddWildcardAndDomainTypeToPagesDomains < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
PROJECT_TYPE = 2
disable_ddl_transaction!
def up
add_column_with_default :pages_domains, :wildcard, :boolean, default: false
add_column_with_default :pages_domains, :domain_type, :integer, limit: 2, default: PROJECT_TYPE
end
def down
remove_column :pages_domains, :wildcard
remove_column :pages_domains, :domain_type
end
end
# frozen_string_literal: true
class AddIndexesToPagesDomainsOnWildcardAndDomainType < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :pages_domains, :wildcard
add_concurrent_index :pages_domains, :domain_type
end
def down
remove_concurrent_index :pages_domains, :wildcard
remove_concurrent_index :pages_domains, :domain_type
end
end
...@@ -2951,13 +2951,17 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do ...@@ -2951,13 +2951,17 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do
t.datetime_with_timezone "certificate_valid_not_before" t.datetime_with_timezone "certificate_valid_not_before"
t.datetime_with_timezone "certificate_valid_not_after" t.datetime_with_timezone "certificate_valid_not_after"
t.integer "certificate_source", limit: 2, default: 0, null: false t.integer "certificate_source", limit: 2, default: 0, null: false
t.boolean "wildcard", default: false, null: false
t.integer "domain_type", limit: 2, default: 2, null: false
t.index ["certificate_source", "certificate_valid_not_after"], name: "index_pages_domains_need_auto_ssl_renewal", where: "(auto_ssl_enabled = true)" t.index ["certificate_source", "certificate_valid_not_after"], name: "index_pages_domains_need_auto_ssl_renewal", where: "(auto_ssl_enabled = true)"
t.index ["domain"], name: "index_pages_domains_on_domain", unique: true t.index ["domain"], name: "index_pages_domains_on_domain", unique: true
t.index ["domain_type"], name: "index_pages_domains_on_domain_type"
t.index ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until" t.index ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until"
t.index ["project_id"], name: "index_pages_domains_on_project_id" t.index ["project_id"], name: "index_pages_domains_on_project_id"
t.index ["remove_at"], name: "index_pages_domains_on_remove_at" t.index ["remove_at"], name: "index_pages_domains_on_remove_at"
t.index ["verified_at", "enabled_until"], name: "index_pages_domains_on_verified_at_and_enabled_until" t.index ["verified_at", "enabled_until"], name: "index_pages_domains_on_verified_at_and_enabled_until"
t.index ["verified_at"], name: "index_pages_domains_on_verified_at" t.index ["verified_at"], name: "index_pages_domains_on_verified_at"
t.index ["wildcard"], name: "index_pages_domains_on_wildcard"
end end
create_table "path_locks", id: :serial, force: :cascade do |t| create_table "path_locks", id: :serial, force: :cascade do |t|
...@@ -3654,6 +3658,17 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do ...@@ -3654,6 +3658,17 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do
t.index ["issue_id"], name: "index_sentry_issues_on_issue_id", unique: true t.index ["issue_id"], name: "index_sentry_issues_on_issue_id", unique: true
end end
create_table "serverless_domain_cluster", primary_key: "uuid", id: :string, limit: 14, force: :cascade do |t|
t.bigint "pages_domain_id", null: false
t.bigint "clusters_applications_knative_id", null: false
t.bigint "creator_id"
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.index ["clusters_applications_knative_id"], name: "idx_serverless_domain_cluster_on_clusters_applications_knative", unique: true
t.index ["creator_id"], name: "index_serverless_domain_cluster_on_creator_id"
t.index ["pages_domain_id"], name: "index_serverless_domain_cluster_on_pages_domain_id"
end
create_table "service_desk_settings", primary_key: "project_id", id: :bigint, default: nil, force: :cascade do |t| create_table "service_desk_settings", primary_key: "project_id", id: :bigint, default: nil, force: :cascade do |t|
t.string "issue_template_key", limit: 255 t.string "issue_template_key", limit: 255
end end
...@@ -4714,6 +4729,9 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do ...@@ -4714,6 +4729,9 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do
add_foreign_key "self_managed_prometheus_alert_events", "environments", on_delete: :cascade add_foreign_key "self_managed_prometheus_alert_events", "environments", on_delete: :cascade
add_foreign_key "self_managed_prometheus_alert_events", "projects", on_delete: :cascade add_foreign_key "self_managed_prometheus_alert_events", "projects", on_delete: :cascade
add_foreign_key "sentry_issues", "issues", on_delete: :cascade add_foreign_key "sentry_issues", "issues", on_delete: :cascade
add_foreign_key "serverless_domain_cluster", "clusters_applications_knative", on_delete: :cascade
add_foreign_key "serverless_domain_cluster", "pages_domains", on_delete: :cascade
add_foreign_key "serverless_domain_cluster", "users", column: "creator_id", on_delete: :nullify
add_foreign_key "service_desk_settings", "projects", on_delete: :cascade add_foreign_key "service_desk_settings", "projects", on_delete: :cascade
add_foreign_key "services", "projects", name: "fk_71cce407f9", on_delete: :cascade add_foreign_key "services", "projects", name: "fk_71cce407f9", on_delete: :cascade
add_foreign_key "slack_integrations", "services", on_delete: :cascade add_foreign_key "slack_integrations", "services", on_delete: :cascade
......
...@@ -446,7 +446,7 @@ module API ...@@ -446,7 +446,7 @@ module API
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
delete ":id" do delete ":id" do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42279') Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/20757')
authenticated_as_admin! authenticated_as_admin!
......
...@@ -5,7 +5,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do ...@@ -5,7 +5,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
include ApiHelpers include ApiHelpers
include HttpIOHelpers include HttpIOHelpers
let(:project) { create(:project, :public) } let(:project) { create(:project, :public, :repository) }
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -511,7 +511,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do ...@@ -511,7 +511,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
def get_show_json def get_show_json
expect { get_show(id: job.id, format: :json) } expect { get_show(id: job.id, format: :json) }
.to change { Gitlab::GitalyClient.get_request_count }.by(1) # ListCommitsByOid .to change { Gitlab::GitalyClient.get_request_count }.by_at_most(2)
end end
def get_show(**extra_params) def get_show(**extra_params)
......
# frozen_string_literal: true
FactoryBot.define do
factory :serverless_domain_cluster, class: Serverless::DomainCluster do
pages_domain { create(:pages_domain) }
knative { create(:clusters_applications_knative) }
creator { create(:user) }
uuid { SecureRandom.hex(7) }
end
end
...@@ -16,6 +16,10 @@ describe Clusters::Applications::Knative do ...@@ -16,6 +16,10 @@ describe Clusters::Applications::Knative do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
end end
describe 'associations' do
it { is_expected.to have_one(:serverless_domain_cluster).class_name('Serverless::DomainCluster').with_foreign_key('clusters_applications_knative_id').inverse_of(:knative) }
end
describe 'when cloud run is enabled' do describe 'when cloud run is enabled' do
let(:cluster) { create(:cluster, :provided_by_gcp, :cloud_run_enabled) } let(:cluster) { create(:cluster, :provided_by_gcp, :cloud_run_enabled) }
let(:knative_cloud_run) { create(:clusters_applications_knative, cluster: cluster) } let(:knative_cloud_run) { create(:clusters_applications_knative, cluster: cluster) }
......
...@@ -474,4 +474,29 @@ describe Deployment do ...@@ -474,4 +474,29 @@ describe Deployment do
end end
end end
end end
context '#update_status' do
let(:deploy) { create(:deployment, status: :running) }
it 'changes the status' do
deploy.update_status('success')
expect(deploy).to be_success
end
it 'schedules SuccessWorker and FinishedWorker when finishing a deploy' do
expect(Deployments::SuccessWorker).to receive(:perform_async)
expect(Deployments::FinishedWorker).to receive(:perform_async)
deploy.update_status('success')
end
it 'updates finished_at when transitioning to a finished status' do
Timecop.freeze do
deploy.update_status('success')
expect(deploy.read_attribute(:finished_at)).to eq(Time.now)
end
end
end
end end
...@@ -175,6 +175,16 @@ describe PagesDomain do ...@@ -175,6 +175,16 @@ describe PagesDomain do
it { is_expected.to validate_presence_of(:verification_code) } it { is_expected.to validate_presence_of(:verification_code) }
end end
describe 'default values' do
it 'defaults wildcard to false' do
expect(subject.wildcard).to eq(false)
end
it 'defaults domain_type to project' do
expect(subject.domain_type).to eq('project')
end
end
describe '#verification_code' do describe '#verification_code' do
subject { pages_domain.verification_code } subject { pages_domain.verification_code }
...@@ -305,6 +315,14 @@ describe PagesDomain do ...@@ -305,6 +315,14 @@ describe PagesDomain do
end end
describe '#update_daemon' do describe '#update_daemon' do
context 'when domain_type is instance' do
it 'does nothing' do
expect(Projects::UpdatePagesConfigurationService).not_to receive(:new)
create(:pages_domain, domain_type: :instance)
end
end
it 'runs when the domain is created' do it 'runs when the domain is created' do
domain = build(:pages_domain) domain = build(:pages_domain)
......
# frozen_string_literal: true
require 'spec_helper'
describe Serverless::DomainCluster do
subject { create(:serverless_domain_cluster) }
describe 'validations' do
it { is_expected.to validate_presence_of(:pages_domain) }
it { is_expected.to validate_presence_of(:knative) }
it { is_expected.to validate_presence_of(:uuid) }
it { is_expected.to validate_uniqueness_of(:uuid) }
it { is_expected.to validate_length_of(:uuid).is_equal_to(14) }
end
describe 'associations' do
it { is_expected.to belong_to(:pages_domain) }
it { is_expected.to belong_to(:knative) }
it { is_expected.to belong_to(:creator).optional }
end
end
...@@ -147,7 +147,7 @@ describe API::Deployments do ...@@ -147,7 +147,7 @@ describe API::Deployments do
expect(response).to have_gitlab_http_status(500) expect(response).to have_gitlab_http_status(500)
end end
it 'links any merged merge requests to the deployment' do it 'links any merged merge requests to the deployment', :sidekiq_inline do
mr = create( mr = create(
:merge_request, :merge_request,
:merged, :merged,
...@@ -199,7 +199,7 @@ describe API::Deployments do ...@@ -199,7 +199,7 @@ describe API::Deployments do
expect(json_response['ref']).to eq('master') expect(json_response['ref']).to eq('master')
end end
it 'links any merged merge requests to the deployment' do it 'links any merged merge requests to the deployment', :sidekiq_inline do
mr = create( mr = create(
:merge_request, :merge_request,
:merged, :merged,
......
...@@ -3,67 +3,54 @@ ...@@ -3,67 +3,54 @@
require 'spec_helper' require 'spec_helper'
describe Deployments::CreateService do describe Deployments::CreateService do
let(:environment) do let(:user) { create(:user) }
double(
:environment,
deployment_platform: double(:platform, cluster_id: 1),
project_id: 2,
id: 3
)
end
let(:user) { double(:user) }
describe '#execute' do describe '#execute' do
let(:service) { described_class.new(environment, user, {}) } let(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) }
it 'does not run the AfterCreateService service if the deployment is not persisted' do
deploy = double(:deployment, persisted?: false)
expect(service) it 'creates a deployment' do
.to receive(:create_deployment) service = described_class.new(
.and_return(deploy) environment,
user,
sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0',
ref: 'master',
tag: false,
status: 'success'
)
expect(Deployments::AfterCreateService) expect(Deployments::SuccessWorker).to receive(:perform_async)
.not_to receive(:new) expect(Deployments::FinishedWorker).to receive(:perform_async)
expect(service.execute).to eq(deploy) expect(service.execute).to be_persisted
end end
it 'runs the AfterCreateService service if the deployment is persisted' do it 'does not change the status if no status is given' do
deploy = double(:deployment, persisted?: true) service = described_class.new(
after_service = double(:after_create_service) environment,
user,
expect(service) sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0',
.to receive(:create_deployment) ref: 'master',
.and_return(deploy) tag: false
)
expect(Deployments::AfterCreateService)
.to receive(:new)
.with(deploy)
.and_return(after_service)
expect(after_service) expect(Deployments::SuccessWorker).not_to receive(:perform_async)
.to receive(:execute) expect(Deployments::FinishedWorker).not_to receive(:perform_async)
expect(service.execute).to eq(deploy) expect(service.execute).to be_persisted
end end
end end
describe '#create_deployment' do describe '#deployment_attributes' do
it 'creates a deployment' do let(:environment) do
environment = build(:environment) double(
service = described_class.new(environment, user, {}) :environment,
deployment_platform: double(:platform, cluster_id: 1),
expect(environment.deployments) project_id: 2,
.to receive(:create) id: 3
.with(an_instance_of(Hash)) )
service.create_deployment
end end
end
describe '#deployment_attributes' do
it 'only includes attributes that we want to persist' do it 'only includes attributes that we want to persist' do
service = described_class.new( service = described_class.new(
environment, environment,
...@@ -72,8 +59,7 @@ describe Deployments::CreateService do ...@@ -72,8 +59,7 @@ describe Deployments::CreateService do
tag: true, tag: true,
sha: '123', sha: '123',
foo: 'bar', foo: 'bar',
on_stop: 'stop', on_stop: 'stop'
status: 'running'
) )
expect(service.deployment_attributes).to eq( expect(service.deployment_attributes).to eq(
...@@ -84,8 +70,7 @@ describe Deployments::CreateService do ...@@ -84,8 +70,7 @@ describe Deployments::CreateService do
tag: true, tag: true,
sha: '123', sha: '123',
user: user, user: user,
on_stop: 'stop', on_stop: 'stop'
status: 'running'
) )
end end
end end
......
...@@ -34,9 +34,9 @@ describe Deployments::UpdateService do ...@@ -34,9 +34,9 @@ describe Deployments::UpdateService do
expect(deploy).to be_canceled expect(deploy).to be_canceled
end end
it 'returns false when the status is not supported' do it 'raises ArgumentError if the status is invalid' do
expect(described_class.new(deploy, status: 'kittens').execute) expect { described_class.new(deploy, status: 'kittens').execute }
.to be_falsey .to raise_error(ArgumentError)
end end
it 'links merge requests when changing the status to success', :sidekiq_inline do it 'links merge requests when changing the status to success', :sidekiq_inline do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment