summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiger <twatson@gitlab.com>2019-03-04 11:00:40 +1100
committerTiger <twatson@gitlab.com>2019-03-20 12:04:46 +1100
commit98a14a498dc3ffe6ea8bcd7db62e8bada5d2eb45 (patch)
tree6ba7e897e543c14b8ca38216f9417964a7cd25c2
parent00f0d356b71fa87f8190810b01add0cc4586e90a (diff)
downloadgitlab-ce-98a14a498dc3ffe6ea8bcd7db62e8bada5d2eb45.tar.gz
Add build prerequisite for Kubernetes namespaces
Builds that have deployments require Kubernetes resources to be created before the build can be deployed. These resources are no longer created when the cluster is created, which allows us to only create the resources required by each specific build.
-rw-r--r--changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml5
-rw-r--r--lib/gitlab/ci/build/prerequisite/factory.rb2
-rw-r--r--lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb48
-rw-r--r--spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb72
4 files changed, 126 insertions, 1 deletions
diff --git a/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml b/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml
new file mode 100644
index 00000000000..2141c75ec72
--- /dev/null
+++ b/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml
@@ -0,0 +1,5 @@
+---
+title: Create Kubernetes resources for projects when their deployment jobs run.
+merge_request: 25586
+author:
+type: changed
diff --git a/lib/gitlab/ci/build/prerequisite/factory.rb b/lib/gitlab/ci/build/prerequisite/factory.rb
index 6b9c17bba07..60cdf7af418 100644
--- a/lib/gitlab/ci/build/prerequisite/factory.rb
+++ b/lib/gitlab/ci/build/prerequisite/factory.rb
@@ -8,7 +8,7 @@ module Gitlab
attr_reader :build
def self.prerequisites
- []
+ [KubernetesNamespace]
end
def initialize(build)
diff --git a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
new file mode 100644
index 00000000000..d1b59d0a64c
--- /dev/null
+++ b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Prerequisite
+ class KubernetesNamespace < Base
+ ##
+ # Cluster settings may have changed since the last deploy,
+ # so we must always ensure the namespace is up to date.
+ #
+ def unmet?
+ build.has_deployment? && clusters_missing_namespaces.present?
+ end
+
+ def complete!
+ return unless unmet?
+
+ clusters_missing_namespaces.each do |cluster|
+ create_or_update_namespace(cluster)
+ end
+ end
+
+ private
+
+ def project
+ build.project
+ end
+
+ def create_or_update_namespace(cluster)
+ kubernetes_namespace = cluster.find_or_initialize_kubernetes_namespace_for_project(project)
+
+ Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
+ cluster: cluster,
+ kubernetes_namespace: kubernetes_namespace
+ ).execute
+ end
+
+ def clusters_missing_namespaces
+ strong_memoize(:clusters_missing_namespaces) do
+ project.all_clusters.missing_kubernetes_namespace(project.kubernetes_namespaces).to_a
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
new file mode 100644
index 00000000000..6f6e4abc0c8
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
+ let(:build) { create(:ci_build) }
+
+ describe '#unmet?' do
+ subject { described_class.new(build).unmet? }
+
+ context 'build has no deployment' do
+ before do
+ expect(build.deployment).to be_nil
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'build has a deployment, and no existing kubernetes namespace' do
+ let!(:deployment) { create(:deployment, deployable: build) }
+ let!(:cluster) { create(:cluster, projects: [build.project]) }
+
+ before do
+ expect(build.project.kubernetes_namespaces).to be_empty
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'build has a deployment and kubernetes namespaces' do
+ let!(:deployment) { create(:deployment, deployable: build) }
+ let!(:cluster) { create(:cluster, projects: [build.project]) }
+ let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#complete!' do
+ let(:cluster) { create(:cluster, projects: [build.project]) }
+ let(:service) { double(execute: true) }
+
+ subject { described_class.new(build).complete! }
+
+ context 'completion is required' do
+ let!(:deployment) { create(:deployment, deployable: build) }
+
+ it 'creates a kubernetes namespace' do
+ expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService)
+ .to receive(:new)
+ .with(cluster: cluster, kubernetes_namespace: instance_of(Clusters::KubernetesNamespace))
+ .and_return(service)
+
+ expect(service).to receive(:execute).once
+
+ subject
+ end
+ end
+
+ context 'completion is not required' do
+ before do
+ expect(build.deployment).to be_nil
+ end
+
+ it 'does not create a namespace' do
+ expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+end