summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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