Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiger Watson <twatson@gitlab.com>2019-08-07 07:40:29 +0300
committerThong Kuah <tkuah@gitlab.com>2019-08-07 07:40:29 +0300
commit36a01a88ce4c35f3d2b455c7943eeb9649b51163 (patch)
treee568be9b9b80626b60f8e0e445ea95ee570e9523 /spec/models
parent54377159730c676bd40b64e66acfb57faf90eabf (diff)
Use separate Kubernetes namespaces per environment
Kubernetes deployments on new clusters will now have a separate namespace per project environment, instead of sharing a single namespace for the project. Behaviour of existing clusters is unchanged. All new functionality is controlled by the :kubernetes_namespace_per_environment feature flag, which is safe to enable/disable at any time.
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/clusters/cluster_spec.rb76
-rw-r--r--spec/models/clusters/kubernetes_namespace_spec.rb84
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb211
-rw-r--r--spec/models/environment_spec.rb59
-rw-r--r--spec/models/project_spec.rb48
5 files changed, 187 insertions, 291 deletions
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 96212d0c864..9afbe6328ca 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -38,11 +38,6 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
it { is_expected.to respond_to :project }
- it do
- expect(subject.knative_services_finder(subject.project))
- .to be_instance_of(Clusters::KnativeServicesFinder)
- end
-
describe '.enabled' do
subject { described_class.enabled }
@@ -534,60 +529,39 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
- describe '#find_or_initialize_kubernetes_namespace_for_project' do
- let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
- let(:project) { cluster.projects.first }
-
- subject { cluster.find_or_initialize_kubernetes_namespace_for_project(project) }
-
- context 'kubernetes namespace exists' do
- context 'with no service account token' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) }
-
- it { is_expected.to eq kubernetes_namespace }
- end
+ describe '#kubernetes_namespace_for' do
+ let(:cluster) { create(:cluster, :group) }
+ let(:environment) { create(:environment) }
- context 'with a service account token' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, project: project, cluster: cluster) }
+ subject { cluster.kubernetes_namespace_for(environment) }
- it { is_expected.to eq kubernetes_namespace }
- end
- end
-
- context 'kubernetes namespace does not exist' do
- it 'initializes a new namespace and sets default values' do
- expect(subject).to be_new_record
- expect(subject.project).to eq project
- expect(subject.cluster).to eq cluster
- expect(subject.namespace).to be_present
- expect(subject.service_account_name).to be_present
- end
+ before do
+ expect(Clusters::KubernetesNamespaceFinder).to receive(:new)
+ .with(cluster, project: environment.project, environment_slug: environment.slug)
+ .and_return(double(execute: persisted_namespace))
end
- context 'a custom scope is provided' do
- let(:scope) { cluster.kubernetes_namespaces.has_service_account_token }
-
- subject { cluster.find_or_initialize_kubernetes_namespace_for_project(project, scope: scope) }
-
- context 'kubernetes namespace exists' do
- context 'with no service account token' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) }
+ context 'a persisted namespace exists' do
+ let(:persisted_namespace) { create(:cluster_kubernetes_namespace) }
- it 'initializes a new namespace and sets default values' do
- expect(subject).to be_new_record
- expect(subject.project).to eq project
- expect(subject.cluster).to eq cluster
- expect(subject.namespace).to be_present
- expect(subject.service_account_name).to be_present
- end
- end
+ it { is_expected.to eq persisted_namespace.namespace }
+ end
- context 'with a service account token' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, project: project, cluster: cluster) }
+ context 'no persisted namespace exists' do
+ let(:persisted_namespace) { nil }
+ let(:namespace_generator) { double }
+ let(:default_namespace) { 'a-default-namespace' }
- it { is_expected.to eq kubernetes_namespace }
- end
+ before do
+ expect(Gitlab::Kubernetes::DefaultNamespace).to receive(:new)
+ .with(cluster, project: environment.project)
+ .and_return(namespace_generator)
+ expect(namespace_generator).to receive(:from_environment_slug)
+ .with(environment.slug)
+ .and_return(default_namespace)
end
+
+ it { is_expected.to eq default_namespace }
end
end
diff --git a/spec/models/clusters/kubernetes_namespace_spec.rb b/spec/models/clusters/kubernetes_namespace_spec.rb
index b5cba80b806..d4e3a0ac84d 100644
--- a/spec/models/clusters/kubernetes_namespace_spec.rb
+++ b/spec/models/clusters/kubernetes_namespace_spec.rb
@@ -24,70 +24,60 @@ RSpec.describe Clusters::KubernetesNamespace, type: :model do
end
end
- describe 'namespace uniqueness validation' do
- let(:cluster_project) { create(:cluster_project) }
- let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace, namespace: 'my-namespace') }
+ describe '.with_environment_slug' do
+ let(:cluster) { create(:cluster, :group) }
+ let(:environment) { create(:environment, slug: slug) }
- subject { kubernetes_namespace }
+ let(:slug) { 'production' }
- context 'when cluster is using the namespace' do
- before do
- create(:cluster_kubernetes_namespace,
- cluster: kubernetes_namespace.cluster,
- namespace: 'my-namespace')
- end
+ subject { described_class.with_environment_slug(slug) }
- it { is_expected.not_to be_valid }
- end
+ context 'there is no associated environment' do
+ let!(:namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, project: environment.project) }
- context 'when cluster is not using the namespace' do
- it { is_expected.to be_valid }
+ it { is_expected.to be_empty }
end
- end
- describe '#set_defaults' do
- let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace) }
- let(:cluster) { kubernetes_namespace.cluster }
- let(:platform) { kubernetes_namespace.platform_kubernetes }
-
- subject { kubernetes_namespace.set_defaults }
-
- describe '#namespace' do
- before do
- platform.update_column(:namespace, namespace)
+ context 'there is an assicated environment' do
+ let!(:namespace) do
+ create(
+ :cluster_kubernetes_namespace,
+ cluster: cluster,
+ project: environment.project,
+ environment: environment
+ )
end
- context 'when platform has a namespace assigned' do
- let(:namespace) { 'platform-namespace' }
-
- it 'copies the namespace' do
- subject
-
- expect(kubernetes_namespace.namespace).to eq('platform-namespace')
- end
+ context 'with a matching slug' do
+ it { is_expected.to eq [namespace] }
end
- context 'when platform does not have namespace assigned' do
- let(:project) { kubernetes_namespace.project }
- let(:namespace) { nil }
- let(:project_slug) { "#{project.path}-#{project.id}" }
-
- it 'fallbacks to project namespace' do
- subject
+ context 'without a matching slug' do
+ let(:environment) { create(:environment, slug: 'staging') }
- expect(kubernetes_namespace.namespace).to eq(project_slug)
- end
+ it { is_expected.to be_empty }
end
end
+ end
- describe '#service_account_name' do
- let(:service_account_name) { "#{kubernetes_namespace.namespace}-service-account" }
+ describe 'namespace uniqueness validation' do
+ let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace, namespace: 'my-namespace') }
- it 'sets a service account name based on namespace' do
- subject
+ subject { kubernetes_namespace }
- expect(kubernetes_namespace.service_account_name).to eq(service_account_name)
+ context 'when cluster is using the namespace' do
+ before do
+ create(:cluster_kubernetes_namespace,
+ cluster: kubernetes_namespace.cluster,
+ environment: kubernetes_namespace.environment,
+ namespace: 'my-namespace')
end
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when cluster is not using the namespace' do
+ it { is_expected.to be_valid }
end
end
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index 5811016ea4d..0c4cf291d20 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -205,192 +205,77 @@ describe Clusters::Platforms::Kubernetes do
it { is_expected.to be_truthy }
end
- describe '#kubernetes_namespace_for' do
- let(:cluster) { create(:cluster, :project) }
- let(:project) { cluster.project }
-
- let(:platform) do
- create(:cluster_platform_kubernetes,
- cluster: cluster,
- namespace: namespace)
- end
-
- subject { platform.kubernetes_namespace_for(project) }
-
- context 'with a namespace assigned' do
- let(:namespace) { 'namespace-123' }
-
- it { is_expected.to eq(namespace) }
-
- context 'kubernetes namespace is present but has no service account token' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
-
- it { is_expected.to eq(namespace) }
- end
- end
-
- context 'with no namespace assigned' do
- let(:namespace) { nil }
-
- context 'when kubernetes namespace is present' do
- let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster) }
-
- before do
- kubernetes_namespace
- end
-
- it { is_expected.to eq(kubernetes_namespace.namespace) }
-
- context 'kubernetes namespace has no service account token' do
- before do
- kubernetes_namespace.update!(namespace: 'old-namespace', service_account_token: nil)
- end
+ describe '#predefined_variables' do
+ let(:project) { create(:project) }
+ let(:cluster) { create(:cluster, :group, platform_kubernetes: platform) }
+ let(:platform) { create(:cluster_platform_kubernetes) }
+ let(:persisted_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) }
- it { is_expected.to eq("#{project.path}-#{project.id}") }
- end
- end
+ let(:environment_name) { 'env/production' }
+ let(:environment_slug) { Gitlab::Slug::Environment.new(environment_name).generate }
- context 'when kubernetes namespace is not present' do
- it { is_expected.to eq("#{project.path}-#{project.id}") }
- end
- end
- end
+ subject { platform.predefined_variables(project: project, environment_name: environment_name) }
- describe '#predefined_variables' do
- let!(:cluster) { create(:cluster, :project, platform_kubernetes: kubernetes) }
- let(:kubernetes) { create(:cluster_platform_kubernetes, api_url: api_url, ca_cert: ca_pem) }
- let(:api_url) { 'https://kube.domain.com' }
- let(:ca_pem) { File.read(Rails.root.join('spec/fixtures/clusters/sample_cert.pem')) }
-
- subject { kubernetes.predefined_variables(project: cluster.project) }
-
- shared_examples 'setting variables' do
- it 'sets the variables' do
- expect(subject).to include(
- { key: 'KUBE_URL', value: api_url, public: true },
- { key: 'KUBE_CA_PEM', value: ca_pem, public: true },
- { key: 'KUBE_CA_PEM_FILE', value: ca_pem, public: true, file: true }
- )
- end
+ before do
+ allow(Clusters::KubernetesNamespaceFinder).to receive(:new)
+ .with(cluster, project: project, environment_slug: environment_slug)
+ .and_return(double(execute: persisted_namespace))
end
- context 'kubernetes namespace is created with no service account token' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
+ it { is_expected.to include(key: 'KUBE_URL', value: platform.api_url, public: true) }
- it_behaves_like 'setting variables'
+ context 'platform has a CA certificate' do
+ let(:ca_pem) { File.read(Rails.root.join('spec/fixtures/clusters/sample_cert.pem')) }
+ let(:platform) { create(:cluster_platform_kubernetes, ca_cert: ca_pem) }
- it 'does not set KUBE_TOKEN' do
- expect(subject).not_to include(
- { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: true }
- )
- end
+ it { is_expected.to include(key: 'KUBE_CA_PEM', value: ca_pem, public: true) }
+ it { is_expected.to include(key: 'KUBE_CA_PEM_FILE', value: ca_pem, public: true, file: true) }
end
- context 'kubernetes namespace is created with service account token' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster) }
-
- it_behaves_like 'setting variables'
+ context 'kubernetes namespace exists' do
+ let(:variable) { Hash(key: :fake_key, value: 'fake_value') }
+ let(:namespace_variables) { Gitlab::Ci::Variables::Collection.new([variable]) }
- it 'sets KUBE_TOKEN' do
- expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true }
- )
+ before do
+ expect(persisted_namespace).to receive(:predefined_variables).and_return(namespace_variables)
end
- context 'the cluster has been set to unmanaged after the namespace was created' do
- before do
- cluster.update!(managed: false)
- end
-
- it_behaves_like 'setting variables'
-
- it 'sets KUBE_TOKEN from the platform' do
- expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: true }
- )
- end
-
- context 'the platform has a custom namespace set' do
- before do
- kubernetes.update!(namespace: 'custom-namespace')
- end
-
- it 'sets KUBE_NAMESPACE from the platform' do
- expect(subject).to include(
- { key: 'KUBE_NAMESPACE', value: kubernetes.namespace, public: true, masked: false }
- )
- end
- end
-
- context 'there is no namespace specified on the platform' do
- let(:project) { cluster.project }
-
- before do
- kubernetes.update!(namespace: nil)
- end
-
- it 'sets KUBE_NAMESPACE to a default for the project' do
- expect(subject).to include(
- { key: 'KUBE_NAMESPACE', value: "#{project.path}-#{project.id}", public: true, masked: false }
- )
- end
- end
- end
+ it { is_expected.to include(variable) }
end
- context 'group level cluster' do
- let!(:cluster) { create(:cluster, :group, platform_kubernetes: kubernetes) }
-
- let(:project) { create(:project, group: cluster.group) }
-
- subject { kubernetes.predefined_variables(project: project) }
-
- context 'no kubernetes namespace for the project' do
- it_behaves_like 'setting variables'
-
- it 'does not return KUBE_TOKEN' do
- expect(subject).not_to include(
- { key: 'KUBE_TOKEN', value: kubernetes.token, public: false }
- )
- end
-
- context 'the cluster is not managed' do
- let!(:cluster) { create(:cluster, :group, :not_managed, platform_kubernetes: kubernetes) }
+ context 'kubernetes namespace does not exist' do
+ let(:persisted_namespace) { nil }
+ let(:namespace) { 'kubernetes-namespace' }
+ let(:kubeconfig) { 'kubeconfig' }
- it_behaves_like 'setting variables'
-
- it 'sets KUBE_TOKEN' do
- expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: true }
- )
- end
- end
+ before do
+ allow(Gitlab::Kubernetes::DefaultNamespace).to receive(:new)
+ .with(cluster, project: project).and_return(double(from_environment_name: namespace))
+ allow(platform).to receive(:kubeconfig).with(namespace).and_return(kubeconfig)
end
- context 'kubernetes namespace exists for the project' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster, project: project) }
+ it { is_expected.not_to include(key: 'KUBE_TOKEN', value: platform.token, public: false, masked: true) }
+ it { is_expected.not_to include(key: 'KUBE_NAMESPACE', value: namespace) }
+ it { is_expected.not_to include(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true) }
- it_behaves_like 'setting variables'
+ context 'cluster is unmanaged' do
+ let(:cluster) { create(:cluster, :group, :not_managed, platform_kubernetes: platform) }
- it 'sets KUBE_TOKEN' do
- expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true }
- )
- end
+ it { is_expected.to include(key: 'KUBE_TOKEN', value: platform.token, public: false, masked: true) }
+ it { is_expected.to include(key: 'KUBE_NAMESPACE', value: namespace) }
+ it { is_expected.to include(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true) }
end
end
- context 'with a domain' do
- let!(:cluster) do
- create(:cluster, :provided_by_gcp, :with_domain,
- platform_kubernetes: kubernetes)
- end
+ context 'cluster variables' do
+ let(:variable) { Hash(key: :fake_key, value: 'fake_value') }
+ let(:cluster_variables) { Gitlab::Ci::Variables::Collection.new([variable]) }
- it 'sets KUBE_INGRESS_BASE_DOMAIN' do
- expect(subject).to include(
- { key: 'KUBE_INGRESS_BASE_DOMAIN', value: cluster.domain, public: true }
- )
+ before do
+ expect(cluster).to receive(:predefined_variables).and_return(cluster_variables)
end
+
+ it { is_expected.to include(variable) }
end
end
@@ -410,7 +295,7 @@ describe Clusters::Platforms::Kubernetes do
end
context 'with valid pods' do
- let(:pod) { kube_pod(environment_slug: environment.slug, namespace: cluster.kubernetes_namespace_for(project), project_slug: project.full_path_slug) }
+ let(:pod) { kube_pod(environment_slug: environment.slug, namespace: cluster.kubernetes_namespace_for(environment), project_slug: project.full_path_slug) }
let(:pod_with_no_terminal) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: "Pending") }
let(:terminals) { kube_terminals(service, pod) }
let(:pods) { [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")] }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index d2e0bed721e..521c4704c87 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -575,6 +575,34 @@ describe Environment, :use_clean_rails_memory_store_caching do
end
end
+ describe '#deployment_namespace' do
+ let(:environment) { create(:environment) }
+
+ subject { environment.deployment_namespace }
+
+ before do
+ allow(environment).to receive(:deployment_platform).and_return(deployment_platform)
+ end
+
+ context 'no deployment platform available' do
+ let(:deployment_platform) { nil }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'deployment platform is available' do
+ let(:cluster) { create(:cluster, :provided_by_user, :project, projects: [environment.project]) }
+ let(:deployment_platform) { cluster.platform }
+
+ it 'retrieves a namespace from the cluster' do
+ expect(cluster).to receive(:kubernetes_namespace_for)
+ .with(environment).and_return('mock-namespace')
+
+ expect(subject).to eq 'mock-namespace'
+ end
+ end
+ end
+
describe '#terminals' do
subject { environment.terminals }
@@ -823,4 +851,35 @@ describe Environment, :use_clean_rails_memory_store_caching do
subject.prometheus_adapter
end
end
+
+ describe '#knative_services_finder' do
+ let(:environment) { create(:environment) }
+
+ subject { environment.knative_services_finder }
+
+ context 'environment has no deployments' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'environment has a deployment' do
+ let!(:deployment) { create(:deployment, :success, environment: environment, cluster: cluster) }
+
+ context 'with no cluster associated' do
+ let(:cluster) { nil }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'with a cluster associated' do
+ let(:cluster) { create(:cluster) }
+
+ it 'calls the service finder' do
+ expect(Clusters::KnativeServicesFinder).to receive(:new)
+ .with(cluster, environment).and_return(:finder)
+
+ is_expected.to eq :finder
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 2e6c81d0a7e..dde766c3813 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2594,45 +2594,33 @@ describe Project do
end
describe '#deployment_variables' do
- context 'when project has no deployment service' do
- let(:project) { create(:project) }
+ let(:project) { create(:project) }
+ let(:environment) { 'production' }
- it 'returns an empty array' do
- expect(project.deployment_variables).to eq []
- end
+ subject { project.deployment_variables(environment: environment) }
+
+ before do
+ expect(project).to receive(:deployment_platform).with(environment: environment)
+ .and_return(deployment_platform)
end
- context 'when project uses mock deployment service' do
- let(:project) { create(:mock_deployment_project) }
+ context 'when project has no deployment platform' do
+ let(:deployment_platform) { nil }
- it 'returns an empty array' do
- expect(project.deployment_variables).to eq []
- end
+ it { is_expected.to eq [] }
end
- context 'when project has a deployment service' do
- context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has not been executed' do
- let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
- let(:project) { cluster.project }
+ context 'when project has a deployment platform' do
+ let(:platform_variables) { %w(platform variables) }
+ let(:deployment_platform) { double }
- it 'does not return variables from this service' do
- expect(project.deployment_variables).not_to include(
- { key: 'KUBE_TOKEN', value: project.deployment_platform.token, public: false, masked: true }
- )
- end
+ before do
+ expect(deployment_platform).to receive(:predefined_variables)
+ .with(project: project, environment_name: environment)
+ .and_return(platform_variables)
end
- context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has been executed' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token) }
- let!(:cluster) { kubernetes_namespace.cluster }
- let(:project) { kubernetes_namespace.project }
-
- it 'returns token from kubernetes namespace' do
- expect(project.deployment_variables).to include(
- { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true }
- )
- end
- end
+ it { is_expected.to eq platform_variables }
end
end