summaryrefslogtreecommitdiff
path: root/spec/models/clusters
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/clusters')
-rw-r--r--spec/models/clusters/applications/cert_manager_spec.rb52
-rw-r--r--spec/models/clusters/applications/helm_spec.rb67
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb16
-rw-r--r--spec/models/clusters/applications/jupyter_spec.rb2
-rw-r--r--spec/models/clusters/applications/knative_spec.rb48
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb17
-rw-r--r--spec/models/clusters/applications/runner_spec.rb33
-rw-r--r--spec/models/clusters/cluster_spec.rb124
-rw-r--r--spec/models/clusters/clusters_hierarchy_spec.rb73
-rw-r--r--spec/models/clusters/kubernetes_namespace_spec.rb84
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb247
11 files changed, 448 insertions, 315 deletions
diff --git a/spec/models/clusters/applications/cert_manager_spec.rb b/spec/models/clusters/applications/cert_manager_spec.rb
index 8d853a04e33..93050e80b07 100644
--- a/spec/models/clusters/applications/cert_manager_spec.rb
+++ b/spec/models/clusters/applications/cert_manager_spec.rb
@@ -3,17 +3,17 @@
require 'rails_helper'
describe Clusters::Applications::CertManager do
- let(:cert_manager) { create(:clusters_applications_cert_managers) }
+ let(:cert_manager) { create(:clusters_applications_cert_manager) }
- include_examples 'cluster application core specs', :clusters_applications_cert_managers
- include_examples 'cluster application status specs', :clusters_applications_cert_managers
- include_examples 'cluster application version specs', :clusters_applications_cert_managers
+ include_examples 'cluster application core specs', :clusters_applications_cert_manager
+ include_examples 'cluster application status specs', :clusters_applications_cert_manager
+ include_examples 'cluster application version specs', :clusters_applications_cert_manager
include_examples 'cluster application initial status specs'
describe '#can_uninstall?' do
subject { cert_manager.can_uninstall? }
- it { is_expected.to be_falsey }
+ it { is_expected.to be_truthy }
end
describe '#install_command' do
@@ -48,7 +48,7 @@ describe Clusters::Applications::CertManager do
expect(subject.version).to eq('v0.5.2')
expect(subject).to be_rbac
expect(subject.files).to eq(cert_manager.files.merge(cluster_issuer_file))
- expect(subject.postinstall).to eq(['/usr/bin/kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml'])
+ expect(subject.postinstall).to eq(['kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml'])
end
context 'for a specific user' do
@@ -72,7 +72,7 @@ describe Clusters::Applications::CertManager do
end
context 'application failed to install previously' do
- let(:cert_manager) { create(:clusters_applications_cert_managers, :errored, version: '0.0.1') }
+ let(:cert_manager) { create(:clusters_applications_cert_manager, :errored, version: '0.0.1') }
it 'is initialized with the locked version' do
expect(subject.version).to eq('v0.5.2')
@@ -80,6 +80,44 @@ describe Clusters::Applications::CertManager do
end
end
+ describe '#uninstall_command' do
+ subject { cert_manager.uninstall_command }
+
+ it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::DeleteCommand) }
+
+ it 'is initialized with cert_manager arguments' do
+ expect(subject.name).to eq('certmanager')
+ expect(subject).to be_rbac
+ expect(subject.files).to eq(cert_manager.files)
+ end
+
+ it 'specifies a post delete command to remove custom resource definitions' do
+ expect(subject.postdelete).to eq([
+ "kubectl delete secret -n gitlab-managed-apps letsencrypt-prod --ignore-not-found",
+ 'kubectl delete crd certificates.certmanager.k8s.io --ignore-not-found',
+ 'kubectl delete crd clusterissuers.certmanager.k8s.io --ignore-not-found',
+ 'kubectl delete crd issuers.certmanager.k8s.io --ignore-not-found'
+ ])
+ end
+
+ context 'secret key name is not found' do
+ before do
+ allow(File).to receive(:read).and_call_original
+ expect(File).to receive(:read)
+ .with(Rails.root.join('vendor', 'cert_manager', 'cluster_issuer.yaml'))
+ .and_return('key: value')
+ end
+
+ it 'does not try and delete the secret' do
+ expect(subject.postdelete).to eq([
+ 'kubectl delete crd certificates.certmanager.k8s.io --ignore-not-found',
+ 'kubectl delete crd clusterissuers.certmanager.k8s.io --ignore-not-found',
+ 'kubectl delete crd issuers.certmanager.k8s.io --ignore-not-found'
+ ])
+ end
+ end
+ end
+
describe '#files' do
let(:application) { cert_manager }
let(:values) { subject[:'values.yaml'] }
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index 6ea6c110d62..00b5c72a3d3 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -19,11 +19,35 @@ describe Clusters::Applications::Helm do
end
describe '#can_uninstall?' do
- let(:helm) { create(:clusters_applications_helm) }
+ context "with other existing applications" do
+ Clusters::Cluster::APPLICATIONS.keys.each do |application_name|
+ next if application_name == 'helm'
+
+ it "is false when #{application_name} is installed" do
+ cluster_application = create("clusters_applications_#{application_name}".to_sym)
+
+ helm = cluster_application.cluster.application_helm
+
+ expect(helm.allowed_to_uninstall?).to be_falsy
+ end
+ end
- subject { helm.can_uninstall? }
+ it 'executes a single query only' do
+ cluster_application = create(:clusters_applications_ingress)
+ helm = cluster_application.cluster.application_helm
- it { is_expected.to be_falsey }
+ query_count = ActiveRecord::QueryRecorder.new { helm.allowed_to_uninstall? }.count
+ expect(query_count).to eq(1)
+ end
+ end
+
+ context "without other existing applications" do
+ subject { helm.can_uninstall? }
+
+ let(:helm) { create(:clusters_applications_helm) }
+
+ it { is_expected.to be_truthy }
+ end
end
describe '#issue_client_cert' do
@@ -73,4 +97,41 @@ describe Clusters::Applications::Helm do
end
end
end
+
+ describe '#uninstall_command' do
+ let(:helm) { create(:clusters_applications_helm) }
+
+ subject { helm.uninstall_command }
+
+ it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::ResetCommand) }
+
+ it 'has name' do
+ expect(subject.name).to eq('helm')
+ end
+
+ it 'has cert files' do
+ expect(subject.files[:'ca.pem']).to be_present
+ expect(subject.files[:'ca.pem']).to eq(helm.ca_cert)
+
+ expect(subject.files[:'cert.pem']).to be_present
+ expect(subject.files[:'key.pem']).to be_present
+
+ cert = OpenSSL::X509::Certificate.new(subject.files[:'cert.pem'])
+ expect(cert.not_after).to be > 999.years.from_now
+ end
+
+ describe 'rbac' do
+ context 'rbac cluster' do
+ it { expect(subject).to be_rbac }
+ end
+
+ context 'non rbac cluster' do
+ before do
+ helm.cluster.platform_kubernetes.abac!
+ end
+
+ it { expect(subject).not_to be_rbac }
+ end
+ end
+ end
end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index 292ddabd2d8..057517d3820 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -21,7 +21,21 @@ describe Clusters::Applications::Ingress do
describe '#can_uninstall?' do
subject { ingress.can_uninstall? }
- it { is_expected.to be_falsey }
+ it 'returns true if application_jupyter_nil_or_installable? AND external_ip_or_hostname? are true' do
+ ingress.external_ip = 'IP'
+
+ is_expected.to be_truthy
+ end
+
+ it 'returns false if application_jupyter_nil_or_installable? is false' do
+ create(:clusters_applications_jupyter, :installed, cluster: ingress.cluster)
+
+ is_expected.to be_falsey
+ end
+
+ it 'returns false if external_ip_or_hostname? is false' do
+ is_expected.to be_falsey
+ end
end
describe '#make_installed!' do
diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb
index 43fa1010b2b..3ff66a074e4 100644
--- a/spec/models/clusters/applications/jupyter_spec.rb
+++ b/spec/models/clusters/applications/jupyter_spec.rb
@@ -16,7 +16,7 @@ describe Clusters::Applications::Jupyter do
subject { jupyter.can_uninstall? }
- it { is_expected.to be_falsey }
+ it { is_expected.to be_truthy }
end
describe '#set_initial_status' do
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index b38cf96de7e..334f10526cb 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -39,7 +39,7 @@ describe Clusters::Applications::Knative do
describe '#can_uninstall?' do
subject { knative.can_uninstall? }
- it { is_expected.to be_falsey }
+ it { is_expected.to be_truthy }
end
describe '#schedule_status_update with external_ip' do
@@ -91,7 +91,7 @@ describe Clusters::Applications::Knative do
end
it 'does not install metrics for prometheus' do
- expect(subject.postinstall).to be_nil
+ expect(subject.postinstall).to be_empty
end
context 'with prometheus installed' do
@@ -101,7 +101,7 @@ describe Clusters::Applications::Knative do
subject { knative.install_command }
it 'installs metrics' do
- expect(subject.postinstall).not_to be_nil
+ expect(subject.postinstall).not_to be_empty
expect(subject.postinstall.length).to be(1)
expect(subject.postinstall[0]).to eql("kubectl apply -f #{Clusters::Applications::Knative::METRICS_CONFIG}")
end
@@ -112,7 +112,7 @@ describe Clusters::Applications::Knative do
subject { knative.install_command }
it 'is initialized with latest version' do
- expect(subject.version).to eq('0.5.0')
+ expect(subject.version).to eq('0.6.0')
end
it_behaves_like 'a command'
@@ -129,6 +129,46 @@ describe Clusters::Applications::Knative do
it_behaves_like 'a command'
end
+ describe '#uninstall_command' do
+ subject { knative.uninstall_command }
+
+ it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::DeleteCommand) }
+
+ it "removes knative deployed services before uninstallation" do
+ 2.times do |i|
+ cluster_project = create(:cluster_project, cluster: knative.cluster)
+
+ create(:cluster_kubernetes_namespace,
+ cluster: cluster_project.cluster,
+ cluster_project: cluster_project,
+ project: cluster_project.project,
+ namespace: "namespace_#{i}")
+ end
+
+ remove_namespaced_services_script = [
+ "kubectl delete ksvc --all -n #{knative.cluster.kubernetes_namespaces.first.namespace}",
+ "kubectl delete ksvc --all -n #{knative.cluster.kubernetes_namespaces.second.namespace}"
+ ]
+
+ expect(subject.predelete).to match_array(remove_namespaced_services_script)
+ end
+
+ it "initializes command with all necessary postdelete script" do
+ api_resources = YAML.safe_load(File.read(Rails.root.join(Clusters::Applications::Knative::API_RESOURCES_PATH)))
+
+ remove_knative_istio_leftovers_script = [
+ "kubectl delete --ignore-not-found ns knative-serving",
+ "kubectl delete --ignore-not-found ns knative-build"
+ ]
+
+ full_delete_commands_size = api_resources.size + remove_knative_istio_leftovers_script.size
+
+ expect(subject.postdelete).to include(*remove_knative_istio_leftovers_script)
+ expect(subject.postdelete.size).to eq(full_delete_commands_size)
+ expect(subject.postdelete[2]).to eq("kubectl delete --ignore-not-found crd #{api_resources[0]}")
+ end
+ end
+
describe '#files' do
let(:application) { knative }
let(:values) { subject[:'values.yaml'] }
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index 26267c64112..eb6ccba5584 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -86,16 +86,15 @@ describe Clusters::Applications::Prometheus do
project: cluster.cluster_project.project)
end
- it 'creates proxy prometheus rest client' do
- expect(subject.prometheus_client).to be_instance_of(RestClient::Resource)
+ it 'creates proxy prometheus_client' do
+ expect(subject.prometheus_client).to be_instance_of(Gitlab::PrometheusClient)
end
- it 'creates proper url' do
- expect(subject.prometheus_client.url).to eq("#{kubernetes_url}/api/v1/namespaces/gitlab-managed-apps/services/prometheus-prometheus-server:80/proxy")
- end
-
- it 'copies options and headers from kube client to proxy client' do
- expect(subject.prometheus_client.options).to eq(kube_client.rest_client.options.merge(headers: kube_client.headers))
+ it 'copies proxy_url, options and headers from kube client to prometheus_client' do
+ expect(Gitlab::PrometheusClient)
+ .to(receive(:new))
+ .with(a_valid_url, kube_client.rest_client.options.merge(headers: kube_client.headers))
+ subject.prometheus_client
end
context 'when cluster is not reachable' do
@@ -142,7 +141,7 @@ describe Clusters::Applications::Prometheus do
end
it 'does not install knative metrics' do
- expect(subject.postinstall).to be_nil
+ expect(subject.postinstall).to be_empty
end
context 'with knative installed' do
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index 4f0cd0efe9c..4abe45a2152 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -18,7 +18,7 @@ describe Clusters::Applications::Runner do
subject { gitlab_runner.can_uninstall? }
- it { is_expected.to be_falsey }
+ it { is_expected.to be_truthy }
end
describe '#install_command' do
@@ -156,4 +156,35 @@ describe Clusters::Applications::Runner do
end
end
end
+
+ describe '#prepare_uninstall' do
+ it 'pauses associated runner' do
+ active_runner = create(:ci_runner, contacted_at: 1.second.ago)
+
+ expect(active_runner.status).to eq(:online)
+
+ application_runner = create(:clusters_applications_runner, :scheduled, runner: active_runner)
+ application_runner.prepare_uninstall
+
+ expect(active_runner.status).to eq(:paused)
+ end
+ end
+
+ describe '#make_uninstalling!' do
+ subject { create(:clusters_applications_runner, :scheduled, runner: ci_runner) }
+
+ it 'calls prepare_uninstall' do
+ expect_any_instance_of(described_class).to receive(:prepare_uninstall).and_call_original
+
+ subject.make_uninstalling!
+ end
+ end
+
+ describe '#post_uninstall' do
+ it 'destroys its runner' do
+ application_runner = create(:clusters_applications_runner, :scheduled, runner: ci_runner)
+
+ expect { application_runner.post_uninstall }.to change { Ci::Runner.count }.by(-1)
+ end
+ end
end
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index f206bb41f45..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 }
@@ -121,26 +116,6 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
- describe '.missing_kubernetes_namespace' do
- let!(:cluster) { create(:cluster, :provided_by_gcp, :project) }
- let(:project) { cluster.project }
- let(:kubernetes_namespaces) { project.kubernetes_namespaces }
-
- subject do
- described_class.joins(:projects).where(projects: { id: project.id }).missing_kubernetes_namespace(kubernetes_namespaces)
- end
-
- it { is_expected.to contain_exactly(cluster) }
-
- context 'kubernetes namespace exists' do
- before do
- create(:cluster_kubernetes_namespace, project: project, cluster: cluster)
- end
-
- it { is_expected.to be_empty }
- end
- end
-
describe 'validations' do
subject { cluster.valid? }
@@ -342,7 +317,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
- context 'when sub-group has configured kubernetes cluster', :nested_groups do
+ context 'when sub-group has configured kubernetes cluster' do
let(:sub_group_cluster) { create(:cluster, :provided_by_gcp, :group) }
let(:sub_group) { sub_group_cluster.group }
let(:project) { create(:project, group: sub_group) }
@@ -423,31 +398,6 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
- describe '#all_projects' do
- let(:project) { create(:project) }
- let(:cluster) { create(:cluster, projects: [project]) }
-
- subject { cluster.all_projects }
-
- context 'project cluster' do
- it 'returns project' do
- is_expected.to eq([project])
- end
- end
-
- context 'group cluster' do
- let(:cluster) { create(:cluster, :group) }
- let(:group) { cluster.group }
- let(:project) { create(:project, group: group) }
- let(:subgroup) { create(:group, parent: group) }
- let(:subproject) { create(:project, group: subgroup) }
-
- it 'returns all projects for group' do
- is_expected.to contain_exactly(project, subproject)
- end
- end
- end
-
describe '#first_project' do
subject { cluster.first_project }
@@ -496,7 +446,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
context 'when applications are created' do
let!(:helm) { create(:clusters_applications_helm, cluster: cluster) }
let!(:ingress) { create(:clusters_applications_ingress, cluster: cluster) }
- let!(:cert_manager) { create(:clusters_applications_cert_managers, cluster: cluster) }
+ let!(:cert_manager) { create(:clusters_applications_cert_manager, cluster: cluster) }
let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) }
let!(:runner) { create(:clusters_applications_runner, cluster: cluster) }
let!(:jupyter) { create(:clusters_applications_jupyter, cluster: cluster) }
@@ -514,19 +464,43 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
subject { cluster.allow_user_defined_namespace? }
context 'project type cluster' do
- it { is_expected.to be_truthy }
+ context 'gitlab managed' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'not managed' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, managed: false) }
+
+ it { is_expected.to be_truthy }
+ end
end
context 'group type cluster' do
- let(:cluster) { create(:cluster, :provided_by_gcp, :group) }
+ context 'gitlab managed' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, :group) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'not managed' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, :group, managed: false) }
- it { is_expected.to be_falsey }
+ it { is_expected.to be_truthy }
+ end
end
context 'instance type cluster' do
- let(:cluster) { create(:cluster, :provided_by_gcp, :instance) }
+ context 'gitlab managed' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, :instance) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'not managed' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, :instance, managed: false) }
- it { is_expected.to be_falsey }
+ it { is_expected.to be_truthy }
+ end
end
end
@@ -555,6 +529,42 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
+ describe '#kubernetes_namespace_for' do
+ let(:cluster) { create(:cluster, :group) }
+ let(:environment) { create(:environment) }
+
+ subject { cluster.kubernetes_namespace_for(environment) }
+
+ 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 persisted namespace exists' do
+ let(:persisted_namespace) { create(:cluster_kubernetes_namespace) }
+
+ it { is_expected.to eq persisted_namespace.namespace }
+ end
+
+ context 'no persisted namespace exists' do
+ let(:persisted_namespace) { nil }
+ let(:namespace_generator) { double }
+ let(:default_namespace) { 'a-default-namespace' }
+
+ 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
+
describe '#predefined_variables' do
subject { cluster.predefined_variables }
diff --git a/spec/models/clusters/clusters_hierarchy_spec.rb b/spec/models/clusters/clusters_hierarchy_spec.rb
new file mode 100644
index 00000000000..0470ebe17ea
--- /dev/null
+++ b/spec/models/clusters/clusters_hierarchy_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::ClustersHierarchy do
+ describe '#base_and_ancestors' do
+ def base_and_ancestors(clusterable)
+ described_class.new(clusterable).base_and_ancestors
+ end
+
+ context 'project in nested group with clusters at every level' do
+ let!(:cluster) { create(:cluster, :project, projects: [project]) }
+ let!(:child) { create(:cluster, :group, groups: [child_group]) }
+ let!(:parent) { create(:cluster, :group, groups: [parent_group]) }
+ let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group]) }
+
+ let(:ancestor_group) { create(:group) }
+ let(:parent_group) { create(:group, parent: ancestor_group) }
+ let(:child_group) { create(:group, parent: parent_group) }
+ let(:project) { create(:project, group: child_group) }
+
+ it 'returns clusters for project' do
+ expect(base_and_ancestors(project)).to eq([cluster, child, parent, ancestor])
+ end
+
+ it 'returns clusters for child_group' do
+ expect(base_and_ancestors(child_group)).to eq([child, parent, ancestor])
+ end
+
+ it 'returns clusters for parent_group' do
+ expect(base_and_ancestors(parent_group)).to eq([parent, ancestor])
+ end
+
+ it 'returns clusters for ancestor_group' do
+ expect(base_and_ancestors(ancestor_group)).to eq([ancestor])
+ end
+ end
+
+ context 'project in a namespace' do
+ let!(:cluster) { create(:cluster, :project) }
+
+ it 'returns clusters for project' do
+ expect(base_and_ancestors(cluster.project)).to eq([cluster])
+ end
+ end
+
+ context 'project in nested group with clusters at some levels' do
+ let!(:child) { create(:cluster, :group, groups: [child_group]) }
+ let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group]) }
+
+ let(:ancestor_group) { create(:group) }
+ let(:parent_group) { create(:group, parent: ancestor_group) }
+ let(:child_group) { create(:group, parent: parent_group) }
+ let(:project) { create(:project, group: child_group) }
+
+ it 'returns clusters for project' do
+ expect(base_and_ancestors(project)).to eq([child, ancestor])
+ end
+
+ it 'returns clusters for child_group' do
+ expect(base_and_ancestors(child_group)).to eq([child, ancestor])
+ end
+
+ it 'returns clusters for parent_group' do
+ expect(base_and_ancestors(parent_group)).to eq([ancestor])
+ end
+
+ it 'returns clusters for ancestor_group' do
+ expect(base_and_ancestors(ancestor_group)).to eq([ancestor])
+ end
+ end
+ 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 c485850c16e..0c4cf291d20 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -2,13 +2,11 @@
require 'spec_helper'
-describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching do
+describe Clusters::Platforms::Kubernetes do
include KubernetesHelpers
- include ReactiveCachingHelpers
it { is_expected.to belong_to(:cluster) }
it { is_expected.to be_kind_of(Gitlab::Kubernetes) }
- it { is_expected.to be_kind_of(ReactiveCaching) }
it { is_expected.to respond_to :ca_pem }
it { is_expected.to validate_exclusion_of(:namespace).in_array(%w(gitlab-managed-apps)) }
@@ -108,7 +106,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
before do
allow(ApplicationSetting)
.to receive(:current)
- .and_return(ApplicationSetting.build_from_defaults(allow_local_requests_from_hooks_and_services: true))
+ .and_return(ApplicationSetting.build_from_defaults(allow_local_requests_from_web_hooks_and_services: true))
end
it { expect(kubernetes.save).to be_truthy }
@@ -207,194 +205,100 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
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) }
- end
-
- context 'with no namespace assigned' do
- let(:namespace) { nil }
-
- context 'when kubernetes namespace is present' do
- let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
-
- before do
- kubernetes_namespace
- end
-
- it { is_expected.to eq(kubernetes_namespace.namespace) }
- end
-
- context 'when kubernetes namespace is not present' do
- it { is_expected.to eq("#{project.path}-#{project.id}") }
- end
- end
- end
-
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
- end
+ 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) }
- context 'kubernetes namespace is created with no service account token' do
- let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
+ let(:environment_name) { 'env/production' }
+ let(:environment_slug) { Gitlab::Slug::Environment.new(environment_name).generate }
- it_behaves_like 'setting variables'
+ subject { platform.predefined_variables(project: project, environment_name: environment_name) }
- it 'sets KUBE_TOKEN' do
- expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: 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, :with_token, 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 '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_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 'namespace is provided' do
- let(:namespace) { 'my-project' }
+ context 'kubernetes namespace exists' do
+ let(:variable) { Hash(key: :fake_key, value: 'fake_value') }
+ let(:namespace_variables) { Gitlab::Ci::Variables::Collection.new([variable]) }
before do
- kubernetes.namespace = namespace
+ expect(persisted_namespace).to receive(:predefined_variables).and_return(namespace_variables)
end
- 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
-
- context 'no namespace provided' do
- 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
+ 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'
+ context 'kubernetes namespace does not exist' do
+ let(:persisted_namespace) { nil }
+ let(:namespace) { 'kubernetes-namespace' }
+ let(:kubeconfig) { 'kubeconfig' }
- 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) }
-
- 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
describe '#terminals' do
- subject { service.terminals(environment) }
+ subject { service.terminals(environment, pods: pods) }
let!(:cluster) { create(:cluster, :project, platform_kubernetes: service) }
let(:project) { cluster.project }
let(:service) { create(:cluster_platform_kubernetes, :configured) }
let(:environment) { build(:environment, project: project, name: "env", slug: "env-000000") }
+ let(:pods) { [{ "bad" => "pod" }] }
context 'with invalid pods' do
it 'returns no terminals' do
- stub_reactive_cache(service, pods: [{ "bad" => "pod" }])
-
is_expected.to be_empty
end
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) }
-
- before do
- stub_reactive_cache(
- service,
- pods: [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")]
- )
- end
+ let(:pods) { [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")] }
it 'returns terminals' do
is_expected.to eq(terminals + terminals)
@@ -409,16 +313,18 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
end
- describe '#calculate_reactive_cache' do
- subject { service.calculate_reactive_cache }
-
- let!(:cluster) { create(:cluster, :project, enabled: enabled, platform_kubernetes: service) }
+ describe '#calculate_reactive_cache_for' do
let(:service) { create(:cluster_platform_kubernetes, :configured) }
- let(:enabled) { true }
- let(:namespace) { cluster.kubernetes_namespace_for(cluster.project) }
+ let(:pod) { kube_pod }
+ let(:namespace) { pod["metadata"]["namespace"] }
+ let(:environment) { instance_double(Environment, deployment_namespace: namespace) }
- context 'when cluster is disabled' do
- let(:enabled) { false }
+ subject { service.calculate_reactive_cache_for(environment) }
+
+ context 'when the kubernetes integration is disabled' do
+ before do
+ allow(service).to receive(:enabled?).and_return(false)
+ end
it { is_expected.to be_nil }
end
@@ -429,7 +335,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
stub_kubeclient_deployments(namespace)
end
- it { is_expected.to include(pods: [kube_pod]) }
+ it { is_expected.to include(pods: [pod]) }
end
context 'when kubernetes responds with 500s' do
@@ -449,34 +355,5 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { is_expected.to include(pods: []) }
end
-
- context 'when the cluster is not project level' do
- let(:cluster) { create(:cluster, :group, platform_kubernetes: service) }
-
- it { is_expected.to include(pods: []) }
- end
- end
-
- describe '#update_kubernetes_namespace' do
- let(:cluster) { create(:cluster, :provided_by_gcp) }
- let(:platform) { cluster.platform }
-
- context 'when namespace is updated' do
- it 'calls ConfigureWorker' do
- expect(ClusterConfigureWorker).to receive(:perform_async).with(cluster.id).once
-
- platform.namespace = 'new-namespace'
- platform.save
- end
- end
-
- context 'when namespace is not updated' do
- it 'does not call ConfigureWorker' do
- expect(ClusterConfigureWorker).not_to receive(:perform_async)
-
- platform.username = "new-username"
- platform.save
- end
- end
end
end