Commit feece771 authored by Grzegorz Bizon's avatar Grzegorz Bizon

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
parents 41aa9fa7 7277b3b3
...@@ -41,7 +41,7 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -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)
......
...@@ -26,7 +26,7 @@ module AutoDevopsHelper ...@@ -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 = {
......
...@@ -365,7 +365,7 @@ module Ci ...@@ -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?
......
...@@ -17,8 +17,7 @@ module Clusters ...@@ -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 ...@@ -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
......
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 ...@@ -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,58 +51,127 @@ module Clusters ...@@ -51,58 +51,127 @@ 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 end
def kubeclient variables
@kubeclient ||= kubernetes_service.kubeclient if manages_kubernetes_service? 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
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?
def update_kubernetes_integration! # We may want to cache extra things in the future
raise 'Kubernetes service already configured' unless manages_kubernetes_service? { pods: read_pods }
end
# This is neccesary, otheriwse enabled? returns true even though cluster updated with enabled: false def kubeclient
cluster.reload @kubeclient ||= build_kubeclient!
end
ensure_kubernetes_service&.update!( private
active: enabled?,
api_url: api_url, def kubeconfig
namespace: namespace, to_kubeconfig(
url: api_url,
namespace: actual_namespace,
token: token, token: token,
ca_pem: ca_cert 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 end
def active? # Returns a hash of all pods in the namespace
manages_kubernetes_service? 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 end
private def kubeclient_ssl_options
opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
def enforce_namespace_to_lower_case if ca_pem.present?
self.namespace = self.namespace&.downcase opts[:cert_store] = OpenSSL::X509::Store.new
opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
end end
# TODO: glue code till we migrate Kubernetes Service into Platforms::Kubernetes class opts
def manages_kubernetes_service? end
return true unless kubernetes_service&.active?
kubernetes_service.api_url == api_url def kubeclient_auth_options
{ bearer_token: token }
end end
def destroy_kubernetes_integration! def join_api_url(api_path)
return unless manages_kubernetes_service? url = URI.parse(api_url)
prefix = url.path.sub(%r{/+\z}, '')
url.path = [prefix, api_path].join("/")
kubernetes_service&.destroy! url.to_s
end end
def kubernetes_service def terminal_auth
@kubernetes_service ||= project&.kubernetes_service {
token: token,
ca_pem: ca_pem,
max_session_time: current_application_settings.terminal_max_session_time
}
end end
def ensure_kubernetes_service def enforce_namespace_to_lower_case
@kubernetes_service ||= kubernetes_service || project&.build_kubernetes_service self.namespace = self.namespace&.downcase
end end
end end
end end
......
...@@ -138,11 +138,11 @@ class Environment < ActiveRecord::Base ...@@ -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?
......
...@@ -897,12 +897,10 @@ class Project < ActiveRecord::Base ...@@ -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 ...@@ -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
......
##
# 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
......
...@@ -5,7 +5,7 @@ ...@@ -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')
......
...@@ -147,7 +147,7 @@ ...@@ -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)
......
...@@ -67,7 +67,7 @@ ...@@ -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') }
......
...@@ -76,7 +76,7 @@ module Gitlab ...@@ -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
......
...@@ -113,11 +113,10 @@ describe Projects::BranchesController do ...@@ -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 ...@@ -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)
......
...@@ -4,15 +4,10 @@ describe 'Auto deploy' do ...@@ -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 ...@@ -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 ...@@ -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
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
...@@ -101,8 +101,7 @@ feature 'Environment' do ...@@ -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 ...@@ -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
......
...@@ -208,8 +208,7 @@ feature 'Environments page', :js do ...@@ -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 ...@@ -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
......
...@@ -4,14 +4,27 @@ describe Gitlab::Ci::Build::Policy::Kubernetes do ...@@ -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) }
......
...@@ -178,9 +178,7 @@ module Gitlab ...@@ -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 ...@@ -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)
......
...@@ -557,13 +557,26 @@ describe Ci::Pipeline, :mailer do ...@@ -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
......
...@@ -9,7 +9,6 @@ describe Clusters::Cluster do ...@@ -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
......
...@@ -5,6 +5,8 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching ...@@ -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,99 +92,175 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching ...@@ -90,99 +92,175 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end end
end end
describe 'after_save from Clusters::Cluster' do describe '#actual_namespace' do
context 'when platform_kubernetes is being cerated' do subject { kubernetes.actual_namespace }
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 let!(:cluster) { create(:cluster, :project, platform_kubernetes: kubernetes) }
cluster.save! let(:project) { cluster.project }
let(:kubernetes) { create(:cluster_platform_kubernetes, :configured, namespace: namespace) }
expect(kubernetes_service.active).to eq(enabled) context 'when namespace is present' do
expect(kubernetes_service.api_url).to eq(platform.api_url) let(:namespace) { 'namespace-123' }
expect(kubernetes_service.namespace).to eq(platform.namespace)
expect(kubernetes_service.ca_pem).to eq(platform.ca_cert) it { is_expected.to eq(namespace) }
end
context 'when namespace is not present' do
let(:namespace) { nil }
it { is_expected.to eq("#{project.path}-#{project.id}") }
end end
end end
context 'when platform_kubernetes has been created' do describe '#default_namespace' do
let(:enabled) { false } subject { kubernetes.send(:default_namespace) }
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 let(:kubernetes) { create(:cluster_platform_kubernetes, :configured) }
cluster.update(enabled: enabled)
expect(kubernetes_service.active).to eq(enabled) context 'when cluster belongs to a project' do
let!(:cluster) { create(:cluster, :project, platform_kubernetes: kubernetes) }
let(:project) { cluster.project }
it { is_expected.to eq("#{project.path}-#{project.id}") }
end
context 'when cluster belongs to nothing' do
let!(:cluster) { create(:cluster, platform_kubernetes: kubernetes) }
it { is_expected.to be_nil }
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
end end
context 'when kubernetes_service has been configured without cluster integration' do context 'namespace is provided' do
let!(:project) { create(:project) } let(:namespace) { 'my-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 before do
create(:kubernetes_service, project: project) kubernetes.namespace = namespace
end end
it 'raises an error' do it_behaves_like 'setting variables'
expect { cluster.save! }.to raise_error('Kubernetes service already configured') 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 end
end end
describe '#actual_namespace' do describe '#terminals' do
subject { kubernetes.actual_namespace } subject { service.terminals(environment) }
let!(:cluster) { create(:cluster, :project, platform_kubernetes: kubernetes) } let!(:cluster) { create(:cluster, :project, platform_kubernetes: service) }
let(:project) { cluster.project } let(:project) { cluster.project }
let(:kubernetes) { create(:cluster_platform_kubernetes, :configured, namespace: namespace) } let(:service) { create(:cluster_platform_kubernetes, :configured) }
let(:environment) { build(:environment, project: project, name: "env", slug: "env-000000") }
context 'when namespace is present' do context 'with invalid pods' do
let(:namespace) { 'namespace-123' } it 'returns no terminals' do
stub_reactive_cache(service, pods: [{ "bad" => "pod" }])
it { is_expected.to eq(namespace) } is_expected.to be_empty
end
end end
context 'when namespace is not present' do context 'with valid pods' do
let(:namespace) { nil } let(:pod) { kube_pod(app: environment.slug) }
let(:terminals) { kube_terminals(service, pod) }
it { is_expected.to eq("#{project.path}-#{project.id}") } before do
stub_reactive_cache(
service,
pods: [pod, pod, kube_pod(app: "should-be-filtered-out")]
)
end end
it 'returns terminals' do
is_expected.to eq(terminals + terminals)
end end
describe '.namespace_for_project' do it 'uses max session time from settings' do
subject { described_class.namespace_for_project(project) } 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
let(:project) { create(:project) } describe '#calculate_reactive_cache' do
subject { service.calculate_reactive_cache }
it { is_expected.to eq("#{project.path}-#{project.id}") } 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 end
describe '#default_namespace' do context 'when kubernetes responds with valid pods' do
subject { kubernetes.default_namespace } before do
stub_kubeclient_pods
end
let(:kubernetes) { create(:cluster_platform_kubernetes, :configured) } it { is_expected.to eq(pods: [kube_pod]) }
end
context 'when cluster belongs to a project' do context 'when kubernetes responds with 500s' do
let!(:cluster) { create(:cluster, :project, platform_kubernetes: kubernetes) } before do
let(:project) { cluster.project } stub_kubeclient_pods(status: 500)
end
it { is_expected.to eq("#{project.path}-#{project.id}") } it { expect { subject }.to raise_error(KubeException) }
end end
context 'when cluster belongs to nothing' do context 'when kubernetes responds with 404s' do
let!(:cluster) { create(:cluster, platform_kubernetes: kubernetes) } before do
stub_kubeclient_pods(status: 404)
end
it { is_expected.to be_nil } it { is_expected.to eq(pods: []) }
end end
end end
end end
...@@ -327,8 +327,7 @@ describe Environment do ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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)
......
...@@ -4,8 +4,8 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do ...@@ -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 }
......
...@@ -2002,14 +2002,27 @@ describe Project do ...@@ -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 ...@@ -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
...@@ -41,9 +41,9 @@ RSpec.shared_examples 'additional metrics query' do ...@@ -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 ...@@ -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'
......
...@@ -35,7 +35,7 @@ describe 'projects/pipelines_settings/_show' do ...@@ -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
......
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment