diff options
author | Tiger <twatson@gitlab.com> | 2019-03-04 11:00:40 +1100 |
---|---|---|
committer | Tiger <twatson@gitlab.com> | 2019-03-20 12:04:46 +1100 |
commit | 98a14a498dc3ffe6ea8bcd7db62e8bada5d2eb45 (patch) | |
tree | 6ba7e897e543c14b8ca38216f9417964a7cd25c2 | |
parent | 00f0d356b71fa87f8190810b01add0cc4586e90a (diff) | |
download | gitlab-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.
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 |