Merge branch '38668-revert-copied-kubernetesservice-logic' into 'master'

Copy `KubernetesService` logic in `Clusters::Platforms::Kubernetes` to make it interchangeable. And implement a selector.

See merge request gitlab-org/gitlab-ce!15515
This commit is contained in:
Grzegorz Bizon 2017-11-30 11:01:14 +00:00
commit feece77132
28 changed files with 612 additions and 264 deletions

View file

@ -41,7 +41,7 @@ class Projects::BranchesController < Projects::ApplicationController
branch_name = sanitize(strip_tags(params[:branch_name])) branch_name = sanitize(strip_tags(params[:branch_name]))
branch_name = Addressable::URI.unescape(branch_name) branch_name = Addressable::URI.unescape(branch_name)
redirect_to_autodeploy = project.empty_repo? && project.deployment_services.present? redirect_to_autodeploy = project.empty_repo? && project.deployment_platform.present?
result = CreateBranchService.new(project, current_user) result = CreateBranchService.new(project, current_user)
.execute(branch_name, ref) .execute(branch_name, ref)

View file

@ -26,7 +26,7 @@ module AutoDevopsHelper
def auto_devops_warning_message(project) def auto_devops_warning_message(project)
missing_domain = !project.auto_devops&.has_domain? missing_domain = !project.auto_devops&.has_domain?
missing_service = !project.kubernetes_service&.active? missing_service = !project.deployment_platform&.active?
if missing_service if missing_service
params = { params = {

View file

@ -365,7 +365,7 @@ module Ci
end end
def has_kubernetes_active? def has_kubernetes_active?
project.kubernetes_service&.active? project.deployment_platform&.active?
end end
def has_stage_seeds? def has_stage_seeds?

View file

@ -17,8 +17,7 @@ module Clusters
# we force autosave to happen when we save `Cluster` model # we force autosave to happen when we save `Cluster` model
has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true
# We have to ":destroy" it today to ensure that we clean also the Kubernetes Integration has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes'
has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :application_helm, class_name: 'Clusters::Applications::Helm' has_one :application_helm, class_name: 'Clusters::Applications::Helm'
has_one :application_ingress, class_name: 'Clusters::Applications::Ingress' has_one :application_ingress, class_name: 'Clusters::Applications::Ingress'
@ -29,15 +28,9 @@ module Clusters
validates :name, cluster_name: true validates :name, cluster_name: true
validate :restrict_modification, on: :update validate :restrict_modification, on: :update
# TODO: Move back this into Clusters::Platforms::Kubernetes in 10.3
# We need callback here because `enabled` belongs to Clusters::Cluster
# Callbacks in Clusters::Platforms::Kubernetes will not be called after update
after_save :update_kubernetes_integration!
delegate :status, to: :provider, allow_nil: true delegate :status, to: :provider, allow_nil: true
delegate :status_reason, to: :provider, allow_nil: true delegate :status_reason, to: :provider, allow_nil: true
delegate :on_creation?, to: :provider, allow_nil: true delegate :on_creation?, to: :provider, allow_nil: true
delegate :update_kubernetes_integration!, to: :platform, allow_nil: true
delegate :active?, to: :platform_kubernetes, prefix: true, allow_nil: true delegate :active?, to: :platform_kubernetes, prefix: true, allow_nil: true
delegate :installed?, to: :application_helm, prefix: true, allow_nil: true delegate :installed?, to: :application_helm, prefix: true, allow_nil: true

View file

@ -1,7 +1,12 @@
module Clusters module Clusters
module Platforms module Platforms
class Kubernetes < ActiveRecord::Base class Kubernetes < ActiveRecord::Base
include Gitlab::CurrentSettings
include Gitlab::Kubernetes
include ReactiveCaching
self.table_name = 'cluster_platforms_kubernetes' self.table_name = 'cluster_platforms_kubernetes'
self.reactive_cache_key = ->(kubernetes) { [kubernetes.class.model_name.singular, kubernetes.id] }
belongs_to :cluster, inverse_of: :platform_kubernetes, class_name: 'Clusters::Cluster' belongs_to :cluster, inverse_of: :platform_kubernetes, class_name: 'Clusters::Cluster'
@ -29,19 +34,14 @@ module Clusters
validates :api_url, url: true, presence: true validates :api_url, url: true, presence: true
validates :token, presence: true validates :token, presence: true
# TODO: Glue code till we migrate Kubernetes Integration into Platforms::Kubernetes after_save :clear_reactive_cache!
after_destroy :destroy_kubernetes_integration!
alias_attribute :ca_pem, :ca_cert alias_attribute :ca_pem, :ca_cert
delegate :project, to: :cluster, allow_nil: true delegate :project, to: :cluster, allow_nil: true
delegate :enabled?, to: :cluster, allow_nil: true delegate :enabled?, to: :cluster, allow_nil: true
class << self alias_method :active?, :enabled?
def namespace_for_project(project)
"#{project.path}-#{project.id}"
end
end
def actual_namespace def actual_namespace
if namespace.present? if namespace.present?
@ -51,59 +51,128 @@ module Clusters
end end
end end
def default_namespace def predefined_variables
self.class.namespace_for_project(project) if project config = YAML.dump(kubeconfig)
variables = [
{ key: 'KUBE_URL', value: api_url, public: true },
{ key: 'KUBE_TOKEN', value: token, public: false },
{ key: 'KUBE_NAMESPACE', value: actual_namespace, public: true },
{ key: 'KUBECONFIG', value: config, public: false, file: true }
]
if ca_pem.present?
variables << { key: 'KUBE_CA_PEM', value: ca_pem, public: true }
variables << { key: 'KUBE_CA_PEM_FILE', value: ca_pem, public: true, file: true }
end
variables
end
# Constructs a list of terminals from the reactive cache
#
# Returns nil if the cache is empty, in which case you should try again a
# short time later
def terminals(environment)
with_reactive_cache do |data|
pods = filter_by_label(data[:pods], app: environment.slug)
terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }
terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
end
end
# Caches resources in the namespace so other calls don't need to block on
# network access
def calculate_reactive_cache
return unless enabled? && project && !project.pending_delete?
# We may want to cache extra things in the future
{ pods: read_pods }
end end
def kubeclient def kubeclient
@kubeclient ||= kubernetes_service.kubeclient if manages_kubernetes_service? @kubeclient ||= build_kubeclient!
end
def update_kubernetes_integration!
raise 'Kubernetes service already configured' unless manages_kubernetes_service?
# This is neccesary, otheriwse enabled? returns true even though cluster updated with enabled: false
cluster.reload
ensure_kubernetes_service&.update!(
active: enabled?,
api_url: api_url,
namespace: namespace,
token: token,
ca_pem: ca_cert
)
end
def active?
manages_kubernetes_service?
end end
private private
def kubeconfig
to_kubeconfig(
url: api_url,
namespace: actual_namespace,
token: token,
ca_pem: ca_pem)
end
def default_namespace
return unless project
slug = "#{project.path}-#{project.id}".downcase
slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
end
def build_kubeclient!(api_path: 'api', api_version: 'v1')
raise "Incomplete settings" unless api_url && actual_namespace
unless (username && password) || token
raise "Either username/password or token is required to access API"
end
::Kubeclient::Client.new(
join_api_url(api_path),
api_version,
auth_options: kubeclient_auth_options,
ssl_options: kubeclient_ssl_options,
http_proxy_uri: ENV['http_proxy']
)
end
# Returns a hash of all pods in the namespace
def read_pods
kubeclient = build_kubeclient!
kubeclient.get_pods(namespace: actual_namespace).as_json
rescue KubeException => err
raise err unless err.error_code == 404
[]
end
def kubeclient_ssl_options
opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
if ca_pem.present?
opts[:cert_store] = OpenSSL::X509::Store.new
opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
end
opts
end
def kubeclient_auth_options
{ bearer_token: token }
end
def join_api_url(api_path)
url = URI.parse(api_url)
prefix = url.path.sub(%r{/+\z}, '')
url.path = [prefix, api_path].join("/")
url.to_s
end
def terminal_auth
{
token: token,
ca_pem: ca_pem,
max_session_time: current_application_settings.terminal_max_session_time
}
end
def enforce_namespace_to_lower_case def enforce_namespace_to_lower_case
self.namespace = self.namespace&.downcase self.namespace = self.namespace&.downcase
end end
# TODO: glue code till we migrate Kubernetes Service into Platforms::Kubernetes class
def manages_kubernetes_service?
return true unless kubernetes_service&.active?
kubernetes_service.api_url == api_url
end
def destroy_kubernetes_integration!
return unless manages_kubernetes_service?
kubernetes_service&.destroy!
end
def kubernetes_service
@kubernetes_service ||= project&.kubernetes_service
end
def ensure_kubernetes_service
@kubernetes_service ||= kubernetes_service || project&.build_kubernetes_service
end
end end
end end
end end

View file

@ -138,11 +138,11 @@ class Environment < ActiveRecord::Base
end end
def has_terminals? def has_terminals?
project.deployment_service.present? && available? && last_deployment.present? project.deployment_platform.present? && available? && last_deployment.present?
end end
def terminals def terminals
project.deployment_service.terminals(self) if has_terminals? project.deployment_platform.terminals(self) if has_terminals?
end end
def has_metrics? def has_metrics?

View file

@ -897,12 +897,10 @@ class Project < ActiveRecord::Base
@ci_service ||= ci_services.reorder(nil).find_by(active: true) @ci_service ||= ci_services.reorder(nil).find_by(active: true)
end end
def deployment_services # TODO: This will be extended for multiple enviroment clusters
services.where(category: :deployment) def deployment_platform
end @deployment_platform ||= clusters.find_by(enabled: true)&.platform_kubernetes
@deployment_platform ||= services.where(category: :deployment).reorder(nil).find_by(active: true)
def deployment_service
@deployment_service ||= deployment_services.reorder(nil).find_by(active: true)
end end
def monitoring_services def monitoring_services
@ -1547,9 +1545,9 @@ class Project < ActiveRecord::Base
end end
def deployment_variables def deployment_variables
return [] unless deployment_service return [] unless deployment_platform
deployment_service.predefined_variables deployment_platform.predefined_variables
end end
def auto_devops_variables def auto_devops_variables

View file

@ -1,3 +1,8 @@
##
# NOTE:
# We'll move this class to Clusters::Platforms::Kubernetes, which contains exactly the same logic.
# After we've migrated data, we'll remove KubernetesService. This would happen in a few months.
# If you're modyfiyng this class, please note that you should update the same change in Clusters::Platforms::Kubernetes.
class KubernetesService < DeploymentService class KubernetesService < DeploymentService
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
include Gitlab::Kubernetes include Gitlab::Kubernetes

View file

@ -5,7 +5,7 @@
.col-sm-4 .col-sm-4
= render 'sidebar' = render 'sidebar'
.col-sm-8 .col-sm-8
- if @project.kubernetes_service&.active? - if @project.deployment_platform&.active?
%h4.prepend-top-0= s_('ClusterIntegration|Cluster management') %h4.prepend-top-0= s_('ClusterIntegration|Cluster management')
%p= s_('ClusterIntegration|A cluster has been set up on this project through the Kubernetes integration page') %p= s_('ClusterIntegration|A cluster has been set up on this project through the Kubernetes integration page')

View file

@ -147,7 +147,7 @@
%ul %ul
%li Be careful. Renaming a project's repository can have unintended side effects. %li Be careful. Renaming a project's repository can have unintended side effects.
%li You will need to update your local repositories to point to the new location. %li You will need to update your local repositories to point to the new location.
- if @project.deployment_services.any? - if @project.deployment_platform.present?
%li Your deployment services will be broken, you will need to manually fix the services after renaming. %li Your deployment services will be broken, you will need to manually fix the services after renaming.
= f.submit 'Rename project', class: "btn btn-warning" = f.submit 'Rename project', class: "btn btn-warning"
- if can?(current_user, :change_namespace, @project) - if can?(current_user, :change_namespace, @project)

View file

@ -67,7 +67,7 @@
- if koding_enabled? && @repository.koding_yml.blank? - if koding_enabled? && @repository.koding_yml.blank?
%li.missing %li.missing
= link_to _('Set up Koding'), add_koding_stack_path(@project) = link_to _('Set up Koding'), add_koding_stack_path(@project)
- if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present? - if @repository.gitlab_ci_yml.blank? && @project.deployment_platform.present?
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', branch_name: 'auto-deploy', context: 'autodeploy') do = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', branch_name: 'auto-deploy', context: 'autodeploy') do
#{ _('Set up auto deploy') } #{ _('Set up auto deploy') }

View file

@ -76,7 +76,7 @@ module Gitlab
timeframe_start: timeframe_start, timeframe_start: timeframe_start,
timeframe_end: timeframe_end, timeframe_end: timeframe_end,
ci_environment_slug: environment.slug, ci_environment_slug: environment.slug,
kube_namespace: environment.project.kubernetes_service&.actual_namespace || '', kube_namespace: environment.project.deployment_platform&.actual_namespace || '',
environment_filter: %{container_name!="POD",environment="#{environment.slug}"} environment_filter: %{container_name!="POD",environment="#{environment.slug}"}
} }
end end

View file

@ -113,11 +113,10 @@ describe Projects::BranchesController do
expect(response).to redirect_to project_tree_path(project, branch) expect(response).to redirect_to project_tree_path(project, branch)
end end
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'redirects to autodeploy setup page' do it 'redirects to autodeploy setup page' do
result = { status: :success, branch: double(name: branch) } result = { status: :success, branch: double(name: branch) }
project.services << build(:kubernetes_service)
expect_any_instance_of(CreateBranchService).to receive(:execute).and_return(result) expect_any_instance_of(CreateBranchService).to receive(:execute).and_return(result)
expect(SystemNoteService).to receive(:new_issue_branch).and_return(true) expect(SystemNoteService).to receive(:new_issue_branch).and_return(true)
@ -132,6 +131,23 @@ describe Projects::BranchesController do
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
before do
project.services << build(:kubernetes_service)
end
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
before do
create(:cluster, :provided_by_gcp, projects: [project])
end
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
context 'without issue feature access' do context 'without issue feature access' do
before do before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)

View file

@ -4,15 +4,10 @@ describe 'Auto deploy' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
before do shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
create :kubernetes_service, project: project
project.team << [user, :master]
sign_in user
end
context 'when no deployment service is active' do context 'when no deployment service is active' do
before do before do
project.kubernetes_service.update!(active: false) trun_off
end end
it 'does not show a button to set up auto deploy' do it 'does not show a button to set up auto deploy' do
@ -23,7 +18,7 @@ describe 'Auto deploy' do
context 'when a deployment service is active' do context 'when a deployment service is active' do
before do before do
project.kubernetes_service.update!(active: true) trun_on
visit project_path(project) visit project_path(project)
end end
@ -52,4 +47,31 @@ describe 'Auto deploy' do
expect(page).to have_content('New Merge Request From auto-deploy into master') expect(page).to have_content('New Merge Request From auto-deploy into master')
end end
end end
end
context 'when user configured kubernetes from Integration > Kubernetes' do
before do
create :kubernetes_service, project: project
project.team << [user, :master]
sign_in user
end
let(:trun_on) { project.deployment_platform.update!(active: true) }
let(:trun_off) { project.deployment_platform.update!(active: false) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
before do
create(:cluster, :provided_by_gcp, projects: [project])
project.team << [user, :master]
sign_in user
end
let(:trun_on) { project.deployment_platform.cluster.update!(enabled: true) }
let(:trun_off) { project.deployment_platform.cluster.update!(enabled: false) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end end

View file

@ -0,0 +1,16 @@
require 'spec_helper'
feature 'Interchangeability between KubernetesService and Platform::Kubernetes' do
EXCEPT_METHODS = %i[test title description help fields initialize_properties namespace namespace= api_url api_url=].freeze
EXCEPT_METHODS_GREP_V = %w[_touched? _changed? _was].freeze
it 'Clusters::Platform::Kubernetes covers core interfaces in KubernetesService' do
expected_interfaces = KubernetesService.instance_methods(false)
expected_interfaces = expected_interfaces - EXCEPT_METHODS
EXCEPT_METHODS_GREP_V.each do |g|
expected_interfaces = expected_interfaces.grep_v(/#{Regexp.escape(g)}\z/)
end
expect(expected_interfaces - Clusters::Platforms::Kubernetes.instance_methods).to be_empty
end
end

View file

@ -101,8 +101,7 @@ feature 'Environment' do
end end
context 'with terminal' do context 'with terminal' do
let(:project) { create(:kubernetes_project, :test_repo) } shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
context 'for project master' do context 'for project master' do
let(:role) { :master } let(:role) { :master }
@ -133,6 +132,20 @@ feature 'Environment' do
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project, :test_repo) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
context 'when environment is available' do context 'when environment is available' do
context 'with stop action' do context 'with stop action' do
given(:action) do given(:action) do

View file

@ -208,8 +208,7 @@ feature 'Environments page', :js do
end end
context 'when kubernetes terminal is available' do context 'when kubernetes terminal is available' do
let(:project) { create(:kubernetes_project, :test_repo) } shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
context 'for project master' do context 'for project master' do
let(:role) { :master } let(:role) { :master }
@ -226,6 +225,20 @@ feature 'Environments page', :js do
end end
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project, :test_repo) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [create(:project, :repository)]) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
end end
end end
end end

View file

@ -4,14 +4,27 @@ describe Gitlab::Ci::Build::Policy::Kubernetes do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when kubernetes service is active' do context 'when kubernetes service is active' do
set(:project) { create(:kubernetes_project) } shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'is satisfied by a kubernetes pipeline' do it 'is satisfied by a kubernetes pipeline' do
expect(described_class.new('active')) expect(described_class.new('active'))
.to be_satisfied_by(pipeline) .to be_satisfied_by(pipeline)
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
context 'when kubernetes service is inactive' do context 'when kubernetes service is inactive' do
set(:project) { create(:project) } set(:project) { create(:project) }

View file

@ -178,9 +178,7 @@ module Gitlab
end end
context 'when kubernetes is active' do context 'when kubernetes is active' do
let(:project) { create(:kubernetes_project) } shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
it 'returns seeds for kubernetes dependent job' do it 'returns seeds for kubernetes dependent job' do
seeds = subject.stage_seeds(pipeline) seeds = subject.stage_seeds(pipeline)
@ -190,6 +188,22 @@ module Gitlab
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
context 'when kubernetes is not active' do context 'when kubernetes is not active' do
it 'does not return seeds for kubernetes dependent job' do it 'does not return seeds for kubernetes dependent job' do
seeds = subject.stage_seeds(pipeline) seeds = subject.stage_seeds(pipeline)

View file

@ -557,13 +557,26 @@ describe Ci::Pipeline, :mailer do
describe '#has_kubernetes_active?' do describe '#has_kubernetes_active?' do
context 'when kubernetes is active' do context 'when kubernetes is active' do
let(:project) { create(:kubernetes_project) } shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'returns true' do it 'returns true' do
expect(pipeline).to have_kubernetes_active expect(pipeline).to have_kubernetes_active
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
context 'when kubernetes is not active' do context 'when kubernetes is not active' do
it 'returns false' do it 'returns false' do
expect(pipeline).not_to have_kubernetes_active expect(pipeline).not_to have_kubernetes_active

View file

@ -9,7 +9,6 @@ describe Clusters::Cluster do
it { is_expected.to delegate_method(:status_reason).to(:provider) } it { is_expected.to delegate_method(:status_reason).to(:provider) }
it { is_expected.to delegate_method(:status_name).to(:provider) } it { is_expected.to delegate_method(:status_name).to(:provider) }
it { is_expected.to delegate_method(:on_creation?).to(:provider) } it { is_expected.to delegate_method(:on_creation?).to(:provider) }
it { is_expected.to delegate_method(:update_kubernetes_integration!).to(:platform) }
it { is_expected.to respond_to :project } it { is_expected.to respond_to :project }
describe '.enabled' do describe '.enabled' do

View file

@ -5,6 +5,8 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
include ReactiveCachingHelpers include ReactiveCachingHelpers
it { is_expected.to belong_to(:cluster) } 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 respond_to :ca_pem }
describe 'before_validation' do describe 'before_validation' do
@ -90,55 +92,6 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end end
end end
describe 'after_save from Clusters::Cluster' do
context 'when platform_kubernetes is being cerated' do
let(:enabled) { true }
let(:project) { create(:project) }
let(:cluster) { build(:cluster, provider_type: :gcp, platform_type: :kubernetes, platform_kubernetes: platform, provider_gcp: provider, enabled: enabled, projects: [project]) }
let(:platform) { build(:cluster_platform_kubernetes, :configured) }
let(:provider) { build(:cluster_provider_gcp) }
let(:kubernetes_service) { project.kubernetes_service }
it 'updates KubernetesService' do
cluster.save!
expect(kubernetes_service.active).to eq(enabled)
expect(kubernetes_service.api_url).to eq(platform.api_url)
expect(kubernetes_service.namespace).to eq(platform.namespace)
expect(kubernetes_service.ca_pem).to eq(platform.ca_cert)
end
end
context 'when platform_kubernetes has been created' do
let(:enabled) { false }
let!(:project) { create(:project) }
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let(:platform) { cluster.platform }
let(:kubernetes_service) { project.kubernetes_service }
it 'updates KubernetesService' do
cluster.update(enabled: enabled)
expect(kubernetes_service.active).to eq(enabled)
end
end
context 'when kubernetes_service has been configured without cluster integration' do
let!(:project) { create(:project) }
let(:cluster) { build(:cluster, provider_type: :gcp, platform_type: :kubernetes, platform_kubernetes: platform, provider_gcp: provider, projects: [project]) }
let(:platform) { build(:cluster_platform_kubernetes, :configured, api_url: 'https://111.111.111.111') }
let(:provider) { build(:cluster_provider_gcp) }
before do
create(:kubernetes_service, project: project)
end
it 'raises an error' do
expect { cluster.save! }.to raise_error('Kubernetes service already configured')
end
end
end
describe '#actual_namespace' do describe '#actual_namespace' do
subject { kubernetes.actual_namespace } subject { kubernetes.actual_namespace }
@ -159,16 +112,8 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end end
end end
describe '.namespace_for_project' do
subject { described_class.namespace_for_project(project) }
let(:project) { create(:project) }
it { is_expected.to eq("#{project.path}-#{project.id}") }
end
describe '#default_namespace' do describe '#default_namespace' do
subject { kubernetes.default_namespace } subject { kubernetes.send(:default_namespace) }
let(:kubernetes) { create(:cluster_platform_kubernetes, :configured) } let(:kubernetes) { create(:cluster_platform_kubernetes, :configured) }
@ -185,4 +130,137 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { is_expected.to be_nil } it { is_expected.to be_nil }
end 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, token: token) }
let(:api_url) { 'https://kube.domain.com' }
let(:ca_pem) { 'CA PEM DATA' }
let(:token) { 'token' }
let(:kubeconfig) do
config_file = expand_fixture_path('config/kubeconfig.yml')
config = YAML.load(File.read(config_file))
config.dig('users', 0, 'user')['token'] = token
config.dig('contexts', 0, 'context')['namespace'] = namespace
config.dig('clusters', 0, 'cluster')['certificate-authority-data'] =
Base64.strict_encode64(ca_pem)
YAML.dump(config)
end
shared_examples 'setting variables' do
it 'sets the variables' do
expect(kubernetes.predefined_variables).to include(
{ key: 'KUBE_URL', value: api_url, public: true },
{ key: 'KUBE_TOKEN', value: token, public: false },
{ key: 'KUBE_NAMESPACE', value: namespace, public: true },
{ key: 'KUBECONFIG', value: kubeconfig, public: false, file: true },
{ key: 'KUBE_CA_PEM', value: ca_pem, public: true },
{ key: 'KUBE_CA_PEM_FILE', value: ca_pem, public: true, file: true }
)
end
end
context 'namespace is provided' do
let(:namespace) { 'my-project' }
before do
kubernetes.namespace = namespace
end
it_behaves_like 'setting variables'
end
context 'no namespace provided' do
let(:namespace) { kubernetes.actual_namespace }
it_behaves_like 'setting variables'
it 'sets the KUBE_NAMESPACE' do
kube_namespace = kubernetes.predefined_variables.find { |h| h[:key] == 'KUBE_NAMESPACE' }
expect(kube_namespace).not_to be_nil
expect(kube_namespace[:value]).to match(/\A#{Gitlab::PathRegex::PATH_REGEX_STR}-\d+\z/)
end
end
end
describe '#terminals' do
subject { service.terminals(environment) }
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") }
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(app: environment.slug) }
let(:terminals) { kube_terminals(service, pod) }
before do
stub_reactive_cache(
service,
pods: [pod, pod, kube_pod(app: "should-be-filtered-out")]
)
end
it 'returns terminals' do
is_expected.to eq(terminals + terminals)
end
it 'uses max session time from settings' do
stub_application_setting(terminal_max_session_time: 600)
times = subject.map { |terminal| terminal[:max_session_time] }
expect(times).to eq [600, 600, 600, 600]
end
end
end
describe '#calculate_reactive_cache' do
subject { service.calculate_reactive_cache }
let!(:cluster) { create(:cluster, :project, enabled: enabled, platform_kubernetes: service) }
let(:service) { create(:cluster_platform_kubernetes, :configured) }
let(:enabled) { true }
context 'when cluster is disabled' do
let(:enabled) { false }
it { is_expected.to be_nil }
end
context 'when kubernetes responds with valid pods' do
before do
stub_kubeclient_pods
end
it { is_expected.to eq(pods: [kube_pod]) }
end
context 'when kubernetes responds with 500s' do
before do
stub_kubeclient_pods(status: 500)
end
it { expect { subject }.to raise_error(KubeException) }
end
context 'when kubernetes responds with 404s' do
before do
stub_kubeclient_pods(status: 404)
end
it { is_expected.to eq(pods: []) }
end
end
end end

View file

@ -327,8 +327,7 @@ describe Environment do
context 'when the enviroment is available' do context 'when the enviroment is available' do
context 'with a deployment service' do context 'with a deployment service' do
let(:project) { create(:kubernetes_project) } shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
context 'and a deployment' do context 'and a deployment' do
let!(:deployment) { create(:deployment, environment: environment) } let!(:deployment) { create(:deployment, environment: environment) }
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
@ -339,6 +338,20 @@ describe Environment do
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
context 'without a deployment service' do context 'without a deployment service' do
it { is_expected.to be_falsy } it { is_expected.to be_falsy }
end end
@ -356,7 +369,6 @@ describe Environment do
end end
describe '#terminals' do describe '#terminals' do
let(:project) { create(:kubernetes_project) }
subject { environment.terminals } subject { environment.terminals }
context 'when the environment has terminals' do context 'when the environment has terminals' do
@ -364,8 +376,9 @@ describe Environment do
allow(environment).to receive(:has_terminals?).and_return(true) allow(environment).to receive(:has_terminals?).and_return(true)
end end
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'returns the terminals from the deployment service' do it 'returns the terminals from the deployment service' do
expect(project.deployment_service) expect(project.deployment_platform)
.to receive(:terminals).with(environment) .to receive(:terminals).with(environment)
.and_return(:fake_terminals) .and_return(:fake_terminals)
@ -373,6 +386,20 @@ describe Environment do
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
context 'when the environment does not have terminals' do context 'when the environment does not have terminals' do
before do before do
allow(environment).to receive(:has_terminals?).and_return(false) allow(environment).to receive(:has_terminals?).and_return(false)

View file

@ -4,8 +4,8 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
include KubernetesHelpers include KubernetesHelpers
include ReactiveCachingHelpers include ReactiveCachingHelpers
let(:project) { build_stubbed(:kubernetes_project) } let(:project) { create(:kubernetes_project) }
let(:service) { project.kubernetes_service } let(:service) { project.deployment_platform }
describe 'Associations' do describe 'Associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }

View file

@ -2002,14 +2002,27 @@ describe Project do
end end
context 'when project has a deployment service' do context 'when project has a deployment service' do
let(:project) { create(:kubernetes_project) } shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'returns variables from this service' do it 'returns variables from this service' do
expect(project.deployment_variables).to include( expect(project.deployment_variables).to include(
{ key: 'KUBE_TOKEN', value: project.kubernetes_service.token, public: false } { key: 'KUBE_TOKEN', value: project.deployment_platform.token, public: false }
) )
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
end end
describe '#secret_variables_for' do describe '#secret_variables_for' do
@ -3083,4 +3096,23 @@ describe Project do
expect(project.wiki_repository_exists?).to eq(false) expect(project.wiki_repository_exists?).to eq(false)
end end
end end
describe '#deployment_platform' do
subject { project.deployment_platform }
let(:project) { create(:project) }
context 'when user configured kubernetes from Integration > Kubernetes' do
let!(:kubernetes_service) { create(:kubernetes_service, project: project) }
it { is_expected.to eq(kubernetes_service) }
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let(:platform_kubernetes) { cluster.platform_kubernetes }
it { is_expected.to eq(platform_kubernetes) }
end
end
end end

View file

@ -41,9 +41,9 @@ RSpec.shared_examples 'additional metrics query' do
end end
describe 'project has Kubernetes service' do describe 'project has Kubernetes service' do
let(:project) { create(:kubernetes_project) } shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
let(:environment) { create(:environment, slug: 'environment-slug', project: project) } let(:environment) { create(:environment, slug: 'environment-slug', project: project) }
let(:kube_namespace) { project.kubernetes_service.actual_namespace } let(:kube_namespace) { project.deployment_platform.actual_namespace }
it_behaves_like 'query context containing environment slug and filter' it_behaves_like 'query context containing environment slug and filter'
@ -54,6 +54,20 @@ RSpec.shared_examples 'additional metrics query' do
end end
end end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
describe 'project without Kubernetes service' do describe 'project without Kubernetes service' do
it_behaves_like 'query context containing environment slug and filter' it_behaves_like 'query context containing environment slug and filter'

View file

@ -35,7 +35,7 @@ describe 'projects/pipelines_settings/_show' do
context 'when kubernetes is active' do context 'when kubernetes is active' do
before do before do
project.build_kubernetes_service(active: true) create(:kubernetes_service, project: project)
end end
context 'when auto devops domain is not defined' do context 'when auto devops domain is not defined' do

View file

@ -1,15 +1,28 @@
require 'spec_helper' require 'spec_helper'
describe ReactiveCachingWorker do describe ReactiveCachingWorker do
let(:project) { create(:kubernetes_project) } let(:service) { project.deployment_platform }
let(:service) { project.deployment_service }
subject { described_class.new.perform("KubernetesService", service.id) }
describe '#perform' do describe '#perform' do
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
it 'calls #exclusively_update_reactive_cache!' do it 'calls #exclusively_update_reactive_cache!' do
expect_any_instance_of(KubernetesService).to receive(:exclusively_update_reactive_cache!) expect_any_instance_of(KubernetesService).to receive(:exclusively_update_reactive_cache!)
subject described_class.new.perform("KubernetesService", service.id)
end
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
it 'calls #exclusively_update_reactive_cache!' do
expect_any_instance_of(Clusters::Platforms::Kubernetes).to receive(:exclusively_update_reactive_cache!)
described_class.new.perform("Clusters::Platforms::Kubernetes", service.id)
end
end end
end end
end end