Commit d8a891a9 authored by Dylan Griffith's avatar Dylan Griffith

Merge branch 'environment-tier' into 'master'

Introduce the explicit definition for production environments [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!55471
parents 1cd9ee31 f0a9e340
......@@ -39,6 +39,7 @@ class Environment < ApplicationRecord
before_validation :generate_slug, if: ->(env) { env.slug.blank? }
before_save :set_environment_type
before_save :ensure_environment_tier
after_save :clear_reactive_cache!
validates :name,
......@@ -87,6 +88,7 @@ class Environment < ApplicationRecord
end
scope :for_project, -> (project) { where(project_id: project) }
scope :for_tier, -> (tier) { where(tier: tier).where('tier IS NOT NULL') }
scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
scope :unfoldered, -> { where(environment_type: nil) }
scope :with_rank, -> do
......@@ -94,6 +96,14 @@ class Environment < ApplicationRecord
end
scope :for_id, -> (id) { where(id: id) }
enum tier: {
production: 0,
staging: 1,
testing: 2,
development: 3,
other: 4
}
state_machine :state, initial: :available do
event :start do
transition stopped: :available
......@@ -429,6 +439,24 @@ class Environment < ApplicationRecord
def generate_slug
self.slug = Gitlab::Slug::Environment.new(name).generate
end
def ensure_environment_tier
return unless ::Feature.enabled?(:environment_tier, project, default_enabled: :yaml)
self.tier ||= guess_tier
end
# Guessing the tier of the environment if it's not explicitly specified by users.
# See https://en.wikipedia.org/wiki/Deployment_environment for industry standard deployment environments
def guess_tier
case name
when %r{dev|review|trunk}i then self.class.tiers[:development]
when %r{test|qc}i then self.class.tiers[:testing]
when %r{st(a|)g|mod(e|)l|pre|demo}i then self.class.tiers[:staging]
when %r{pr(o|)d|live}i then self.class.tiers[:production]
else self.class.tiers[:other]
end
end
end
Environment.prepend_if_ee('EE::Environment')
---
title: Add tier column to the environments table
merge_request: 55471
author:
type: added
---
name: environment_tier
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55471
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323166
milestone: '13.10'
type: development
group: group::release
default_enabled: false
# frozen_string_literal: true
class AddTierToEnvironments < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :environments, :tier, :smallint
end
end
def down
with_lock_retries do
remove_column :environments, :tier
end
end
end
# frozen_string_literal: true
class AddIndexToEnvironmentsTier < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
INDEX_NAME = 'index_environments_on_project_id_and_tier'
DOWNTIME = false
def up
add_concurrent_index :environments, [:project_id, :tier], where: 'tier IS NOT NULL', name: INDEX_NAME
end
def down
remove_concurrent_index :environments, :state, name: INDEX_NAME
end
end
6c52ab55814241b37014949976c4f3a0c63bea0a4f9a301735cc9f4c509f433d
\ No newline at end of file
e1641d84828e3d87aea626dbce6b3b2d231d08fcf1475991fe8d11714cdb0af0
\ No newline at end of file
......@@ -12163,7 +12163,8 @@ CREATE TABLE environments (
state character varying DEFAULT 'available'::character varying NOT NULL,
slug character varying NOT NULL,
auto_stop_at timestamp with time zone,
auto_delete_at timestamp with time zone
auto_delete_at timestamp with time zone,
tier smallint
);
CREATE SEQUENCE environments_id_seq
......@@ -22207,6 +22208,8 @@ CREATE UNIQUE INDEX index_environments_on_project_id_and_name ON environments US
CREATE UNIQUE INDEX index_environments_on_project_id_and_slug ON environments USING btree (project_id, slug);
CREATE INDEX index_environments_on_project_id_and_tier ON environments USING btree (project_id, tier) WHERE (tier IS NOT NULL);
CREATE INDEX index_environments_on_project_id_state_environment_type ON environments USING btree (project_id, state, environment_type);
CREATE INDEX index_environments_on_state_and_auto_stop_at ON environments USING btree (state, auto_stop_at) WHERE ((auto_stop_at IS NOT NULL) AND ((state)::text = 'available'::text));
......@@ -15,6 +15,22 @@ FactoryBot.define do
state { :stopped }
end
trait :production do
tier { :production }
end
trait :staging do
tier { :staging }
end
trait :testing do
tier { :testing }
end
trait :development do
tier { :development }
end
trait :with_review_app do |environment|
transient do
ref { 'master' }
......
......@@ -34,6 +34,39 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
describe '.before_save' do
it 'ensures environment tier when a new object is created' do
environment = build(:environment, name: 'gprd', tier: nil)
expect { environment.save }.to change { environment.tier }.from(nil).to('production')
end
it 'ensures environment tier when an existing object is updated' do
environment = create(:environment, name: 'gprd')
environment.update_column(:tier, nil)
expect { environment.stop! }.to change { environment.reload.tier }.from(nil).to('production')
end
it 'does not overwrite the existing environment tier' do
environment = create(:environment, name: 'gprd', tier: :production)
expect { environment.update!(name: 'gstg') }.not_to change { environment.reload.tier }
end
context 'when environment_tier feature flag is disabled' do
before do
stub_feature_flags(environment_tier: false)
end
it 'does not ensure environment tier' do
environment = build(:environment, name: 'gprd', tier: nil)
expect { environment.save }.not_to change { environment.tier }
end
end
end
describe '.order_by_last_deployed_at' do
let!(:environment1) { create(:environment, project: project) }
let!(:environment2) { create(:environment, project: project) }
......@@ -195,6 +228,62 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
describe '.for_tier' do
let_it_be(:environment) { create(:environment, :production) }
it 'returns the production environment when searching for production tier' do
expect(described_class.for_tier(:production)).to eq([environment])
end
it 'returns nothing when searching for staging tier' do
expect(described_class.for_tier(:staging)).to be_empty
end
end
describe '#guess_tier' do
using RSpec::Parameterized::TableSyntax
subject { environment.send(:guess_tier) }
let(:environment) { build(:environment, name: name) }
where(:name, :tier) do
'review/feature' | described_class.tiers[:development]
'review/product' | described_class.tiers[:development]
'DEV' | described_class.tiers[:development]
'development' | described_class.tiers[:development]
'trunk' | described_class.tiers[:development]
'test' | described_class.tiers[:testing]
'TEST' | described_class.tiers[:testing]
'testing' | described_class.tiers[:testing]
'testing-prd' | described_class.tiers[:testing]
'acceptance-testing' | described_class.tiers[:testing]
'QC' | described_class.tiers[:testing]
'gstg' | described_class.tiers[:staging]
'staging' | described_class.tiers[:staging]
'stage' | described_class.tiers[:staging]
'Model' | described_class.tiers[:staging]
'MODL' | described_class.tiers[:staging]
'Pre-production' | described_class.tiers[:staging]
'pre' | described_class.tiers[:staging]
'Demo' | described_class.tiers[:staging]
'gprd' | described_class.tiers[:production]
'gprd-cny' | described_class.tiers[:production]
'production' | described_class.tiers[:production]
'Production' | described_class.tiers[:production]
'prod' | described_class.tiers[:production]
'PROD' | described_class.tiers[:production]
'Live' | described_class.tiers[:production]
'canary' | described_class.tiers[:other]
'other' | described_class.tiers[:other]
'EXP' | described_class.tiers[:other]
end
with_them do
it { is_expected.to eq(tier) }
end
end
describe '#expire_etag_cache' do
let(:store) { Gitlab::EtagCaching::Store.new }
......
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