Associate clusters model to groups
Even though we currently only should have one group for a cluster, we allow the flexibility to associate to other groups in the future. This also matches the runner <=> groups association. - Adds Cluster#first_group, aliased to Cluster#group. For the conceivable future, a cluster will have at most one group. - Prevent mixing of group and project clusters. If project type clusters, it should only have projects assigned. Similarly with groups. - Default cluster_type to :project_type. As it's very small table we can set default and null: false in one release.
This commit is contained in:
parent
b868b02c62
commit
0e15eec86d
13 changed files with 188 additions and 3 deletions
|
@ -20,6 +20,12 @@ module Clusters
|
|||
has_many :cluster_projects, class_name: 'Clusters::Project'
|
||||
has_many :projects, through: :cluster_projects, class_name: '::Project'
|
||||
|
||||
has_many :cluster_groups, class_name: 'Clusters::Group'
|
||||
has_many :groups, through: :cluster_groups, class_name: '::Group'
|
||||
|
||||
has_one :cluster_group, -> { order(id: :desc) }, class_name: 'Clusters::Group'
|
||||
has_one :group, through: :cluster_group, class_name: '::Group'
|
||||
|
||||
# we force autosave to happen when we save `Cluster` model
|
||||
has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true
|
||||
|
||||
|
@ -38,8 +44,12 @@ module Clusters
|
|||
accepts_nested_attributes_for :platform_kubernetes, update_only: true
|
||||
|
||||
validates :name, cluster_name: true
|
||||
validates :cluster_type, presence: true
|
||||
validate :restrict_modification, on: :update
|
||||
|
||||
validate :no_groups, unless: :group_type?
|
||||
validate :no_projects, unless: :project_type?
|
||||
|
||||
delegate :status, to: :provider, allow_nil: true
|
||||
delegate :status_reason, to: :provider, allow_nil: true
|
||||
delegate :on_creation?, to: :provider, allow_nil: true
|
||||
|
@ -50,6 +60,12 @@ module Clusters
|
|||
delegate :available?, to: :application_ingress, prefix: true, allow_nil: true
|
||||
delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true
|
||||
|
||||
enum cluster_type: {
|
||||
instance_type: 1,
|
||||
group_type: 2,
|
||||
project_type: 3
|
||||
}
|
||||
|
||||
enum platform_type: {
|
||||
kubernetes: 1
|
||||
}
|
||||
|
@ -122,5 +138,17 @@ module Clusters
|
|||
|
||||
true
|
||||
end
|
||||
|
||||
def no_groups
|
||||
if groups.any?
|
||||
errors.add(:cluster, 'cannot have groups assigned')
|
||||
end
|
||||
end
|
||||
|
||||
def no_projects
|
||||
if projects.any?
|
||||
errors.add(:cluster, 'cannot have projects assigned')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
10
app/models/clusters/group.rb
Normal file
10
app/models/clusters/group.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Clusters
|
||||
class Group < ActiveRecord::Base
|
||||
self.table_name = 'cluster_groups'
|
||||
|
||||
belongs_to :cluster, class_name: 'Clusters::Cluster'
|
||||
belongs_to :group, class_name: '::Group'
|
||||
end
|
||||
end
|
|
@ -42,6 +42,7 @@ module DeploymentPlatform
|
|||
{
|
||||
name: 'kubernetes-template',
|
||||
projects: [self],
|
||||
cluster_type: :project_type,
|
||||
provider_type: :user,
|
||||
platform_type: :kubernetes,
|
||||
platform_kubernetes_attributes: platform_kubernetes_attributes_from_service_template
|
||||
|
|
|
@ -41,6 +41,9 @@ class Group < Namespace
|
|||
has_many :boards
|
||||
has_many :badges, class_name: 'GroupBadge'
|
||||
|
||||
has_many :cluster_groups, class_name: 'Clusters::Group'
|
||||
has_many :clusters, through: :cluster_groups, class_name: 'Clusters::Cluster'
|
||||
|
||||
has_many :todos
|
||||
|
||||
accepts_nested_attributes_for :variables, allow_destroy: true
|
||||
|
|
|
@ -9,9 +9,9 @@ module Clusters
|
|||
end
|
||||
|
||||
def execute(project:, access_token: nil)
|
||||
raise ArgumentError.new(_('Instance does not support multiple Kubernetes clusters')) unless can_create_cluster?(project)
|
||||
raise ArgumentError, _('Instance does not support multiple Kubernetes clusters') unless can_create_cluster?(project)
|
||||
|
||||
cluster_params = params.merge(user: current_user, projects: [project])
|
||||
cluster_params = params.merge(user: current_user, cluster_type: :project_type, projects: [project])
|
||||
cluster_params[:provider_gcp_attributes].try do |provider|
|
||||
provider[:access_token] = access_token
|
||||
end
|
||||
|
|
5
changelogs/unreleased/34758-create-group-clusters.yml
Normal file
5
changelogs/unreleased/34758-create-group-clusters.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adds model and migrations to enable group level clusters
|
||||
merge_request: 22307
|
||||
author:
|
||||
type: other
|
17
db/migrate/20181014203236_create_cluster_groups.rb
Normal file
17
db/migrate/20181014203236_create_cluster_groups.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateClusterGroups < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
create_table :cluster_groups do |t|
|
||||
t.references :cluster, null: false, foreign_key: { on_delete: :cascade }
|
||||
t.references :group, null: false, index: true
|
||||
|
||||
t.index [:cluster_id, :group_id], unique: true
|
||||
t.foreign_key :namespaces, column: :group_id, on_delete: :cascade
|
||||
end
|
||||
end
|
||||
end
|
18
db/migrate/20181017001059_add_cluster_type_to_clusters.rb
Normal file
18
db/migrate/20181017001059_add_cluster_type_to_clusters.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddClusterTypeToClusters < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
PROJECT_CLUSTER_TYPE = 3
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_column_with_default(:clusters, :cluster_type, :smallint, default: PROJECT_CLUSTER_TYPE)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column(:clusters, :cluster_type)
|
||||
end
|
||||
end
|
13
db/schema.rb
13
db/schema.rb
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20181016152238) do
|
||||
ActiveRecord::Schema.define(version: 20181017001059) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -599,6 +599,14 @@ ActiveRecord::Schema.define(version: 20181016152238) do
|
|||
|
||||
add_index "ci_variables", ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree
|
||||
|
||||
create_table "cluster_groups", force: :cascade do |t|
|
||||
t.integer "cluster_id", null: false
|
||||
t.integer "group_id", null: false
|
||||
end
|
||||
|
||||
add_index "cluster_groups", ["cluster_id", "group_id"], name: "index_cluster_groups_on_cluster_id_and_group_id", unique: true, using: :btree
|
||||
add_index "cluster_groups", ["group_id"], name: "index_cluster_groups_on_group_id", using: :btree
|
||||
|
||||
create_table "cluster_platforms_kubernetes", force: :cascade do |t|
|
||||
t.integer "cluster_id", null: false
|
||||
t.datetime_with_timezone "created_at", null: false
|
||||
|
@ -654,6 +662,7 @@ ActiveRecord::Schema.define(version: 20181016152238) do
|
|||
t.boolean "enabled", default: true
|
||||
t.string "name", null: false
|
||||
t.string "environment_scope", default: "*", null: false
|
||||
t.integer "cluster_type", limit: 2, default: 3, null: false
|
||||
end
|
||||
|
||||
add_index "clusters", ["enabled"], name: "index_clusters_on_enabled", using: :btree
|
||||
|
@ -2372,6 +2381,8 @@ ActiveRecord::Schema.define(version: 20181016152238) do
|
|||
add_foreign_key "ci_triggers", "projects", name: "fk_e3e63f966e", on_delete: :cascade
|
||||
add_foreign_key "ci_triggers", "users", column: "owner_id", name: "fk_e8e10d1964", on_delete: :cascade
|
||||
add_foreign_key "ci_variables", "projects", name: "fk_ada5eb64b3", on_delete: :cascade
|
||||
add_foreign_key "cluster_groups", "clusters", on_delete: :cascade
|
||||
add_foreign_key "cluster_groups", "namespaces", column: "group_id", on_delete: :cascade
|
||||
add_foreign_key "cluster_platforms_kubernetes", "clusters", on_delete: :cascade
|
||||
add_foreign_key "cluster_projects", "clusters", on_delete: :cascade
|
||||
add_foreign_key "cluster_projects", "projects", on_delete: :cascade
|
||||
|
|
|
@ -2,13 +2,28 @@ FactoryBot.define do
|
|||
factory :cluster, class: Clusters::Cluster do
|
||||
user
|
||||
name 'test-cluster'
|
||||
cluster_type :project_type
|
||||
|
||||
trait :instance do
|
||||
cluster_type { Clusters::Cluster.cluster_types[:instance_type] }
|
||||
end
|
||||
|
||||
trait :project do
|
||||
cluster_type { Clusters::Cluster.cluster_types[:project_type] }
|
||||
|
||||
before(:create) do |cluster, evaluator|
|
||||
cluster.projects << create(:project, :repository)
|
||||
end
|
||||
end
|
||||
|
||||
trait :group do
|
||||
cluster_type { Clusters::Cluster.cluster_types[:group_type] }
|
||||
|
||||
before(:create) do |cluster, evalutor|
|
||||
cluster.groups << create(:group)
|
||||
end
|
||||
end
|
||||
|
||||
trait :provided_by_user do
|
||||
provider_type :user
|
||||
platform_type :kubernetes
|
||||
|
|
|
@ -4,7 +4,10 @@ require 'spec_helper'
|
|||
|
||||
describe Clusters::Cluster do
|
||||
it { is_expected.to belong_to(:user) }
|
||||
it { is_expected.to have_many(:cluster_projects) }
|
||||
it { is_expected.to have_many(:projects) }
|
||||
it { is_expected.to have_many(:cluster_groups) }
|
||||
it { is_expected.to have_many(:groups) }
|
||||
it { is_expected.to have_one(:provider_gcp) }
|
||||
it { is_expected.to have_one(:platform_kubernetes) }
|
||||
it { is_expected.to have_one(:application_helm) }
|
||||
|
@ -178,6 +181,53 @@ describe Clusters::Cluster do
|
|||
it { expect(cluster.update(enabled: false)).to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'cluster_type validations' do
|
||||
let(:instance_cluster) { create(:cluster, :instance) }
|
||||
let(:group_cluster) { create(:cluster, :group) }
|
||||
let(:project_cluster) { create(:cluster, :project) }
|
||||
|
||||
it 'validates presence' do
|
||||
cluster = build(:cluster, :project, cluster_type: nil)
|
||||
|
||||
expect(cluster).not_to be_valid
|
||||
expect(cluster.errors.full_messages).to include("Cluster type can't be blank")
|
||||
end
|
||||
|
||||
context 'project_type cluster' do
|
||||
it 'does not allow setting group' do
|
||||
project_cluster.groups << build(:group)
|
||||
|
||||
expect(project_cluster).not_to be_valid
|
||||
expect(project_cluster.errors.full_messages).to include('Cluster cannot have groups assigned')
|
||||
end
|
||||
end
|
||||
|
||||
context 'group_type cluster' do
|
||||
it 'does not allow setting project' do
|
||||
group_cluster.projects << build(:project)
|
||||
|
||||
expect(group_cluster).not_to be_valid
|
||||
expect(group_cluster.errors.full_messages).to include('Cluster cannot have projects assigned')
|
||||
end
|
||||
end
|
||||
|
||||
context 'instance_type cluster' do
|
||||
it 'does not allow setting group' do
|
||||
instance_cluster.groups << build(:group)
|
||||
|
||||
expect(instance_cluster).not_to be_valid
|
||||
expect(instance_cluster.errors.full_messages).to include('Cluster cannot have groups assigned')
|
||||
end
|
||||
|
||||
it 'does not allow setting project' do
|
||||
instance_cluster.projects << build(:project)
|
||||
|
||||
expect(instance_cluster).not_to be_valid
|
||||
expect(instance_cluster.errors.full_messages).to include('Cluster cannot have projects assigned')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#provider' do
|
||||
|
@ -229,6 +279,23 @@ describe Clusters::Cluster do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#group' do
|
||||
subject { cluster.group }
|
||||
|
||||
context 'when cluster belongs to a group' do
|
||||
let(:cluster) { create(:cluster, :group) }
|
||||
let(:group) { cluster.groups.first }
|
||||
|
||||
it { is_expected.to eq(group) }
|
||||
end
|
||||
|
||||
context 'when cluster does not belong to any group' do
|
||||
let(:cluster) { create(:cluster) }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#applications' do
|
||||
set(:cluster) { create(:cluster) }
|
||||
|
||||
|
|
8
spec/models/clusters/group_spec.rb
Normal file
8
spec/models/clusters/group_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Clusters::Group do
|
||||
it { is_expected.to belong_to(:cluster) }
|
||||
it { is_expected.to belong_to(:group) }
|
||||
end
|
|
@ -19,6 +19,8 @@ describe Group do
|
|||
it { is_expected.to have_one(:chat_team) }
|
||||
it { is_expected.to have_many(:custom_attributes).class_name('GroupCustomAttribute') }
|
||||
it { is_expected.to have_many(:badges).class_name('GroupBadge') }
|
||||
it { is_expected.to have_many(:cluster_groups).class_name('Clusters::Group') }
|
||||
it { is_expected.to have_many(:clusters).class_name('Clusters::Cluster') }
|
||||
|
||||
describe '#members & #requesters' do
|
||||
let(:requester) { create(:user) }
|
||||
|
|
Loading…
Reference in a new issue