diff options
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 |