Commit 979ee931 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'specify-environment-tier' into 'master'

Allow users to specify environment tier via .gitlab-ci.yml

See merge request gitlab-org/gitlab!55723
parents dee82d54 8cb21e77
......@@ -486,6 +486,10 @@ module Ci
self.options.fetch(:environment, {}).fetch(:action, 'start') if self.options
end
def environment_deployment_tier
self.options.dig(:environment, :deployment_tier) if self.options
end
def outdated_deployment?
success? && !deployment.try(:last?)
end
......
......@@ -25,11 +25,10 @@ module Deployments
def update_environment(deployment)
ActiveRecord::Base.transaction do
if (url = expanded_environment_url)
environment.external_url = url
end
# Renew attributes at update
renew_external_url
renew_auto_stop_in
renew_deployment_tier
environment.fire_state_event(action)
if environment.save && !environment.stopped?
......@@ -56,11 +55,25 @@ module Deployments
environment_options[:action] || 'start'
end
def renew_external_url
if (url = expanded_environment_url)
environment.external_url = url
end
end
def renew_auto_stop_in
return unless deployable
environment.auto_stop_in = deployable.environment_auto_stop_in
end
def renew_deployment_tier
return unless deployable && ::Feature.enabled?(:environment_tier, deployable.project, default_enabled: :yaml)
if (tier = deployable.environment_deployment_tier)
environment.tier = tier
end
end
end
end
......
......@@ -10,7 +10,7 @@ module Gitlab
class Environment < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Configurable
ALLOWED_KEYS = %i[name url action on_stop auto_stop_in kubernetes].freeze
ALLOWED_KEYS = %i[name url action on_stop auto_stop_in kubernetes deployment_tier].freeze
entry :kubernetes, Entry::Kubernetes, description: 'Kubernetes deployment configuration.'
......@@ -47,6 +47,11 @@ module Gitlab
inclusion: { in: %w[start stop prepare], message: 'should be start, stop or prepare' },
allow_nil: true
validates :deployment_tier,
type: String,
inclusion: { in: ::Environment.tiers.keys, message: "must be one of #{::Environment.tiers.keys.join(', ')}" },
allow_nil: true
validates :on_stop, type: String, allow_nil: true
validates :kubernetes, type: Hash, allow_nil: true
validates :auto_stop_in, duration: true, allow_nil: true
......@@ -85,6 +90,10 @@ module Gitlab
value[:auto_stop_in]
end
def deployment_tier
value[:deployment_tier]
end
def value
case @config
when String then { name: @config, action: 'start' }
......
......@@ -13,7 +13,9 @@ module Gitlab
def to_resource
environments.safe_find_or_create_by(name: expanded_environment_name) do |environment|
# Initialize the attributes at creation
environment.auto_stop_in = auto_stop_in
environment.tier = deployment_tier if ::Feature.enabled?(:environment_tier, job.project, default_enabled: :yaml)
end
end
......@@ -27,6 +29,10 @@ module Gitlab
job.environment_auto_stop_in
end
def deployment_tier
job.environment_deployment_tier
end
def expanded_environment_name
job.expanded_environment_name
end
......
......@@ -305,4 +305,37 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do
it { expect(entry).to be_valid }
end
end
describe 'deployment_tier' do
let(:config) do
{ name: 'customer-portal', deployment_tier: deployment_tier }
end
context 'is a string' do
let(:deployment_tier) { 'production' }
it { expect(entry).to be_valid }
end
context 'is a hash' do
let(:deployment_tier) { Hash(tier: 'production') }
it { expect(entry).not_to be_valid }
end
context 'is nil' do
let(:deployment_tier) { nil }
it { expect(entry).to be_valid }
end
context 'is unknown value' do
let(:deployment_tier) { 'unknown' }
it 'is invalid and adds an error' do
expect(entry).not_to be_valid
expect(entry.errors).to include("environment deployment tier must be one of #{::Environment.tiers.keys.join(', ')}")
end
end
end
end
......@@ -88,6 +88,65 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
end
end
context 'when job has deployment tier attribute' do
let(:attributes) do
{
environment: 'customer-portal',
options: {
environment: {
name: 'customer-portal',
deployment_tier: deployment_tier
}
}
}
end
let(:deployment_tier) { 'production' }
context 'when environment has not been created yet' do
it 'sets the specified deployment tier' do
is_expected.to be_production
end
context 'when deployment tier is staging' do
let(:deployment_tier) { 'staging' }
it 'sets the specified deployment tier' do
is_expected.to be_staging
end
end
context 'when deployment tier is unknown' do
let(:deployment_tier) { 'unknown' }
it 'raises an error' do
expect { subject }.to raise_error(ArgumentError, "'unknown' is not a valid tier")
end
end
context 'when environment_tier feature flag is disabled' do
before do
stub_feature_flags(environment_tier: false)
end
it 'does not set the specified tier' do
expect(subject.tier).to be_nil
end
end
end
context 'when environment has already been created' do
before do
create(:environment, :staging, project: project, name: 'customer-portal')
end
it 'does not overwrite the specified deployment tier' do
# This is to be updated when a deployment succeeded i.e. Deployments::UpdateEnvironmentService.
is_expected.to be_staging
end
end
end
context 'when job starts a review app' do
let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
let(:expected_environment_name) { "review/#{job.ref}" }
......
......@@ -1261,6 +1261,21 @@ RSpec.describe Ci::Build do
end
end
describe '#environment_deployment_tier' do
subject { build.environment_deployment_tier }
let(:build) { described_class.new(options: options) }
let(:options) { { environment: { deployment_tier: 'production' } } }
it { is_expected.to eq('production') }
context 'when options does not include deployment_tier' do
let(:options) { { environment: { name: 'production' } } }
it { is_expected.to be_nil }
end
end
describe 'deployment' do
describe '#outdated_deployment?' do
subject { build.outdated_deployment? }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::CreatePipelineService do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:developer) { create(:user) }
let(:service) { described_class.new(project, user, ref: 'master') }
let(:user) { developer }
before_all do
project.add_developer(developer)
end
describe '#execute' do
subject { service.execute(:push) }
context 'with deployment tier' do
before do
config = YAML.dump(
deploy: {
script: 'ls',
environment: { name: "review/$CI_COMMIT_REF_NAME", deployment_tier: tier }
})
stub_ci_pipeline_yaml_file(config)
end
let(:tier) { 'development' }
it 'creates the environment with the expected tier' do
is_expected.to be_created_successfully
expect(Environment.find_by_name("review/master")).to be_development
end
context 'when tier is testing' do
let(:tier) { 'testing' }
it 'creates the environment with the expected tier' do
is_expected.to be_created_successfully
expect(Environment.find_by_name("review/master")).to be_testing
end
end
end
end
end
......@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Deployments::UpdateEnvironmentService do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:options) { { name: 'production' } }
let(:options) { { name: environment_name } }
let(:pipeline) do
create(
:ci_pipeline,
......@@ -20,13 +20,14 @@ RSpec.describe Deployments::UpdateEnvironmentService do
pipeline: pipeline,
ref: 'master',
tag: false,
environment: 'production',
environment: environment_name,
options: { environment: options },
project: project)
end
let(:deployment) { job.deployment }
let(:environment) { deployment.environment }
let(:environment_name) { 'production' }
subject(:service) { described_class.new(deployment) }
......@@ -131,6 +132,66 @@ RSpec.describe Deployments::UpdateEnvironmentService do
end
end
end
context 'when deployment tier is specified' do
let(:environment_name) { 'customer-portal' }
let(:options) { { name: environment_name, deployment_tier: 'production' } }
context 'when tier has already been set' do
before do
environment.update_column(:tier, Environment.tiers[:other])
end
it 'overwrites the guessed tier by the specified deployment tier' do
expect { subject.execute }
.to change { environment.reset.tier }.from('other').to('production')
end
end
context 'when tier has not been set' do
before do
environment.update_column(:tier, nil)
end
it 'sets the specified deployment tier' do
expect { subject.execute }
.to change { environment.reset.tier }.from(nil).to('production')
end
context 'when deployment was created by an external CD system' do
before do
deployment.update_column(:deployable_id, nil)
end
it 'guesses the deployment tier' do
expect { subject.execute }
.to change { environment.reset.tier }.from(nil).to('other')
end
end
context 'when environment_tier feature flag is disabled' do
before do
stub_feature_flags(environment_tier: false)
end
it 'does not set the specified deployment tier' do
expect { subject.execute }.not_to change { environment.reset.tier }
end
end
end
end
context 'when deployment tier is not specified' do
let(:environment_name) { 'customer-portal' }
let(:options) { { name: environment_name } }
it 'guesses the deployment tier' do
environment.update_column(:tier, nil)
expect { subject.execute }
.to change { environment.reset.tier }.from(nil).to('other')
end
end
end
describe '#expanded_environment_url' 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