Merge branch 'top_level_clusters_controller' into 'master'
Top level clusters controller See merge request gitlab-org/gitlab-ce!22438
This commit is contained in:
commit
46fd31594e
|
@ -1,5 +0,0 @@
|
|||
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initGkeDropdowns();
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Clusters::ApplicationsController < Clusters::BaseController
|
||||
before_action :cluster
|
||||
before_action :authorize_create_cluster!, only: [:create]
|
||||
|
||||
def create
|
||||
Clusters::Applications::CreateService
|
||||
.new(@cluster, current_user, create_cluster_application_params)
|
||||
.execute(request)
|
||||
|
||||
head :no_content
|
||||
rescue Clusters::Applications::CreateService::InvalidApplicationError
|
||||
render_404
|
||||
rescue StandardError
|
||||
head :bad_request
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cluster
|
||||
@cluster ||= clusterable.clusters.find(params[:id]) || render_404
|
||||
end
|
||||
|
||||
def create_cluster_application_params
|
||||
params.permit(:application, :hostname)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Clusters::BaseController < ApplicationController
|
||||
include RoutableActions
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
before_action :authorize_read_cluster!
|
||||
|
||||
helper_method :clusterable
|
||||
|
||||
private
|
||||
|
||||
def cluster
|
||||
@cluster ||= clusterable.clusters.find(params[:id])
|
||||
.present(current_user: current_user)
|
||||
end
|
||||
|
||||
def authorize_update_cluster!
|
||||
access_denied! unless can?(current_user, :update_cluster, cluster)
|
||||
end
|
||||
|
||||
def authorize_admin_cluster!
|
||||
access_denied! unless can?(current_user, :admin_cluster, cluster)
|
||||
end
|
||||
|
||||
def authorize_read_cluster!
|
||||
access_denied! unless can?(current_user, :read_cluster, clusterable)
|
||||
end
|
||||
|
||||
def authorize_create_cluster!
|
||||
access_denied! unless can?(current_user, :create_cluster, clusterable)
|
||||
end
|
||||
|
||||
def clusterable
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
|
@ -0,0 +1,218 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Clusters::ClustersController < Clusters::BaseController
|
||||
include RoutableActions
|
||||
|
||||
before_action :cluster, except: [:index, :new, :create_gcp, :create_user]
|
||||
before_action :generate_gcp_authorize_url, only: [:new]
|
||||
before_action :validate_gcp_token, only: [:new]
|
||||
before_action :gcp_cluster, only: [:new]
|
||||
before_action :user_cluster, only: [:new]
|
||||
before_action :authorize_create_cluster!, only: [:new]
|
||||
before_action :authorize_update_cluster!, only: [:update]
|
||||
before_action :authorize_admin_cluster!, only: [:destroy]
|
||||
before_action :update_applications_status, only: [:cluster_status]
|
||||
|
||||
helper_method :token_in_session
|
||||
|
||||
STATUS_POLLING_INTERVAL = 10_000
|
||||
|
||||
def index
|
||||
clusters = ClustersFinder.new(clusterable, current_user, :all).execute
|
||||
@clusters = clusters.page(params[:page]).per(20)
|
||||
end
|
||||
|
||||
def new
|
||||
end
|
||||
|
||||
# Overridding ActionController::Metal#status is NOT a good idea
|
||||
def cluster_status
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL)
|
||||
|
||||
render json: ClusterSerializer
|
||||
.new(current_user: @current_user)
|
||||
.represent_status(@cluster)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def update
|
||||
Clusters::UpdateService
|
||||
.new(current_user, update_params)
|
||||
.execute(cluster)
|
||||
|
||||
if cluster.valid?
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
head :no_content
|
||||
end
|
||||
format.html do
|
||||
flash[:notice] = _('Kubernetes cluster was successfully updated.')
|
||||
redirect_to cluster.show_path
|
||||
end
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.json { head :bad_request }
|
||||
format.html { render :show }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if cluster.destroy
|
||||
flash[:notice] = _('Kubernetes cluster integration was successfully removed.')
|
||||
redirect_to clusterable.index_path, status: :found
|
||||
else
|
||||
flash[:notice] = _('Kubernetes cluster integration was not removed.')
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
def create_gcp
|
||||
@gcp_cluster = ::Clusters::CreateService
|
||||
.new(current_user, create_gcp_cluster_params)
|
||||
.execute(access_token: token_in_session)
|
||||
.present(current_user: current_user)
|
||||
|
||||
if @gcp_cluster.persisted?
|
||||
redirect_to @gcp_cluster.show_path
|
||||
else
|
||||
generate_gcp_authorize_url
|
||||
validate_gcp_token
|
||||
user_cluster
|
||||
|
||||
render :new, locals: { active_tab: 'gcp' }
|
||||
end
|
||||
end
|
||||
|
||||
def create_user
|
||||
@user_cluster = ::Clusters::CreateService
|
||||
.new(current_user, create_user_cluster_params)
|
||||
.execute(access_token: token_in_session)
|
||||
.present(current_user: current_user)
|
||||
|
||||
if @user_cluster.persisted?
|
||||
redirect_to @user_cluster.show_path
|
||||
else
|
||||
generate_gcp_authorize_url
|
||||
validate_gcp_token
|
||||
gcp_cluster
|
||||
|
||||
render :new, locals: { active_tab: 'user' }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_params
|
||||
if cluster.managed?
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:environment_scope,
|
||||
platform_kubernetes_attributes: [
|
||||
:namespace
|
||||
]
|
||||
)
|
||||
else
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
:environment_scope,
|
||||
platform_kubernetes_attributes: [
|
||||
:api_url,
|
||||
:token,
|
||||
:ca_cert,
|
||||
:namespace
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def create_gcp_cluster_params
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
:environment_scope,
|
||||
provider_gcp_attributes: [
|
||||
:gcp_project_id,
|
||||
:zone,
|
||||
:num_nodes,
|
||||
:machine_type,
|
||||
:legacy_abac
|
||||
]).merge(
|
||||
provider_type: :gcp,
|
||||
platform_type: :kubernetes,
|
||||
clusterable: clusterable.subject
|
||||
)
|
||||
end
|
||||
|
||||
def create_user_cluster_params
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
:environment_scope,
|
||||
platform_kubernetes_attributes: [
|
||||
:namespace,
|
||||
:api_url,
|
||||
:token,
|
||||
:ca_cert,
|
||||
:authorization_type
|
||||
]).merge(
|
||||
provider_type: :user,
|
||||
platform_type: :kubernetes,
|
||||
clusterable: clusterable.subject
|
||||
)
|
||||
end
|
||||
|
||||
def generate_gcp_authorize_url
|
||||
state = generate_session_key_redirect(clusterable.new_path.to_s)
|
||||
|
||||
@authorize_url = GoogleApi::CloudPlatform::Client.new(
|
||||
nil, callback_google_api_auth_url,
|
||||
state: state).authorize_url
|
||||
rescue GoogleApi::Auth::ConfigMissingError
|
||||
# no-op
|
||||
end
|
||||
|
||||
def gcp_cluster
|
||||
@gcp_cluster = ::Clusters::Cluster.new.tap do |cluster|
|
||||
cluster.build_provider_gcp
|
||||
end
|
||||
end
|
||||
|
||||
def user_cluster
|
||||
@user_cluster = ::Clusters::Cluster.new.tap do |cluster|
|
||||
cluster.build_platform_kubernetes
|
||||
end
|
||||
end
|
||||
|
||||
def validate_gcp_token
|
||||
@valid_gcp_token = GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
|
||||
.validate_token(expires_at_in_session)
|
||||
end
|
||||
|
||||
def token_in_session
|
||||
session[GoogleApi::CloudPlatform::Client.session_key_for_token]
|
||||
end
|
||||
|
||||
def expires_at_in_session
|
||||
@expires_at_in_session ||=
|
||||
session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at]
|
||||
end
|
||||
|
||||
def generate_session_key_redirect(uri)
|
||||
GoogleApi::CloudPlatform::Client.new_session_key_for_redirect_uri do |key|
|
||||
session[key] = uri
|
||||
end
|
||||
end
|
||||
|
||||
def update_applications_status
|
||||
@cluster.applications.each(&:schedule_status_update)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ProjectUnauthorized
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# EE would override this
|
||||
def project_unauthorized_proc
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -3,23 +3,25 @@
|
|||
module RoutableActions
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def find_routable!(routable_klass, requested_full_path, extra_authorization_proc: nil)
|
||||
def find_routable!(routable_klass, requested_full_path, extra_authorization_proc: nil, not_found_or_authorized_proc: nil)
|
||||
routable = routable_klass.find_by_full_path(requested_full_path, follow_redirects: request.get?)
|
||||
if routable_authorized?(routable, extra_authorization_proc)
|
||||
ensure_canonical_path(routable, requested_full_path)
|
||||
routable
|
||||
else
|
||||
handle_not_found_or_authorized(routable)
|
||||
if not_found_or_authorized_proc
|
||||
not_found_or_authorized_proc.call(routable)
|
||||
end
|
||||
|
||||
route_not_found unless performed?
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# This is overridden in gitlab-ee.
|
||||
def handle_not_found_or_authorized(_routable)
|
||||
route_not_found
|
||||
end
|
||||
|
||||
def routable_authorized?(routable, extra_authorization_proc)
|
||||
return false unless routable
|
||||
|
||||
action = :"read_#{routable.class.to_s.underscore}"
|
||||
return false unless can?(current_user, action, routable)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class Projects::ApplicationController < ApplicationController
|
||||
include CookiesHelper
|
||||
include RoutableActions
|
||||
include ProjectUnauthorized
|
||||
include ChecksCollaboration
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
|
@ -21,7 +22,7 @@ class Projects::ApplicationController < ApplicationController
|
|||
path = File.join(params[:namespace_id], params[:project_id] || params[:id])
|
||||
auth_proc = ->(project) { !project.pending_delete? }
|
||||
|
||||
@project = find_routable!(Project, path, extra_authorization_proc: auth_proc)
|
||||
@project = find_routable!(Project, path, extra_authorization_proc: auth_proc, not_found_or_authorized_proc: project_unauthorized_proc)
|
||||
end
|
||||
|
||||
def build_canonical_path(project)
|
||||
|
|
|
@ -1,29 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::Clusters::ApplicationsController < Projects::ApplicationController
|
||||
before_action :cluster
|
||||
before_action :authorize_read_cluster!
|
||||
before_action :authorize_create_cluster!, only: [:create]
|
||||
class Projects::Clusters::ApplicationsController < Clusters::ApplicationsController
|
||||
include ProjectUnauthorized
|
||||
|
||||
def create
|
||||
Clusters::Applications::CreateService
|
||||
.new(@cluster, current_user, create_cluster_application_params)
|
||||
.execute(request)
|
||||
|
||||
head :no_content
|
||||
rescue Clusters::Applications::CreateService::InvalidApplicationError
|
||||
render_404
|
||||
rescue StandardError
|
||||
head :bad_request
|
||||
end
|
||||
prepend_before_action :project
|
||||
|
||||
private
|
||||
|
||||
def cluster
|
||||
@cluster ||= project.clusters.find(params[:id]) || render_404
|
||||
def clusterable
|
||||
@clusterable ||= ClusterablePresenter.fabricate(project, current_user: current_user)
|
||||
end
|
||||
|
||||
def create_cluster_application_params
|
||||
params.permit(:application, :hostname)
|
||||
def project
|
||||
@project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), not_found_or_authorized_proc: project_unauthorized_proc)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,224 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::ClustersController < Projects::ApplicationController
|
||||
before_action :cluster, except: [:index, :new, :create_gcp, :create_user]
|
||||
before_action :authorize_read_cluster!
|
||||
before_action :generate_gcp_authorize_url, only: [:new]
|
||||
before_action :validate_gcp_token, only: [:new]
|
||||
before_action :gcp_cluster, only: [:new]
|
||||
before_action :user_cluster, only: [:new]
|
||||
before_action :authorize_create_cluster!, only: [:new]
|
||||
before_action :authorize_update_cluster!, only: [:update]
|
||||
before_action :authorize_admin_cluster!, only: [:destroy]
|
||||
before_action :update_applications_status, only: [:status]
|
||||
helper_method :token_in_session
|
||||
class Projects::ClustersController < Clusters::ClustersController
|
||||
include ProjectUnauthorized
|
||||
|
||||
STATUS_POLLING_INTERVAL = 10_000
|
||||
prepend_before_action :project
|
||||
before_action :repository
|
||||
|
||||
def index
|
||||
clusters = ClustersFinder.new(project, current_user, :all).execute
|
||||
@clusters = clusters.page(params[:page]).per(20)
|
||||
end
|
||||
|
||||
def new
|
||||
end
|
||||
|
||||
def status
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL)
|
||||
|
||||
render json: ClusterSerializer
|
||||
.new(project: @project, current_user: @current_user)
|
||||
.represent_status(@cluster)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def update
|
||||
Clusters::UpdateService
|
||||
.new(current_user, update_params)
|
||||
.execute(cluster)
|
||||
|
||||
if cluster.valid?
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
head :no_content
|
||||
end
|
||||
format.html do
|
||||
flash[:notice] = _('Kubernetes cluster was successfully updated.')
|
||||
redirect_to project_cluster_path(project, cluster)
|
||||
end
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.json { head :bad_request }
|
||||
format.html { render :show }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if cluster.destroy
|
||||
flash[:notice] = _('Kubernetes cluster integration was successfully removed.')
|
||||
redirect_to project_clusters_path(project), status: :found
|
||||
else
|
||||
flash[:notice] = _('Kubernetes cluster integration was not removed.')
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
def create_gcp
|
||||
@gcp_cluster = ::Clusters::CreateService
|
||||
.new(current_user, create_gcp_cluster_params)
|
||||
.execute(project: project, access_token: token_in_session)
|
||||
|
||||
if @gcp_cluster.persisted?
|
||||
redirect_to project_cluster_path(project, @gcp_cluster)
|
||||
else
|
||||
generate_gcp_authorize_url
|
||||
validate_gcp_token
|
||||
user_cluster
|
||||
|
||||
render :new, locals: { active_tab: 'gcp' }
|
||||
end
|
||||
end
|
||||
|
||||
def create_user
|
||||
@user_cluster = ::Clusters::CreateService
|
||||
.new(current_user, create_user_cluster_params)
|
||||
.execute(project: project, access_token: token_in_session)
|
||||
|
||||
if @user_cluster.persisted?
|
||||
redirect_to project_cluster_path(project, @user_cluster)
|
||||
else
|
||||
generate_gcp_authorize_url
|
||||
validate_gcp_token
|
||||
gcp_cluster
|
||||
|
||||
render :new, locals: { active_tab: 'user' }
|
||||
end
|
||||
end
|
||||
layout 'project'
|
||||
|
||||
private
|
||||
|
||||
def cluster
|
||||
@cluster ||= project.clusters.find(params[:id])
|
||||
.present(current_user: current_user)
|
||||
def clusterable
|
||||
@clusterable ||= ClusterablePresenter.fabricate(project, current_user: current_user)
|
||||
end
|
||||
|
||||
def update_params
|
||||
if cluster.managed?
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:environment_scope,
|
||||
platform_kubernetes_attributes: [
|
||||
:namespace
|
||||
]
|
||||
)
|
||||
else
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
:environment_scope,
|
||||
platform_kubernetes_attributes: [
|
||||
:api_url,
|
||||
:token,
|
||||
:ca_cert,
|
||||
:namespace
|
||||
]
|
||||
)
|
||||
end
|
||||
def project
|
||||
@project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), not_found_or_authorized_proc: project_unauthorized_proc)
|
||||
end
|
||||
|
||||
def create_gcp_cluster_params
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
:environment_scope,
|
||||
provider_gcp_attributes: [
|
||||
:gcp_project_id,
|
||||
:zone,
|
||||
:num_nodes,
|
||||
:machine_type,
|
||||
:legacy_abac
|
||||
]).merge(
|
||||
provider_type: :gcp,
|
||||
platform_type: :kubernetes
|
||||
)
|
||||
end
|
||||
|
||||
def create_user_cluster_params
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
:environment_scope,
|
||||
platform_kubernetes_attributes: [
|
||||
:namespace,
|
||||
:api_url,
|
||||
:token,
|
||||
:ca_cert,
|
||||
:authorization_type
|
||||
]).merge(
|
||||
provider_type: :user,
|
||||
platform_type: :kubernetes
|
||||
)
|
||||
end
|
||||
|
||||
def generate_gcp_authorize_url
|
||||
state = generate_session_key_redirect(new_project_cluster_path(@project).to_s)
|
||||
|
||||
@authorize_url = GoogleApi::CloudPlatform::Client.new(
|
||||
nil, callback_google_api_auth_url,
|
||||
state: state).authorize_url
|
||||
rescue GoogleApi::Auth::ConfigMissingError
|
||||
# no-op
|
||||
end
|
||||
|
||||
def gcp_cluster
|
||||
@gcp_cluster = ::Clusters::Cluster.new.tap do |cluster|
|
||||
cluster.build_provider_gcp
|
||||
end
|
||||
end
|
||||
|
||||
def user_cluster
|
||||
@user_cluster = ::Clusters::Cluster.new.tap do |cluster|
|
||||
cluster.build_platform_kubernetes
|
||||
end
|
||||
end
|
||||
|
||||
def validate_gcp_token
|
||||
@valid_gcp_token = GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
|
||||
.validate_token(expires_at_in_session)
|
||||
end
|
||||
|
||||
def token_in_session
|
||||
session[GoogleApi::CloudPlatform::Client.session_key_for_token]
|
||||
end
|
||||
|
||||
def expires_at_in_session
|
||||
@expires_at_in_session ||=
|
||||
session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at]
|
||||
end
|
||||
|
||||
def generate_session_key_redirect(uri)
|
||||
GoogleApi::CloudPlatform::Client.new_session_key_for_redirect_uri do |key|
|
||||
session[key] = uri
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_update_cluster!
|
||||
access_denied! unless can?(current_user, :update_cluster, cluster)
|
||||
end
|
||||
|
||||
def authorize_admin_cluster!
|
||||
access_denied! unless can?(current_user, :admin_cluster, cluster)
|
||||
end
|
||||
|
||||
def update_applications_status
|
||||
@cluster.applications.each(&:schedule_status_update)
|
||||
def repository
|
||||
@repository ||= project.repository
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ClustersFinder
|
||||
def initialize(project, user, scope)
|
||||
@project = project
|
||||
def initialize(clusterable, user, scope)
|
||||
@clusterable = clusterable
|
||||
@user = user
|
||||
@scope = scope || :active
|
||||
end
|
||||
|
||||
def execute
|
||||
clusters = project.clusters
|
||||
clusters = clusterable.clusters
|
||||
filter_by_scope(clusters)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :project, :user, :scope
|
||||
attr_reader :clusterable, :user, :scope
|
||||
|
||||
def filter_by_scope(clusters)
|
||||
case scope.to_sym
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ClustersHelper
|
||||
def has_multiple_clusters?(project)
|
||||
# EE overrides this
|
||||
def has_multiple_clusters?
|
||||
false
|
||||
end
|
||||
|
||||
|
@ -10,7 +11,7 @@ module ClustersHelper
|
|||
return unless show_gcp_signup_offer?
|
||||
|
||||
content_tag :section, class: 'no-animate expanded' do
|
||||
render 'projects/clusters/gcp_signup_offer_banner'
|
||||
render 'clusters/clusters/gcp_signup_offer_banner'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ClusterablePresenter < Gitlab::View::Presenter::Delegated
|
||||
presents :clusterable
|
||||
|
||||
def self.fabricate(clusterable, **attributes)
|
||||
presenter_class = "#{clusterable.class.name}ClusterablePresenter".constantize
|
||||
attributes_with_presenter_class = attributes.merge(presenter_class: presenter_class)
|
||||
|
||||
Gitlab::View::Presenter::Factory
|
||||
.new(clusterable, attributes_with_presenter_class)
|
||||
.fabricate!
|
||||
end
|
||||
|
||||
def can_create_cluster?
|
||||
can?(current_user, :create_cluster, clusterable)
|
||||
end
|
||||
|
||||
def index_path
|
||||
polymorphic_path([clusterable, :clusters])
|
||||
end
|
||||
|
||||
def new_path
|
||||
new_polymorphic_path([clusterable, :cluster])
|
||||
end
|
||||
|
||||
def create_user_clusters_path
|
||||
polymorphic_path([clusterable, :clusters], action: :create_user)
|
||||
end
|
||||
|
||||
def create_gcp_clusters_path
|
||||
polymorphic_path([clusterable, :clusters], action: :create_gcp)
|
||||
end
|
||||
|
||||
def cluster_status_cluster_path(cluster, params = {})
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def install_applications_cluster_path(cluster, application)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def cluster_path(cluster, params = {})
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
|
@ -11,5 +11,13 @@ module Clusters
|
|||
def can_toggle_cluster?
|
||||
can?(current_user, :update_cluster, cluster) && created?
|
||||
end
|
||||
|
||||
def show_path
|
||||
if cluster.project_type?
|
||||
project_cluster_path(project, cluster)
|
||||
else
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProjectClusterablePresenter < ClusterablePresenter
|
||||
def cluster_status_cluster_path(cluster, params = {})
|
||||
cluster_status_project_cluster_path(clusterable, cluster, params)
|
||||
end
|
||||
|
||||
def install_applications_cluster_path(cluster, application)
|
||||
install_applications_project_cluster_path(clusterable, cluster, application)
|
||||
end
|
||||
|
||||
def cluster_path(cluster, params = {})
|
||||
project_cluster_path(clusterable, cluster, params)
|
||||
end
|
||||
end
|
|
@ -8,10 +8,11 @@ module Clusters
|
|||
@current_user, @params = user, params.dup
|
||||
end
|
||||
|
||||
def execute(project:, access_token: nil)
|
||||
raise ArgumentError, _('Instance does not support multiple Kubernetes clusters') unless can_create_cluster?(project)
|
||||
def execute(access_token: nil)
|
||||
raise ArgumentError, 'Unknown clusterable provided' unless clusterable
|
||||
raise ArgumentError, _('Instance does not support multiple Kubernetes clusters') unless can_create_cluster?
|
||||
|
||||
cluster_params = params.merge(user: current_user, cluster_type: :project_type, projects: [project])
|
||||
cluster_params = params.merge(user: current_user).merge(clusterable_params)
|
||||
cluster_params[:provider_gcp_attributes].try do |provider|
|
||||
provider[:access_token] = access_token
|
||||
end
|
||||
|
@ -27,9 +28,20 @@ module Clusters
|
|||
Clusters::Cluster.create(cluster_params)
|
||||
end
|
||||
|
||||
def clusterable
|
||||
@clusterable ||= params.delete(:clusterable)
|
||||
end
|
||||
|
||||
def clusterable_params
|
||||
case clusterable
|
||||
when ::Project
|
||||
{ cluster_type: :project_type, projects: [clusterable] }
|
||||
end
|
||||
end
|
||||
|
||||
# EE would override this method
|
||||
def can_create_cluster?(project)
|
||||
project.clusters.empty?
|
||||
def can_create_cluster?
|
||||
clusterable.clusters.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
= s_('ClusterIntegration|Remove Kubernetes cluster integration')
|
||||
%p
|
||||
= s_("ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster.")
|
||||
= link_to(s_('ClusterIntegration|Remove integration'), namespace_project_cluster_path(@project.namespace, @project, @cluster.id), method: :delete, class: 'btn btn-danger', data: { confirm: s_("ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster.")})
|
||||
= link_to(s_('ClusterIntegration|Remove integration'), clusterable.cluster_path(@cluster), method: :delete, class: 'btn btn-danger', data: { confirm: s_("ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster.")})
|
|
@ -2,7 +2,7 @@
|
|||
.table-section.section-30
|
||||
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Kubernetes cluster")
|
||||
.table-mobile-content
|
||||
= link_to cluster.name, namespace_project_cluster_path(@project.namespace, @project, cluster)
|
||||
= link_to cluster.name, cluster.show_path
|
||||
.table-section.section-30
|
||||
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Environment scope")
|
||||
.table-mobile-content= cluster.environment_scope
|
||||
|
@ -16,7 +16,7 @@
|
|||
class: "#{'is-checked' if cluster.enabled?} #{'is-disabled' if !cluster.can_toggle_cluster?}",
|
||||
"aria-label": s_("ClusterIntegration|Toggle Kubernetes Cluster"),
|
||||
disabled: !cluster.can_toggle_cluster?,
|
||||
data: { endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } }
|
||||
data: { endpoint: clusterable.cluster_path(cluster, format: :json) } }
|
||||
%input.js-project-feature-toggle-input{ type: "hidden", value: cluster.enabled? }
|
||||
= icon("spinner spin", class: "loading-icon")
|
||||
%span.toggle-icon
|
|
@ -7,6 +7,6 @@
|
|||
- link_to_help_page = link_to(_('Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
|
||||
%p= s_('ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page}
|
||||
|
||||
- if can?(current_user, :create_cluster, @project)
|
||||
- if clusterable.can_create_cluster?
|
||||
.text-center
|
||||
= link_to s_('ClusterIntegration|Add Kubernetes cluster'), new_project_cluster_path(@project), class: 'btn btn-success'
|
||||
= link_to s_('ClusterIntegration|Add Kubernetes cluster'), clusterable.new_path, class: 'btn btn-success'
|
|
@ -1,4 +1,4 @@
|
|||
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
|
||||
= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
.form-group
|
||||
%h5= s_('ClusterIntegration|Integration status')
|
||||
|
@ -13,7 +13,7 @@
|
|||
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
|
||||
.form-text.text-muted= s_('ClusterIntegration|Enable or disable GitLab\'s connection to your Kubernetes cluster.')
|
||||
|
||||
- if has_multiple_clusters?(@project)
|
||||
- if has_multiple_clusters?
|
||||
.form-group
|
||||
%h5= s_('ClusterIntegration|Environment scope')
|
||||
= field.text_field :environment_scope, class: 'col-md-6 form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Environment scope')
|
||||
|
@ -23,7 +23,7 @@
|
|||
.form-group
|
||||
= field.submit _('Save changes'), class: 'btn btn-success'
|
||||
|
||||
- unless has_multiple_clusters?(@project)
|
||||
- unless has_multiple_clusters?
|
||||
%h5= s_('ClusterIntegration|Environment scope')
|
||||
%p
|
||||
%code *
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
%p= link_to('Select a different Google account', @authorize_url)
|
||||
|
||||
= form_for @gcp_cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: create_gcp_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
|
||||
= form_for @gcp_cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: clusterable.create_gcp_clusters_path, as: :cluster do |field|
|
||||
= form_errors(@gcp_cluster)
|
||||
.form-group
|
||||
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
|
||||
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
|
||||
.form-group
|
||||
= field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
|
||||
= field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
|
||||
= field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?, placeholder: s_('ClusterIntegration|Environment scope')
|
||||
|
||||
= field.fields_for :provider_gcp, @gcp_cluster.provider_gcp do |provider_gcp_field|
|
||||
.form-group
|
|
@ -6,7 +6,7 @@
|
|||
%span.input-group-append
|
||||
= clipboard_button(text: @cluster.name, title: s_('ClusterIntegration|Copy Kubernetes cluster name'), class: 'input-group-text btn-default')
|
||||
|
||||
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
|
||||
= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
|
||||
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
.tab-content.gitlab-tab-content
|
||||
.tab-pane{ id: 'create-gcp-cluster-pane', class: active_when(active_tab == 'gcp'), role: 'tabpanel' }
|
||||
= render 'projects/clusters/gcp/header'
|
||||
= render 'clusters/clusters/gcp/header'
|
||||
- if @valid_gcp_token
|
||||
= render 'projects/clusters/gcp/form'
|
||||
= render 'clusters/clusters/gcp/form'
|
||||
- elsif @authorize_url
|
||||
.signin-with-google
|
||||
= link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px'), @authorize_url)
|
||||
|
@ -32,5 +32,5 @@
|
|||
= s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link }
|
||||
|
||||
.tab-pane{ id: 'add-user-cluster-pane', class: active_when(active_tab == 'user'), role: 'tabpanel' }
|
||||
= render 'projects/clusters/user/header'
|
||||
= render 'projects/clusters/user/form'
|
||||
= render 'clusters/clusters/user/header'
|
||||
= render 'clusters/clusters/user/form'
|
|
@ -1,24 +1,25 @@
|
|||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
- add_to_breadcrumbs "Kubernetes Clusters", project_clusters_path(@project)
|
||||
- add_to_breadcrumbs "Kubernetes Clusters", clusterable.index_path
|
||||
- breadcrumb_title @cluster.name
|
||||
- page_title _("Kubernetes Cluster")
|
||||
- manage_prometheus_path = edit_project_service_path(@cluster.project, 'prometheus') if @project
|
||||
|
||||
- expanded = Rails.env.test?
|
||||
|
||||
- status_path = status_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster.id, format: :json) if can?(current_user, :admin_cluster, @cluster)
|
||||
- status_path = clusterable.cluster_status_cluster_path(@cluster.id, format: :json) if can?(current_user, :admin_cluster, @cluster)
|
||||
.edit-cluster-form.js-edit-cluster-form{ data: { status_path: status_path,
|
||||
install_helm_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :helm),
|
||||
install_ingress_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :ingress),
|
||||
install_prometheus_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :prometheus),
|
||||
install_runner_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :runner),
|
||||
install_jupyter_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :jupyter),
|
||||
install_helm_path: clusterable.install_applications_cluster_path(@cluster, :helm),
|
||||
install_ingress_path: clusterable.install_applications_cluster_path(@cluster, :ingress),
|
||||
install_prometheus_path: clusterable.install_applications_cluster_path(@cluster, :prometheus),
|
||||
install_runner_path: clusterable.install_applications_cluster_path(@cluster, :runner),
|
||||
install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
|
||||
toggle_status: @cluster.enabled? ? 'true': 'false',
|
||||
cluster_status: @cluster.status_name,
|
||||
cluster_status_reason: @cluster.status_reason,
|
||||
help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
|
||||
ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-ip-address'),
|
||||
ingress_dns_help_path: help_page_path('topics/autodevops/quick_start_guide.md', anchor: 'point-dns-at-cluster-ip'),
|
||||
manage_prometheus_path: edit_project_service_path(@cluster.project, 'prometheus') } }
|
||||
manage_prometheus_path: manage_prometheus_path } }
|
||||
|
||||
.js-cluster-application-notice
|
||||
.flash-container
|
||||
|
@ -38,9 +39,9 @@
|
|||
%p= s_('ClusterIntegration|See and edit the details for your Kubernetes cluster')
|
||||
.settings-content
|
||||
- if @cluster.managed?
|
||||
= render 'projects/clusters/gcp/show'
|
||||
= render 'clusters/clusters/gcp/show'
|
||||
- else
|
||||
= render 'projects/clusters/user/show'
|
||||
= render 'clusters/clusters/user/show'
|
||||
|
||||
%section.settings.no-animate#js-cluster-advanced-settings{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
|
@ -1,9 +1,9 @@
|
|||
= form_for @user_cluster, url: create_user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
|
||||
= form_for @user_cluster, url: clusterable.create_user_clusters_path, as: :cluster do |field|
|
||||
= form_errors(@user_cluster)
|
||||
.form-group
|
||||
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
|
||||
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
|
||||
- if has_multiple_clusters?(@project)
|
||||
- if has_multiple_clusters?
|
||||
.form-group
|
||||
= field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
|
||||
= field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope')
|
|
@ -1,4 +1,4 @@
|
|||
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
|
||||
= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
.form-group
|
||||
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Change to top level controller for clusters so that we can use it for project
|
||||
clusters (now) and group clusters (later)
|
||||
merge_request: 22438
|
||||
author:
|
||||
type: other
|
|
@ -84,6 +84,23 @@ Rails.application.routes.draw do
|
|||
draw :instance_statistics
|
||||
end
|
||||
|
||||
concern :clusterable do
|
||||
resources :clusters, only: [:index, :new, :show, :update, :destroy] do
|
||||
collection do
|
||||
post :create_user
|
||||
post :create_gcp
|
||||
end
|
||||
|
||||
member do
|
||||
scope :applications do
|
||||
post '/:application', to: 'clusters/applications#create', as: :install_applications
|
||||
end
|
||||
|
||||
get :cluster_status, format: :json
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
draw :api
|
||||
draw :sidekiq
|
||||
draw :help
|
||||
|
|
|
@ -206,20 +206,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
end
|
||||
end
|
||||
|
||||
resources :clusters, except: [:edit, :create] do
|
||||
collection do
|
||||
post :create_gcp
|
||||
post :create_user
|
||||
end
|
||||
|
||||
member do
|
||||
get :status, format: :json
|
||||
|
||||
scope :applications do
|
||||
post '/:application', to: 'clusters/applications#create', as: :install_applications
|
||||
end
|
||||
end
|
||||
end
|
||||
concerns :clusterable
|
||||
|
||||
resources :environments, except: [:destroy] do
|
||||
member do
|
||||
|
|
|
@ -4,7 +4,7 @@ module QA
|
|||
module Operations
|
||||
module Kubernetes
|
||||
class Add < Page::Base
|
||||
view 'app/views/projects/clusters/new.html.haml' do
|
||||
view 'app/views/clusters/clusters/new.html.haml' do
|
||||
element :add_existing_cluster_button, "Add existing cluster" # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module QA
|
|||
module Operations
|
||||
module Kubernetes
|
||||
class AddExisting < Page::Base
|
||||
view 'app/views/projects/clusters/user/_form.html.haml' do
|
||||
view 'app/views/clusters/clusters/user/_form.html.haml' do
|
||||
element :cluster_name, 'text_field :name' # rubocop:disable QA/ElementWithPattern
|
||||
element :api_url, 'text_field :api_url' # rubocop:disable QA/ElementWithPattern
|
||||
element :ca_certificate, 'text_area :ca_cert' # rubocop:disable QA/ElementWithPattern
|
||||
|
|
|
@ -4,7 +4,7 @@ module QA
|
|||
module Operations
|
||||
module Kubernetes
|
||||
class Index < Page::Base
|
||||
view 'app/views/projects/clusters/_empty_state.html.haml' do
|
||||
view 'app/views/clusters/clusters/_empty_state.html.haml' do
|
||||
element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Add Kubernetes cluster')" # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Projects::Clusters::ApplicationsController do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Projects::ClustersController do
|
||||
|
@ -218,9 +220,9 @@ describe Projects::ClustersController do
|
|||
describe 'security' do
|
||||
before do
|
||||
allow_any_instance_of(described_class)
|
||||
.to receive(:token_in_session).and_return('token')
|
||||
.to receive(:token_in_session).and_return('token')
|
||||
allow_any_instance_of(described_class)
|
||||
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
|
||||
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
|
||||
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
|
||||
.to receive(:projects_zones_clusters_create) do
|
||||
OpenStruct.new(
|
||||
|
@ -318,14 +320,15 @@ describe Projects::ClustersController do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'GET status' do
|
||||
describe 'GET cluster_status' do
|
||||
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
|
||||
|
||||
def go
|
||||
get :status, namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
id: cluster,
|
||||
format: :json
|
||||
get :cluster_status,
|
||||
namespace_id: project.namespace.to_param,
|
||||
project_id: project.to_param,
|
||||
id: cluster,
|
||||
format: :json
|
||||
end
|
||||
|
||||
describe 'functionality' do
|
||||
|
@ -359,9 +362,10 @@ describe Projects::ClustersController do
|
|||
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
def go
|
||||
get :show, namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
id: cluster
|
||||
get :show,
|
||||
namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
id: cluster
|
||||
end
|
||||
|
||||
describe 'functionality' do
|
||||
|
@ -401,8 +405,8 @@ describe Projects::ClustersController do
|
|||
end
|
||||
|
||||
def go(format: :html)
|
||||
put :update, params.merge(namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
put :update, params.merge(namespace_id: project.namespace.to_param,
|
||||
project_id: project.to_param,
|
||||
id: cluster,
|
||||
format: format
|
||||
)
|
||||
|
@ -530,9 +534,10 @@ describe Projects::ClustersController do
|
|||
let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
|
||||
|
||||
def go
|
||||
delete :destroy, namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
id: cluster
|
||||
delete :destroy,
|
||||
namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
id: cluster
|
||||
end
|
||||
|
||||
describe 'functionality' do
|
||||
|
@ -591,4 +596,10 @@ describe Projects::ClustersController do
|
|||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'no project_id param' do
|
||||
it 'does not respond to any action without project_id param' do
|
||||
expect { get :index }.to raise_error(ActionController::UrlGenerationError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe ClusterablePresenter do
|
||||
include Gitlab::Routing.url_helpers
|
||||
|
||||
describe '.fabricate' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
subject { described_class.fabricate(project) }
|
||||
|
||||
it 'creates an object from a descendant presenter' do
|
||||
expect(subject).to be_kind_of(ProjectClusterablePresenter)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Clusters::ClusterPresenter do
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp) }
|
||||
include Gitlab::Routing.url_helpers
|
||||
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, :project) }
|
||||
|
||||
subject(:presenter) do
|
||||
described_class.new(cluster)
|
||||
|
@ -71,4 +73,14 @@ describe Clusters::ClusterPresenter do
|
|||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#show_path' do
|
||||
subject { described_class.new(cluster).show_path }
|
||||
|
||||
context 'project_type cluster' do
|
||||
let(:project) { cluster.project }
|
||||
|
||||
it { is_expected.to eq(project_cluster_path(project, cluster)) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe ProjectClusterablePresenter do
|
||||
include Gitlab::Routing.url_helpers
|
||||
|
||||
let(:presenter) { described_class.new(project) }
|
||||
let(:project) { create(:project) }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
describe '#can_create_cluster?' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
subject { presenter.can_create_cluster? }
|
||||
|
||||
before do
|
||||
allow(presenter).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
context 'when user can create' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when user cannot create' do
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#index_path' do
|
||||
subject { presenter.index_path }
|
||||
|
||||
it { is_expected.to eq(project_clusters_path(project)) }
|
||||
end
|
||||
|
||||
describe '#new_path' do
|
||||
subject { presenter.new_path }
|
||||
|
||||
it { is_expected.to eq(new_project_cluster_path(project)) }
|
||||
end
|
||||
|
||||
describe '#create_user_clusters_path' do
|
||||
subject { presenter.create_user_clusters_path }
|
||||
|
||||
it { is_expected.to eq(create_user_project_clusters_path(project)) }
|
||||
end
|
||||
|
||||
describe '#create_gcp_clusters_path' do
|
||||
subject { presenter.create_gcp_clusters_path }
|
||||
|
||||
it { is_expected.to eq(create_gcp_project_clusters_path(project)) }
|
||||
end
|
||||
|
||||
describe '#cluster_status_cluster_path' do
|
||||
subject { presenter.cluster_status_cluster_path(cluster) }
|
||||
|
||||
it { is_expected.to eq(cluster_status_project_cluster_path(project, cluster)) }
|
||||
end
|
||||
|
||||
describe '#install_applications_cluster_path' do
|
||||
let(:application) { :helm }
|
||||
|
||||
subject { presenter.install_applications_cluster_path(cluster, application) }
|
||||
|
||||
it { is_expected.to eq(install_applications_project_cluster_path(project, cluster, application)) }
|
||||
end
|
||||
|
||||
describe '#cluster_path' do
|
||||
subject { presenter.cluster_path(cluster) }
|
||||
|
||||
it { is_expected.to eq(project_cluster_path(project, cluster)) }
|
||||
end
|
||||
end
|
|
@ -5,18 +5,43 @@ describe Clusters::CreateService do
|
|||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
subject { described_class.new(user, params).execute(project: project, access_token: access_token) }
|
||||
subject { described_class.new(user, params).execute(access_token: access_token) }
|
||||
|
||||
context 'when provider is gcp' do
|
||||
context 'when project has no clusters' do
|
||||
context 'when correct params' do
|
||||
include_context 'valid cluster create params'
|
||||
let(:params) do
|
||||
{
|
||||
name: 'test-cluster',
|
||||
provider_type: :gcp,
|
||||
provider_gcp_attributes: {
|
||||
gcp_project_id: 'gcp-project',
|
||||
zone: 'us-central1-a',
|
||||
num_nodes: 1,
|
||||
machine_type: 'machine_type-a',
|
||||
legacy_abac: 'true'
|
||||
},
|
||||
clusterable: project
|
||||
}
|
||||
end
|
||||
|
||||
include_examples 'create cluster service success'
|
||||
end
|
||||
|
||||
context 'when invalid params' do
|
||||
include_context 'invalid cluster create params'
|
||||
let(:params) do
|
||||
{
|
||||
name: 'test-cluster',
|
||||
provider_type: :gcp,
|
||||
provider_gcp_attributes: {
|
||||
gcp_project_id: '!!!!!!!',
|
||||
zone: 'us-central1-a',
|
||||
num_nodes: 1,
|
||||
machine_type: 'machine_type-a'
|
||||
},
|
||||
clusterable: project
|
||||
}
|
||||
end
|
||||
|
||||
include_examples 'create cluster service error'
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue