Second iteration of Move Kubernetes from service to Cluster page
This commit is contained in:
parent
04a882d8d3
commit
4b66bdfa1a
38 changed files with 1160 additions and 632 deletions
|
@ -48,6 +48,7 @@ export default class Clusters {
|
|||
|
||||
this.toggle = this.toggle.bind(this);
|
||||
this.installApplication = this.installApplication.bind(this);
|
||||
this.showToken = this.showToken.bind(this);
|
||||
|
||||
this.toggleButton = document.querySelector('.js-toggle-cluster');
|
||||
this.toggleInput = document.querySelector('.js-toggle-input');
|
||||
|
@ -56,6 +57,8 @@ export default class Clusters {
|
|||
this.creatingContainer = document.querySelector('.js-cluster-creating');
|
||||
this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason');
|
||||
this.successApplicationContainer = document.querySelector('.js-cluster-application-notice');
|
||||
this.showTokenButton = document.querySelector('.js-show-cluster-token');
|
||||
this.tokenField = document.querySelector('.js-cluster-token');
|
||||
|
||||
initSettingsPanels();
|
||||
this.initApplications();
|
||||
|
@ -97,11 +100,13 @@ export default class Clusters {
|
|||
|
||||
addListeners() {
|
||||
this.toggleButton.addEventListener('click', this.toggle);
|
||||
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
|
||||
eventHub.$on('installApplication', this.installApplication);
|
||||
}
|
||||
|
||||
removeListeners() {
|
||||
this.toggleButton.removeEventListener('click', this.toggle);
|
||||
if (this.showTokenButton) this.showTokenButton.removeEventListener('click', this.showToken);
|
||||
eventHub.$off('installApplication', this.installApplication);
|
||||
}
|
||||
|
||||
|
@ -149,6 +154,16 @@ export default class Clusters {
|
|||
this.toggleInput.setAttribute('value', this.toggleButton.classList.contains('checked').toString());
|
||||
}
|
||||
|
||||
showToken() {
|
||||
const type = this.tokenField.getAttribute('type');
|
||||
|
||||
if (type === 'password') {
|
||||
this.tokenField.setAttribute('type', 'text');
|
||||
} else {
|
||||
this.tokenField.setAttribute('type', 'password');
|
||||
}
|
||||
}
|
||||
|
||||
hideAll() {
|
||||
this.errorContainer.classList.add('hidden');
|
||||
this.successContainer.classList.add('hidden');
|
||||
|
|
|
@ -8,3 +8,9 @@
|
|||
// Wait for the Vue to kick-in and render the applications block
|
||||
min-height: 302px;
|
||||
}
|
||||
|
||||
.clusters-dropdown-menu {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@include new-style-dropdown('.clusters-dropdown ');
|
||||
|
|
75
app/controllers/projects/clusters/gcp_controller.rb
Normal file
75
app/controllers/projects/clusters/gcp_controller.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
class Projects::Clusters::GcpController < Projects::ApplicationController
|
||||
before_action :authorize_read_cluster!
|
||||
before_action :authorize_google_api, except: [:login]
|
||||
before_action :authorize_create_cluster!, only: [:new, :create]
|
||||
|
||||
def login
|
||||
begin
|
||||
state = generate_session_key_redirect(gcp_new_namespace_project_clusters_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
|
||||
end
|
||||
|
||||
def new
|
||||
@cluster = ::Clusters::Cluster.new.tap do |cluster|
|
||||
cluster.build_provider_gcp
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@cluster = ::Clusters::CreateService
|
||||
.new(project, current_user, create_params)
|
||||
.execute(token_in_session)
|
||||
|
||||
if @cluster.persisted?
|
||||
redirect_to project_cluster_path(project, @cluster)
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
provider_gcp_attributes: [
|
||||
:gcp_project_id,
|
||||
:zone,
|
||||
:num_nodes,
|
||||
:machine_type
|
||||
]).merge(
|
||||
provider_type: :gcp,
|
||||
platform_type: :kubernetes
|
||||
)
|
||||
end
|
||||
|
||||
def authorize_google_api
|
||||
unless GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
|
||||
.validate_token(expires_at_in_session)
|
||||
redirect_to action: 'login'
|
||||
end
|
||||
end
|
||||
|
||||
def token_in_session
|
||||
@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
|
||||
end
|
39
app/controllers/projects/clusters/user_controller.rb
Normal file
39
app/controllers/projects/clusters/user_controller.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
class Projects::Clusters::UserController < Projects::ApplicationController
|
||||
before_action :authorize_read_cluster!
|
||||
before_action :authorize_create_cluster!, only: [:new, :create]
|
||||
|
||||
def new
|
||||
@cluster = ::Clusters::Cluster.new.tap do |cluster|
|
||||
cluster.build_platform_kubernetes
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@cluster = ::Clusters::CreateService
|
||||
.new(project, current_user, create_params)
|
||||
.execute
|
||||
|
||||
if @cluster.persisted?
|
||||
redirect_to project_cluster_path(project, @cluster)
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
platform_kubernetes_attributes: [
|
||||
:namespace,
|
||||
:api_url,
|
||||
:token,
|
||||
:ca_cert
|
||||
]).merge(
|
||||
provider_type: :user,
|
||||
platform_type: :kubernetes
|
||||
)
|
||||
end
|
||||
end
|
|
@ -1,11 +1,12 @@
|
|||
class Projects::ClustersController < Projects::ApplicationController
|
||||
before_action :cluster, except: [:login, :index, :new, :new_gcp, :create]
|
||||
before_action :cluster, except: [:index, :new]
|
||||
before_action :authorize_read_cluster!
|
||||
before_action :authorize_create_cluster!, only: [:new, :new_gcp, :create]
|
||||
before_action :authorize_google_api, only: [:new_gcp, :create]
|
||||
before_action :authorize_create_cluster!, only: [:new]
|
||||
before_action :authorize_update_cluster!, only: [:update]
|
||||
before_action :authorize_admin_cluster!, only: [:destroy]
|
||||
|
||||
STATUS_POLLING_INTERVAL = 10_000
|
||||
|
||||
def index
|
||||
if project.cluster
|
||||
redirect_to project_cluster_path(project, project.cluster)
|
||||
|
@ -14,43 +15,13 @@ class Projects::ClustersController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def login
|
||||
begin
|
||||
state = generate_session_key_redirect(providers_gcp_new_namespace_project_clusters_url.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
|
||||
end
|
||||
|
||||
def new
|
||||
end
|
||||
|
||||
def new_gcp
|
||||
@cluster = Clusters::Cluster.new.tap do |cluster|
|
||||
cluster.build_provider_gcp
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@cluster = Clusters::CreateService
|
||||
.new(project, current_user, create_params)
|
||||
.execute(token_in_session)
|
||||
|
||||
if @cluster.persisted?
|
||||
redirect_to project_cluster_path(project, @cluster)
|
||||
else
|
||||
render :new_gcp
|
||||
end
|
||||
end
|
||||
|
||||
def status
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
Gitlab::PollingInterval.set_header(response, interval: 10_000)
|
||||
Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL)
|
||||
|
||||
render json: ClusterSerializer
|
||||
.new(project: @project, current_user: @current_user)
|
||||
|
@ -88,46 +59,29 @@ class Projects::ClustersController < Projects::ApplicationController
|
|||
private
|
||||
|
||||
def cluster
|
||||
@cluster ||= project.cluster.present(current_user: current_user)
|
||||
end
|
||||
|
||||
def create_params
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
:provider_type,
|
||||
provider_gcp_attributes: [
|
||||
:gcp_project_id,
|
||||
:zone,
|
||||
:num_nodes,
|
||||
:machine_type
|
||||
])
|
||||
@cluster ||= project.clusters.find(params[:id])
|
||||
.present(current_user: current_user)
|
||||
end
|
||||
|
||||
def update_params
|
||||
params.require(:cluster).permit(:enabled)
|
||||
end
|
||||
|
||||
def authorize_google_api
|
||||
unless GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
|
||||
.validate_token(expires_at_in_session)
|
||||
redirect_to action: 'login'
|
||||
end
|
||||
end
|
||||
|
||||
def token_in_session
|
||||
@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
|
||||
if cluster.managed?
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
platform_kubernetes_attributes: [
|
||||
:namespace
|
||||
]
|
||||
)
|
||||
else
|
||||
params.require(:cluster).permit(
|
||||
:enabled,
|
||||
:name,
|
||||
platform_kubernetes_attributes: [
|
||||
:api_url,
|
||||
:token,
|
||||
:ca_cert,
|
||||
:namespace
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ module Clusters
|
|||
# we force autosave to happen when we save `Cluster` model
|
||||
has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true
|
||||
|
||||
has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes'
|
||||
has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', autosave: true
|
||||
|
||||
has_one :application_helm, class_name: 'Clusters::Applications::Helm'
|
||||
has_one :application_ingress, class_name: 'Clusters::Applications::Ingress'
|
||||
|
@ -70,6 +70,10 @@ module Clusters
|
|||
return platform_kubernetes if kubernetes?
|
||||
end
|
||||
|
||||
def managed?
|
||||
!user?
|
||||
end
|
||||
|
||||
def first_project
|
||||
return @first_project if defined?(@first_project)
|
||||
|
||||
|
|
|
@ -34,12 +34,15 @@ module Clusters
|
|||
validates :api_url, url: true, presence: true
|
||||
validates :token, presence: true
|
||||
|
||||
validate :prevent_modification, on: :update
|
||||
|
||||
after_save :clear_reactive_cache!
|
||||
|
||||
alias_attribute :ca_pem, :ca_cert
|
||||
|
||||
delegate :project, to: :cluster, allow_nil: true
|
||||
delegate :enabled?, to: :cluster, allow_nil: true
|
||||
delegate :managed?, to: :cluster, allow_nil: true
|
||||
|
||||
alias_method :active?, :enabled?
|
||||
|
||||
|
@ -173,6 +176,17 @@ module Clusters
|
|||
def enforce_namespace_to_lower_case
|
||||
self.namespace = self.namespace&.downcase
|
||||
end
|
||||
|
||||
def prevent_modification
|
||||
return unless managed?
|
||||
|
||||
if api_url_changed? || token_changed? || ca_pem_changed?
|
||||
errors.add(:base, "cannot modify managed cluster")
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ module Clusters
|
|||
class CreateService < BaseService
|
||||
attr_reader :access_token
|
||||
|
||||
def execute(access_token)
|
||||
def execute(access_token = nil)
|
||||
@access_token = access_token
|
||||
|
||||
create_cluster.tap do |cluster|
|
||||
|
|
|
@ -3,11 +3,7 @@
|
|||
# Custom validator for ClusterName.
|
||||
class ClusterNameValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
if record.user?
|
||||
unless value.present?
|
||||
record.errors.add(attribute, " has to be present")
|
||||
end
|
||||
elsif record.gcp?
|
||||
if record.managed?
|
||||
if record.persisted? && record.name_changed?
|
||||
record.errors.add(attribute, " can not be changed because it's synchronized with provider")
|
||||
end
|
||||
|
@ -19,6 +15,10 @@ class ClusterNameValidator < ActiveModel::EachValidator
|
|||
unless value =~ Gitlab::Regex.kubernetes_namespace_regex
|
||||
record.errors.add(attribute, Gitlab::Regex.kubernetes_namespace_regex_message)
|
||||
end
|
||||
else
|
||||
unless value.present?
|
||||
record.errors.add(attribute, " has to be present")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -146,7 +146,7 @@
|
|||
= number_with_delimiter(@project.open_merge_requests_count)
|
||||
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts, :clusters]) do
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts, :clusters, :user, :gcp]) do
|
||||
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines' do
|
||||
.nav-icon-container
|
||||
= sprite_icon('pipeline')
|
||||
|
@ -154,7 +154,7 @@
|
|||
CI / CD
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts], html_options: { class: "fly-out-top-item" } ) do
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts, :clusters, :user, :gcp], html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to project_pipelines_path(@project) do
|
||||
%strong.fly-out-top-item-name
|
||||
#{ _('CI / CD') }
|
||||
|
@ -183,18 +183,18 @@
|
|||
%span
|
||||
Environments
|
||||
|
||||
- if project_nav_tab? :clusters
|
||||
= nav_link(controller: [:clusters, :user, :gcp]) do
|
||||
= link_to project_clusters_path(@project), title: 'Cluster', class: 'shortcuts-cluster' do
|
||||
%span
|
||||
Cluster
|
||||
|
||||
- if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
|
||||
= nav_link(path: 'pipelines#charts') do
|
||||
= link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do
|
||||
%span
|
||||
Charts
|
||||
|
||||
- if project_nav_tab? :clusters
|
||||
= nav_link(controller: :clusters) do
|
||||
= link_to project_clusters_path(@project), title: 'Cluster', class: 'shortcuts-cluster' do
|
||||
%span
|
||||
Cluster
|
||||
|
||||
- if project_nav_tab? :wiki
|
||||
= nav_link(controller: :wikis) do
|
||||
= link_to get_project_wiki_path(@project), class: 'shortcuts-wiki' do
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
- if can?(current_user, :admin_cluster, @cluster)
|
||||
.append-bottom-20
|
||||
%label.append-bottom-10
|
||||
= s_('ClusterIntegration|Google Container Engine')
|
||||
%p
|
||||
- link_gke = link_to(s_('ClusterIntegration|Google Container Engine'), @cluster.gke_cluster_url, target: '_blank', rel: 'noopener noreferrer')
|
||||
= s_('ClusterIntegration|Manage your cluster by visiting %{link_gke}').html_safe % { link_gke: link_gke }
|
||||
- if @cluster.managed?
|
||||
.append-bottom-20
|
||||
%label.append-bottom-10
|
||||
= s_('ClusterIntegration|Google Container Engine')
|
||||
%p
|
||||
- link_gke = link_to(s_('ClusterIntegration|Google Container Engine'), @cluster.gke_cluster_url, target: '_blank', rel: 'noopener noreferrer')
|
||||
= s_('ClusterIntegration|Manage your cluster by visiting %{link_gke}').html_safe % { link_gke: link_gke }
|
||||
|
||||
.well.form-group
|
||||
%label.text-danger
|
||||
|
|
21
app/views/projects/clusters/_banner.html.haml
Normal file
21
app/views/projects/clusters/_banner.html.haml
Normal file
|
@ -0,0 +1,21 @@
|
|||
%h4= s_('ClusterIntegration|Enable cluster integration')
|
||||
.settings-content
|
||||
|
||||
.hidden.js-cluster-error.alert.alert-danger.alert-block.append-bottom-10{ role: 'alert' }
|
||||
= s_('ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine')
|
||||
%p.js-error-reason
|
||||
|
||||
.hidden.js-cluster-creating.alert.alert-info.alert-block.append-bottom-10{ role: 'alert' }
|
||||
= s_('ClusterIntegration|Cluster is being created on Google Container Engine...')
|
||||
|
||||
.hidden.js-cluster-success.alert.alert-success.alert-block.append-bottom-10{ role: 'alert' }
|
||||
= s_('ClusterIntegration|Cluster was successfully created on Google Container Engine. Refresh the page to see cluster\'s details')
|
||||
|
||||
%p
|
||||
- if @cluster.enabled?
|
||||
- if can?(current_user, :update_cluster, @cluster)
|
||||
= s_('ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab\'s connection to it.')
|
||||
- else
|
||||
= s_('ClusterIntegration|Cluster integration is enabled for this project.')
|
||||
- else
|
||||
= s_('ClusterIntegration|Cluster integration is disabled for this project.')
|
12
app/views/projects/clusters/_dropdown.html.haml
Normal file
12
app/views/projects/clusters/_dropdown.html.haml
Normal file
|
@ -0,0 +1,12 @@
|
|||
%h4.prepend-top-0= s_('ClusterIntegration|Choose how to set up cluster integration')
|
||||
|
||||
.dropdown.clusters-dropdown
|
||||
%button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', data: { toggle: 'dropdown' }, 'aria-haspopup': true, 'aria-expanded': false }
|
||||
%span.dropdown-toggle-text
|
||||
= dropdown_text
|
||||
= icon('chevron-down')
|
||||
%ul.dropdown-menu.clusters-dropdown-menu.dropdown-menu-full-width
|
||||
%li
|
||||
= link_to(s_('ClusterIntegration|Create cluster on Google Container Engine'), gcp_new_namespace_project_clusters_path(@project.namespace, @project))
|
||||
%li
|
||||
= link_to(s_('ClusterIntegration|Add an existing cluster'), user_new_namespace_project_clusters_path(@project.namespace, @project))
|
16
app/views/projects/clusters/_enabled.html.haml
Normal file
16
app/views/projects/clusters/_enabled.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
|||
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
.form-group.append-bottom-20
|
||||
%label.append-bottom-10
|
||||
= field.hidden_field :enabled, { class: 'js-toggle-input'}
|
||||
|
||||
%button{ type: 'button',
|
||||
class: "js-toggle-cluster project-feature-toggle #{'checked' unless !@cluster.enabled?} #{'disabled' unless can?(current_user, :update_cluster, @cluster)}",
|
||||
'aria-label': s_('ClusterIntegration|Toggle Cluster'),
|
||||
disabled: !can?(current_user, :update_cluster, @cluster),
|
||||
data: { 'enabled-text': 'Enabled', 'disabled-text': 'Disabled' } }
|
||||
|
||||
- if can?(current_user, :update_cluster, @cluster)
|
||||
.form-group
|
||||
= field.submit _('Save'), class: 'btn btn-success'
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
.row
|
||||
.col-sm-8.col-sm-offset-4
|
||||
%p
|
||||
- link_to_help_page = link_to(s_('ClusterIntegration|help page'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
|
||||
= s_('ClusterIntegration|Read our %{link_to_help_page} on cluster integration.').html_safe % { link_to_help_page: link_to_help_page}
|
||||
|
||||
= form_for @cluster, url: namespace_project_clusters_path(@project.namespace, @project, @cluster), as: :cluster do |field|
|
||||
= field.hidden_field :provider_type, value: :gcp
|
||||
= form_errors(@cluster)
|
||||
.form-group
|
||||
= field.label :name, s_('ClusterIntegration|Cluster name')
|
||||
= field.text_field :name, class: 'form-control'
|
||||
|
||||
= field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field|
|
||||
.form-group
|
||||
= provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project ID')
|
||||
= link_to(s_('ClusterIntegration|See your projects'), 'https://console.cloud.google.com/home/dashboard', target: '_blank', rel: 'noopener noreferrer')
|
||||
= provider_gcp_field.text_field :gcp_project_id, class: 'form-control'
|
||||
|
||||
.form-group
|
||||
= provider_gcp_field.label :zone, s_('ClusterIntegration|Zone')
|
||||
= link_to(s_('ClusterIntegration|See zones'), 'https://cloud.google.com/compute/docs/regions-zones/regions-zones', target: '_blank', rel: 'noopener noreferrer')
|
||||
= provider_gcp_field.text_field :zone, class: 'form-control', placeholder: 'us-central1-a'
|
||||
|
||||
.form-group
|
||||
= provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes')
|
||||
= provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3'
|
||||
|
||||
.form-group
|
||||
= provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type')
|
||||
= link_to(s_('ClusterIntegration|See machine types'), 'https://cloud.google.com/compute/docs/machine-types', target: '_blank', rel: 'noopener noreferrer')
|
||||
= provider_gcp_field.text_field :machine_type, class: 'form-control', placeholder: 'n1-standard-2'
|
||||
|
||||
.form-group
|
||||
= field.submit s_('ClusterIntegration|Create cluster'), class: 'btn btn-save'
|
32
app/views/projects/clusters/gcp/_form.html.haml
Normal file
32
app/views/projects/clusters/gcp/_form.html.haml
Normal file
|
@ -0,0 +1,32 @@
|
|||
%p
|
||||
- link_to_help_page = link_to(s_('ClusterIntegration|help page'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
|
||||
= s_('ClusterIntegration|Read our %{link_to_help_page} on cluster integration.').html_safe % { link_to_help_page: link_to_help_page}
|
||||
|
||||
= form_for @cluster, html: { class: 'prepend-top-20' }, url: gcp_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
.form-group
|
||||
= field.label :name, s_('ClusterIntegration|Cluster name')
|
||||
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Cluster name')
|
||||
|
||||
= field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field|
|
||||
.form-group
|
||||
= provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project ID')
|
||||
= link_to(s_('ClusterIntegration|See your projects'), 'https://console.cloud.google.com/home/dashboard', target: '_blank', rel: 'noopener noreferrer')
|
||||
= provider_gcp_field.text_field :gcp_project_id, class: 'form-control', placeholder: s_('ClusterIntegration|Project ID')
|
||||
|
||||
.form-group
|
||||
= provider_gcp_field.label :zone, s_('ClusterIntegration|Zone')
|
||||
= link_to(s_('ClusterIntegration|See zones'), 'https://cloud.google.com/compute/docs/regions-zones/regions-zones', target: '_blank', rel: 'noopener noreferrer')
|
||||
= provider_gcp_field.text_field :zone, class: 'form-control', placeholder: 'us-central1-a'
|
||||
|
||||
.form-group
|
||||
= provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes')
|
||||
= provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3'
|
||||
|
||||
.form-group
|
||||
= provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type')
|
||||
= link_to(s_('ClusterIntegration|See machine types'), 'https://cloud.google.com/compute/docs/machine-types', target: '_blank', rel: 'noopener noreferrer')
|
||||
= provider_gcp_field.text_field :machine_type, class: 'form-control', placeholder: 'n1-standard-4'
|
||||
|
||||
.form-group
|
||||
= field.submit s_('ClusterIntegration|Create cluster'), class: 'btn btn-success'
|
|
@ -1,5 +1,5 @@
|
|||
%h4.prepend-top-0
|
||||
= s_('ClusterIntegration|Create new cluster on Google Container Engine')
|
||||
%h4.prepend-top-20
|
||||
= s_('ClusterIntegration|Enter the details for your cluster')
|
||||
%p
|
||||
= s_('ClusterIntegration|Please make sure that your Google account meets the following requirements:')
|
||||
%ul
|
40
app/views/projects/clusters/gcp/_show.html.haml
Normal file
40
app/views/projects/clusters/gcp/_show.html.haml
Normal file
|
@ -0,0 +1,40 @@
|
|||
.form-group
|
||||
%label.append-bottom-10{ for: 'cluster-name' }
|
||||
= s_('ClusterIntegration|Cluster name')
|
||||
.input-group
|
||||
%input.form-control.cluster-name.js-select-on-focus{ value: @cluster.name, readonly: true }
|
||||
%span.input-group-btn
|
||||
= clipboard_button(text: @cluster.name, title: s_('ClusterIntegration|Copy cluster name'), class: 'btn-default')
|
||||
|
||||
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
|
||||
.input-group
|
||||
= platform_kubernetes_field.text_field :api_url, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|API URL'), readonly: true
|
||||
%span.input-group-btn
|
||||
= clipboard_button(text: @cluster.platform_kubernetes.api_url, title: s_('ClusterIntegration|Copy API URL'), class: 'btn-default')
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
|
||||
.input-group
|
||||
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)'), readonly: true
|
||||
%span.input-group-addon.clipboard-addon
|
||||
= clipboard_button(text: @cluster.platform_kubernetes.ca_cert, title: s_('ClusterIntegration|Copy CA Certificate'), class: 'btn-blank')
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
|
||||
.input-group
|
||||
= platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token js-select-on-focus', type: 'password', placeholder: s_('ClusterIntegration|Token'), readonly: true
|
||||
%span.input-group-btn
|
||||
%button.btn.btn-default.js-show-cluster-token{ type: 'button' }
|
||||
= s_('ClusterIntegration|Show')
|
||||
= clipboard_button(text: @cluster.platform_kubernetes.token, title: s_('ClusterIntegration|Copy Token'), class: 'btn-default')
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
|
||||
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
|
||||
|
||||
.form-group
|
||||
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
.row.prepend-top-default
|
||||
.col-sm-4
|
||||
= render 'sidebar'
|
||||
= render 'projects/clusters/sidebar'
|
||||
.col-sm-8
|
||||
= render 'projects/clusters/dropdown', dropdown_text: s_('ClusterIntegration|Create cluster on Google Container Engine')
|
||||
= render 'header'
|
||||
.row
|
||||
.col-sm-8.col-sm-offset-4.signin-with-google
|
10
app/views/projects/clusters/gcp/new.html.haml
Normal file
10
app/views/projects/clusters/gcp/new.html.haml
Normal file
|
@ -0,0 +1,10 @@
|
|||
- breadcrumb_title "Cluster"
|
||||
- page_title _("New Cluster")
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-sm-4
|
||||
= render 'projects/clusters/sidebar'
|
||||
.col-sm-8
|
||||
= render 'projects/clusters/dropdown', dropdown_text: s_('ClusterIntegration|Create cluster on Google Container Engine')
|
||||
= render 'header'
|
||||
= render 'form'
|
|
@ -5,16 +5,9 @@
|
|||
.col-sm-4
|
||||
= render 'sidebar'
|
||||
.col-sm-8
|
||||
- if @project.deployment_platform&.active?
|
||||
%h4.prepend-top-0= s_('ClusterIntegration|Cluster management')
|
||||
%h4.prepend-top-0= s_('ClusterIntegration|Choose how to set up cluster integration')
|
||||
|
||||
%p= s_('ClusterIntegration|A cluster has been set up on this project through the Kubernetes integration page')
|
||||
= link_to s_('ClusterIntegration|Manage Kubernetes integration'), edit_project_service_path(@project, :kubernetes), class: 'btn append-bottom-20'
|
||||
|
||||
- else
|
||||
%h4.prepend-top-0= s_('ClusterIntegration|Choose how to set up cluster integration')
|
||||
|
||||
%p= s_('ClusterIntegration|Create a new cluster on Google Container Engine right from GitLab')
|
||||
= link_to s_('ClusterIntegration|Create on GKE'), providers_gcp_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20'
|
||||
%p= s_('ClusterIntegration|Enter the details for an existing Kubernetes cluster')
|
||||
= link_to s_('ClusterIntegration|Add an existing cluster'), edit_project_service_path(@project, :kubernetes), class: 'btn append-bottom-20'
|
||||
%p= s_('ClusterIntegration|Create a new cluster on Google Engine right from GitLab')
|
||||
= link_to s_('ClusterIntegration|Create on GKE'), gcp_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20'
|
||||
%p= s_('ClusterIntegration|Enter the details for an existing Kubernetes cluster')
|
||||
= link_to s_('ClusterIntegration|Add an existing cluster'), user_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20'
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
- breadcrumb_title "Cluster"
|
||||
- page_title _("New Cluster")
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-sm-4
|
||||
= render 'sidebar'
|
||||
.col-sm-8
|
||||
= render 'header'
|
||||
|
||||
= render 'form'
|
|
@ -13,52 +13,16 @@
|
|||
cluster_status_reason: @cluster.status_reason,
|
||||
help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications') } }
|
||||
|
||||
|
||||
.js-cluster-application-notice
|
||||
.flash-container
|
||||
|
||||
%section.settings.no-animate.expanded
|
||||
%h4= s_('ClusterIntegration|Enable cluster integration')
|
||||
.settings-content
|
||||
|
||||
.hidden.js-cluster-error.alert.alert-danger.alert-block.append-bottom-10{ role: 'alert' }
|
||||
= s_('ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine')
|
||||
%p.js-error-reason
|
||||
|
||||
.hidden.js-cluster-creating.alert.alert-info.alert-block.append-bottom-10{ role: 'alert' }
|
||||
= s_('ClusterIntegration|Cluster is being created on Google Container Engine...')
|
||||
|
||||
.hidden.js-cluster-success.alert.alert-success.alert-block.append-bottom-10{ role: 'alert' }
|
||||
= s_('ClusterIntegration|Cluster was successfully created on Google Container Engine')
|
||||
|
||||
%p
|
||||
- if @cluster.enabled?
|
||||
- if can?(current_user, :update_cluster, @cluster)
|
||||
= s_('ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab\'s connection to it.')
|
||||
- else
|
||||
= s_('ClusterIntegration|Cluster integration is enabled for this project.')
|
||||
- else
|
||||
= s_('ClusterIntegration|Cluster integration is disabled for this project.')
|
||||
|
||||
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
.form-group.append-bottom-20
|
||||
%label.append-bottom-10
|
||||
= field.hidden_field :enabled, { class: 'js-toggle-input'}
|
||||
|
||||
%button{ type: 'button',
|
||||
class: "js-toggle-cluster project-feature-toggle #{'checked' unless !@cluster.enabled?} #{'disabled' unless can?(current_user, :update_cluster, @cluster)}",
|
||||
'aria-label': s_('ClusterIntegration|Toggle Cluster'),
|
||||
disabled: !can?(current_user, :update_cluster, @cluster),
|
||||
data: { 'enabled-text': 'Enabled', 'disabled-text': 'Disabled' } }
|
||||
|
||||
- if can?(current_user, :update_cluster, @cluster)
|
||||
.form-group
|
||||
= field.submit _('Save'), class: 'btn btn-success'
|
||||
= render 'banner'
|
||||
= render 'enabled'
|
||||
|
||||
.cluster-applications-table#js-cluster-applications
|
||||
|
||||
%section.settings#js-cluster-details
|
||||
%section.settings#js-cluster-details{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4= s_('ClusterIntegration|Cluster details')
|
||||
%button.btn.js-settings-toggle
|
||||
|
@ -66,20 +30,16 @@
|
|||
%p= s_('ClusterIntegration|See and edit the details for your cluster')
|
||||
|
||||
.settings-content
|
||||
|
||||
.form_group.append-bottom-20
|
||||
%label.append-bottom-10{ for: 'cluster-name' }
|
||||
= s_('ClusterIntegration|Cluster name')
|
||||
.input-group
|
||||
%input.form-control.cluster-name{ value: @cluster.name, disabled: true }
|
||||
%span.input-group-addon.clipboard-addon
|
||||
= clipboard_button(text: @cluster.name, title: s_('ClusterIntegration|Copy cluster name'))
|
||||
- if @cluster.managed?
|
||||
= render 'projects/clusters/gcp/show'
|
||||
- else
|
||||
= render 'projects/clusters/user/show'
|
||||
|
||||
%section.settings.no-animate#js-cluster-advanced-settings{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4= _('Advanced settings')
|
||||
%button.btn.js-settings-toggle
|
||||
= expanded ? 'Collapse' : 'Expand'
|
||||
%p= s_('ClusterIntegration|Manage Cluster integration on your GitLab project')
|
||||
%p= s_('ClusterIntegration|Manage cluster integration on your GitLab project')
|
||||
.settings-content
|
||||
= render 'advanced_settings'
|
||||
|
|
25
app/views/projects/clusters/user/_form.html.haml
Normal file
25
app/views/projects/clusters/user/_form.html.haml
Normal file
|
@ -0,0 +1,25 @@
|
|||
= form_for @cluster, url: user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
.form-group
|
||||
= field.label :name, s_('ClusterIntegration|Cluster name')
|
||||
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Cluster name')
|
||||
|
||||
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
|
||||
= platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
|
||||
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
|
||||
= platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off'
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
|
||||
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
|
||||
|
||||
.form-group
|
||||
= field.submit s_('ClusterIntegration|Add cluster'), class: 'btn btn-success'
|
5
app/views/projects/clusters/user/_header.html.haml
Normal file
5
app/views/projects/clusters/user/_header.html.haml
Normal file
|
@ -0,0 +1,5 @@
|
|||
%h4.prepend-top-20
|
||||
= s_('ClusterIntegration|Enter the details for your cluster')
|
||||
%p
|
||||
- link_to_help_page = link_to(s_('ClusterIntegration|documentation'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
|
||||
= s_('ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters').html_safe % { link_to_help_page: link_to_help_page }
|
29
app/views/projects/clusters/user/_show.html.haml
Normal file
29
app/views/projects/clusters/user/_show.html.haml
Normal file
|
@ -0,0 +1,29 @@
|
|||
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
|
||||
= form_errors(@cluster)
|
||||
.form-group
|
||||
= field.label :name, s_('ClusterIntegration|Cluster name')
|
||||
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Cluster name')
|
||||
|
||||
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
|
||||
= platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
|
||||
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
|
||||
.input-group
|
||||
= platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token', type: 'password', placeholder: s_('ClusterIntegration|Token'), autocomplete: 'off'
|
||||
%span.input-group-addon.clipboard-addon
|
||||
%button.js-show-cluster-token.btn-blank{ type: 'button' }
|
||||
= s_('ClusterIntegration|Show')
|
||||
|
||||
.form-group
|
||||
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
|
||||
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
|
||||
|
||||
.form-group
|
||||
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
|
11
app/views/projects/clusters/user/new.html.haml
Normal file
11
app/views/projects/clusters/user/new.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
|||
- breadcrumb_title "Cluster"
|
||||
- page_title _("New Cluster")
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-sm-4
|
||||
= render 'projects/clusters/sidebar'
|
||||
.col-sm-8
|
||||
= render 'projects/clusters/dropdown', dropdown_text: s_('ClusterIntegration|Add an existing cluster')
|
||||
= render 'header'
|
||||
.prepend-top-20
|
||||
= render 'form'
|
5
changelogs/unreleased/35616-move-k8-to-cluster-page.yml
Normal file
5
changelogs/unreleased/35616-move-k8-to-cluster-page.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Create a new form to add Existing Kubernetes Cluster
|
||||
merge_request: 14805
|
||||
author:
|
||||
type: added
|
|
@ -183,10 +183,16 @@ constraints(ProjectUrlConstrainer.new) do
|
|||
end
|
||||
end
|
||||
|
||||
resources :clusters, except: [:edit] do
|
||||
resources :clusters, except: [:edit, :create] do
|
||||
collection do
|
||||
get :login
|
||||
get '/providers/gcp/new', action: :new_gcp
|
||||
scope :providers do
|
||||
get '/user/new', to: 'clusters/user#new'
|
||||
post '/user', to: 'clusters/user#create'
|
||||
|
||||
get '/gcp/new', to: 'clusters/gcp#new'
|
||||
get '/gcp/login', to: 'clusters/gcp#login'
|
||||
post '/gcp', to: 'clusters/gcp#create'
|
||||
end
|
||||
end
|
||||
|
||||
member do
|
||||
|
|
185
spec/controllers/projects/clusters/gcp_controller_spec.rb
Normal file
185
spec/controllers/projects/clusters/gcp_controller_spec.rb
Normal file
|
@ -0,0 +1,185 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Projects::Clusters::GcpController do
|
||||
include AccessMatchersForController
|
||||
include GoogleApi::CloudPlatformHelpers
|
||||
|
||||
set(:project) { create(:project) }
|
||||
|
||||
describe 'GET login' do
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when omniauth has been configured' do
|
||||
let(:key) { 'secret-key' }
|
||||
|
||||
let(:session_key_for_redirect_uri) do
|
||||
GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(SecureRandom).to receive(:hex).and_return(key)
|
||||
end
|
||||
|
||||
it 'has authorize_url' do
|
||||
go
|
||||
|
||||
expect(assigns(:authorize_url)).to include(key)
|
||||
expect(session[session_key_for_redirect_uri]).to eq(gcp_new_project_clusters_path(project))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when omniauth has not configured' do
|
||||
before do
|
||||
stub_omniauth_setting(providers: [])
|
||||
end
|
||||
|
||||
it 'does not have authorize_url' do
|
||||
go
|
||||
|
||||
expect(assigns(:authorize_url)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
def go
|
||||
get :login, namespace_id: project.namespace, project_id: project
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET new' do
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when access token is valid' do
|
||||
before do
|
||||
stub_google_api_validate_token
|
||||
end
|
||||
|
||||
it 'has new object' do
|
||||
go
|
||||
|
||||
expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when access token is expired' do
|
||||
before do
|
||||
stub_google_api_expired_token
|
||||
end
|
||||
|
||||
it { expect(go).to redirect_to(gcp_login_project_clusters_path(project)) }
|
||||
end
|
||||
|
||||
context 'when access token is not stored in session' do
|
||||
it { expect(go).to redirect_to(gcp_login_project_clusters_path(project)) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
def go
|
||||
get :new, namespace_id: project.namespace, project_id: project
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST create' do
|
||||
let(:params) do
|
||||
{
|
||||
cluster: {
|
||||
name: 'new-cluster',
|
||||
provider_gcp_attributes: {
|
||||
gcp_project_id: '111'
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when access token is valid' do
|
||||
before do
|
||||
stub_google_api_validate_token
|
||||
end
|
||||
|
||||
context 'when creates a cluster on gke' do
|
||||
it 'creates a new cluster' do
|
||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||
expect { go }.to change { Clusters::Cluster.count }
|
||||
.and change { Clusters::Providers::Gcp.count }
|
||||
expect(response).to redirect_to(project_cluster_path(project, project.cluster))
|
||||
expect(project.cluster).to be_gcp
|
||||
expect(project.cluster).to be_kubernetes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when access token is expired' do
|
||||
before do
|
||||
stub_google_api_expired_token
|
||||
end
|
||||
|
||||
it 'redirects to login page' do
|
||||
expect(go).to redirect_to(gcp_login_project_clusters_path(project))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when access token is not stored in session' do
|
||||
it 'redirects to login page' do
|
||||
expect(go).to redirect_to(gcp_login_project_clusters_path(project))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
def go
|
||||
post :create, params.merge(namespace_id: project.namespace, project_id: project)
|
||||
end
|
||||
end
|
||||
end
|
87
spec/controllers/projects/clusters/user_controller_spec.rb
Normal file
87
spec/controllers/projects/clusters/user_controller_spec.rb
Normal file
|
@ -0,0 +1,87 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Projects::Clusters::UserController do
|
||||
include AccessMatchersForController
|
||||
|
||||
set(:project) { create(:project) }
|
||||
|
||||
describe 'GET new' do
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'has new object' do
|
||||
go
|
||||
|
||||
expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
def go
|
||||
get :new, namespace_id: project.namespace, project_id: project
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST create' do
|
||||
let(:params) do
|
||||
{
|
||||
cluster: {
|
||||
name: 'new-cluster',
|
||||
platform_kubernetes_attributes: {
|
||||
api_url: 'http://my-url',
|
||||
token: 'test',
|
||||
namespace: 'aaa'
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when creates a cluster' do
|
||||
it 'creates a new cluster' do
|
||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||
expect { go }.to change { Clusters::Cluster.count }
|
||||
.and change { Clusters::Platforms::Kubernetes.count }
|
||||
expect(response).to redirect_to(project_cluster_path(project, project.cluster))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
def go
|
||||
post :create, params.merge(namespace_id: project.namespace, project_id: project)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,6 +4,8 @@ describe Projects::ClustersController do
|
|||
include AccessMatchersForController
|
||||
include GoogleApi::CloudPlatformHelpers
|
||||
|
||||
set(:project) { create(:project) }
|
||||
|
||||
describe 'GET index' do
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
@ -14,22 +16,18 @@ describe Projects::ClustersController do
|
|||
end
|
||||
|
||||
context 'when project has a cluster' do
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
it { expect(go).to redirect_to(project_cluster_path(project, project.cluster)) }
|
||||
end
|
||||
|
||||
context 'when project does not have a cluster' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
it { expect(go).to redirect_to(new_project_cluster_path(project)) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
|
@ -46,198 +44,8 @@ describe Projects::ClustersController do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'GET login' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when omniauth has been configured' do
|
||||
let(:key) { 'secere-key' }
|
||||
|
||||
let(:session_key_for_redirect_uri) do
|
||||
GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(SecureRandom).to receive(:hex).and_return(key)
|
||||
end
|
||||
|
||||
it 'has authorize_url' do
|
||||
go
|
||||
|
||||
expect(assigns(:authorize_url)).to include(key)
|
||||
expect(session[session_key_for_redirect_uri]).to eq(providers_gcp_new_project_clusters_url(project))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when omniauth has not configured' do
|
||||
before do
|
||||
stub_omniauth_setting(providers: [])
|
||||
end
|
||||
|
||||
it 'does not have authorize_url' do
|
||||
go
|
||||
|
||||
expect(assigns(:authorize_url)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
def go
|
||||
get :login, namespace_id: project.namespace, project_id: project
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'requires to login' do
|
||||
it 'redirects to create a cluster' do
|
||||
subject
|
||||
|
||||
expect(response).to redirect_to(login_project_clusters_path(project))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET new_gcp' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when access token is valid' do
|
||||
before do
|
||||
stub_google_api_validate_token
|
||||
end
|
||||
|
||||
it 'has new object' do
|
||||
go
|
||||
|
||||
expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when access token is expired' do
|
||||
before do
|
||||
stub_google_api_expired_token
|
||||
end
|
||||
|
||||
it { expect(go).to redirect_to(login_project_clusters_path(project)) }
|
||||
end
|
||||
|
||||
context 'when access token is not stored in session' do
|
||||
it { expect(go).to redirect_to(login_project_clusters_path(project)) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
def go
|
||||
get :new_gcp, namespace_id: project.namespace, project_id: project
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST create' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
cluster: {
|
||||
name: 'new-cluster',
|
||||
provider_type: :gcp,
|
||||
provider_gcp_attributes: {
|
||||
gcp_project_id: '111'
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when access token is valid' do
|
||||
before do
|
||||
stub_google_api_validate_token
|
||||
end
|
||||
|
||||
context 'when creates a cluster on gke' do
|
||||
it 'creates a new cluster' do
|
||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||
expect { go }.to change { Clusters::Cluster.count }
|
||||
expect(response).to redirect_to(project_cluster_path(project, project.cluster))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when access token is expired' do
|
||||
before do
|
||||
stub_google_api_expired_token
|
||||
end
|
||||
|
||||
it 'redirects to login page' do
|
||||
expect(go).to redirect_to(login_project_clusters_path(project))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when access token is not stored in session' do
|
||||
it 'redirects to login page' do
|
||||
expect(go).to redirect_to(login_project_clusters_path(project))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
def go
|
||||
post :create, params.merge(namespace_id: project.namespace, project_id: project)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET status' do
|
||||
let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
@ -275,8 +83,7 @@ describe Projects::ClustersController do
|
|||
end
|
||||
|
||||
describe 'GET show' do
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
@ -313,10 +120,8 @@ describe Projects::ClustersController do
|
|||
end
|
||||
|
||||
describe 'PUT update' do
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
|
||||
describe 'functionality' do
|
||||
context 'when cluster is provided by GCP' do
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
|
@ -324,10 +129,16 @@ describe Projects::ClustersController do
|
|||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when update enabled' do
|
||||
context 'when changing parameters' do
|
||||
let(:params) do
|
||||
{
|
||||
cluster: { enabled: false }
|
||||
cluster: {
|
||||
enabled: false,
|
||||
name: 'my-new-cluster-name',
|
||||
platform_kubernetes_attributes: {
|
||||
namespace: 'my-namespace'
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -340,8 +151,14 @@ describe Projects::ClustersController do
|
|||
expect(cluster.enabled).to be_falsey
|
||||
end
|
||||
|
||||
it "does not change cluster name" do
|
||||
go
|
||||
|
||||
expect(cluster.name).to eq('test-cluster')
|
||||
end
|
||||
|
||||
context 'when cluster is being created' do
|
||||
let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
|
||||
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
|
||||
|
||||
it "rejects changes" do
|
||||
go
|
||||
|
@ -354,11 +171,46 @@ describe Projects::ClustersController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when cluster is provided by user' do
|
||||
let(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when changing parameters' do
|
||||
let(:params) do
|
||||
{
|
||||
cluster: {
|
||||
enabled: false,
|
||||
name: 'my-new-cluster-name',
|
||||
platform_kubernetes_attributes: {
|
||||
namespace: 'my-namespace'
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it "updates and redirects back to show page" do
|
||||
go
|
||||
|
||||
cluster.reload
|
||||
expect(response).to redirect_to(project_cluster_path(project, project.cluster))
|
||||
expect(flash[:notice]).to eq('Cluster was successfully updated.')
|
||||
expect(cluster.enabled).to be_falsey
|
||||
expect(cluster.name).to eq('my-new-cluster-name')
|
||||
expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
cluster: { enabled: false }
|
||||
}
|
||||
{ cluster: { enabled: false } }
|
||||
end
|
||||
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
|
@ -378,10 +230,7 @@ describe Projects::ClustersController do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'delete update' do
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
|
||||
describe 'DELETE destroy' do
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
|
@ -390,31 +239,37 @@ describe Projects::ClustersController do
|
|||
sign_in(user)
|
||||
end
|
||||
|
||||
it "destroys and redirects back to clusters list" do
|
||||
expect { go }
|
||||
.to change { Clusters::Cluster.count }.by(-1)
|
||||
.and change { Clusters::Platforms::Kubernetes.count }.by(-1)
|
||||
.and change { Clusters::Providers::Gcp.count }.by(-1)
|
||||
context 'when cluster is provided by GCP' do
|
||||
context 'when cluster is created' do
|
||||
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
expect(response).to redirect_to(project_clusters_path(project))
|
||||
expect(flash[:notice]).to eq('Cluster integration was successfully removed.')
|
||||
end
|
||||
it "destroys and redirects back to clusters list" do
|
||||
expect { go }
|
||||
.to change { Clusters::Cluster.count }.by(-1)
|
||||
.and change { Clusters::Platforms::Kubernetes.count }.by(-1)
|
||||
.and change { Clusters::Providers::Gcp.count }.by(-1)
|
||||
|
||||
context 'when cluster is being created' do
|
||||
let(:cluster) { create(:cluster, :project, :providing_by_gcp) }
|
||||
expect(response).to redirect_to(project_clusters_path(project))
|
||||
expect(flash[:notice]).to eq('Cluster integration was successfully removed.')
|
||||
end
|
||||
end
|
||||
|
||||
it "destroys and redirects back to clusters list" do
|
||||
expect { go }
|
||||
.to change { Clusters::Cluster.count }.by(-1)
|
||||
.and change { Clusters::Providers::Gcp.count }.by(-1)
|
||||
context 'when cluster is being created' do
|
||||
let!(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
|
||||
|
||||
expect(response).to redirect_to(project_clusters_path(project))
|
||||
expect(flash[:notice]).to eq('Cluster integration was successfully removed.')
|
||||
it "destroys and redirects back to clusters list" do
|
||||
expect { go }
|
||||
.to change { Clusters::Cluster.count }.by(-1)
|
||||
.and change { Clusters::Providers::Gcp.count }.by(-1)
|
||||
|
||||
expect(response).to redirect_to(project_clusters_path(project))
|
||||
expect(flash[:notice]).to eq('Cluster integration was successfully removed.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provider is user' do
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_user) }
|
||||
context 'when cluster is provided by user' do
|
||||
let!(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
|
||||
|
||||
it "destroys and redirects back to clusters list" do
|
||||
expect { go }
|
||||
|
@ -429,6 +284,8 @@ describe Projects::ClustersController do
|
|||
end
|
||||
|
||||
describe 'security' do
|
||||
set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:master).of(project) }
|
||||
|
|
|
@ -13,27 +13,20 @@ FactoryGirl.define do
|
|||
provider_type :user
|
||||
platform_type :kubernetes
|
||||
|
||||
platform_kubernetes do
|
||||
create(:cluster_platform_kubernetes, :configured)
|
||||
end
|
||||
platform_kubernetes factory: [:cluster_platform_kubernetes, :configured]
|
||||
end
|
||||
|
||||
trait :provided_by_gcp do
|
||||
provider_type :gcp
|
||||
platform_type :kubernetes
|
||||
|
||||
before(:create) do |cluster, evaluator|
|
||||
cluster.platform_kubernetes = build(:cluster_platform_kubernetes, :configured)
|
||||
cluster.provider_gcp = build(:cluster_provider_gcp, :created)
|
||||
end
|
||||
provider_gcp factory: [:cluster_provider_gcp, :created]
|
||||
platform_kubernetes factory: [:cluster_platform_kubernetes, :configured]
|
||||
end
|
||||
|
||||
trait :providing_by_gcp do
|
||||
provider_type :gcp
|
||||
|
||||
provider_gcp do
|
||||
create(:cluster_provider_gcp, :creating)
|
||||
end
|
||||
provider_gcp factory: [:cluster_provider_gcp, :creating]
|
||||
end
|
||||
end
|
||||
end
|
107
spec/features/projects/clusters/applications_spec.rb
Normal file
107
spec/features/projects/clusters/applications_spec.rb
Normal file
|
@ -0,0 +1,107 @@
|
|||
require 'spec_helper'
|
||||
|
||||
feature 'Clusters Applications', :js do
|
||||
include GoogleApi::CloudPlatformHelpers
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
describe 'Installing applications' do
|
||||
before do
|
||||
visit project_cluster_path(project, cluster)
|
||||
end
|
||||
|
||||
context 'when cluster is being created' do
|
||||
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project])}
|
||||
|
||||
scenario 'user is unable to install applications' do
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button').text).to eq('Install')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cluster is created' do
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project])}
|
||||
|
||||
scenario 'user can install applications' do
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to be_nil
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user installs Helm' do
|
||||
before do
|
||||
allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil)
|
||||
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
page.find(:css, '.js-cluster-application-install-button').click
|
||||
end
|
||||
end
|
||||
|
||||
it 'he sees status transition' do
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
# FE sends request and gets the response, then the buttons is "Install"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
|
||||
|
||||
Clusters::Cluster.last.application_helm.make_installing!
|
||||
|
||||
# FE starts polling and update the buttons to "Installing"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installing')
|
||||
|
||||
Clusters::Cluster.last.application_helm.make_installed!
|
||||
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installed')
|
||||
end
|
||||
|
||||
expect(page).to have_content('Helm Tiller was successfully installed on your cluster')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user installs Ingress' do
|
||||
context 'when user installs application: Ingress' do
|
||||
before do
|
||||
allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil)
|
||||
|
||||
create(:cluster_applications_helm, :installed, cluster: cluster)
|
||||
|
||||
page.within('.js-cluster-application-row-ingress') do
|
||||
page.find(:css, '.js-cluster-application-install-button').click
|
||||
end
|
||||
end
|
||||
|
||||
it 'he sees status transition' do
|
||||
page.within('.js-cluster-application-row-ingress') do
|
||||
# FE sends request and gets the response, then the buttons is "Install"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
|
||||
|
||||
Clusters::Cluster.last.application_ingress.make_installing!
|
||||
|
||||
# FE starts polling and update the buttons to "Installing"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installing')
|
||||
|
||||
Clusters::Cluster.last.application_ingress.make_installed!
|
||||
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installed')
|
||||
end
|
||||
|
||||
expect(page).to have_content('Ingress was successfully installed on your cluster')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
136
spec/features/projects/clusters/gcp_spec.rb
Normal file
136
spec/features/projects/clusters/gcp_spec.rb
Normal file
|
@ -0,0 +1,136 @@
|
|||
require 'spec_helper'
|
||||
|
||||
feature 'Gcp Cluster', :js do
|
||||
include GoogleApi::CloudPlatformHelpers
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
gitlab_sign_in(user)
|
||||
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
|
||||
end
|
||||
|
||||
context 'when user has signed with Google' do
|
||||
before do
|
||||
allow_any_instance_of(Projects::Clusters::GcpController)
|
||||
.to receive(:token_in_session).and_return('token')
|
||||
allow_any_instance_of(Projects::Clusters::GcpController)
|
||||
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
|
||||
end
|
||||
|
||||
context 'when user does not have a cluster and visits cluster index page' do
|
||||
before do
|
||||
visit project_clusters_path(project)
|
||||
|
||||
click_link 'Create on GKE'
|
||||
end
|
||||
|
||||
context 'when user filled form with valid parameters' do
|
||||
before do
|
||||
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
|
||||
.to receive(:projects_zones_clusters_create) do
|
||||
OpenStruct.new(
|
||||
self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
|
||||
status: 'RUNNING'
|
||||
)
|
||||
end
|
||||
|
||||
allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
|
||||
|
||||
fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
|
||||
fill_in 'cluster_name', with: 'dev-cluster'
|
||||
click_button 'Create cluster'
|
||||
end
|
||||
|
||||
it 'user sees a cluster details page and creation status' do
|
||||
expect(page).to have_content('Cluster is being created on Google Container Engine...')
|
||||
|
||||
Clusters::Cluster.last.provider.make_created!
|
||||
|
||||
expect(page).to have_content('Cluster was successfully created on Google Container Engine')
|
||||
end
|
||||
|
||||
it 'user sees a error if something worng during creation' do
|
||||
expect(page).to have_content('Cluster is being created on Google Container Engine...')
|
||||
|
||||
Clusters::Cluster.last.provider.make_errored!('Something wrong!')
|
||||
|
||||
expect(page).to have_content('Something wrong!')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user filled form with invalid parameters' do
|
||||
before do
|
||||
click_button 'Create cluster'
|
||||
end
|
||||
|
||||
it 'user sees a validation error' do
|
||||
expect(page).to have_css('#error_explanation')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does have a cluster and visits cluster page' do
|
||||
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||
|
||||
before do
|
||||
visit project_cluster_path(project, cluster)
|
||||
end
|
||||
|
||||
it 'user sees a cluster details page' do
|
||||
expect(page).to have_button('Save')
|
||||
expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
|
||||
end
|
||||
|
||||
context 'when user disables the cluster' do
|
||||
before do
|
||||
page.find(:css, '.js-toggle-cluster').click
|
||||
click_button 'Save'
|
||||
end
|
||||
|
||||
it 'user sees the successful message' do
|
||||
expect(page).to have_content('Cluster was successfully updated.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user changes cluster parameters' do
|
||||
before do
|
||||
fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
|
||||
click_button 'Save changes'
|
||||
end
|
||||
|
||||
it 'user sees the successful message' do
|
||||
expect(page).to have_content('Cluster was successfully updated.')
|
||||
expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user destroy the cluster' do
|
||||
before do
|
||||
page.accept_confirm do
|
||||
click_link 'Remove integration'
|
||||
end
|
||||
end
|
||||
|
||||
it 'user sees creation form with the successful message' do
|
||||
expect(page).to have_content('Cluster integration was successfully removed.')
|
||||
expect(page).to have_link('Create on GKE')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has not signed with Google' do
|
||||
before do
|
||||
visit project_clusters_path(project)
|
||||
|
||||
click_link 'Create on GKE'
|
||||
end
|
||||
|
||||
it 'user sees a login page' do
|
||||
expect(page).to have_css('.signin-with-google')
|
||||
end
|
||||
end
|
||||
end
|
101
spec/features/projects/clusters/user_spec.rb
Normal file
101
spec/features/projects/clusters/user_spec.rb
Normal file
|
@ -0,0 +1,101 @@
|
|||
require 'spec_helper'
|
||||
|
||||
feature 'User Cluster', :js do
|
||||
include GoogleApi::CloudPlatformHelpers
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
gitlab_sign_in(user)
|
||||
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
|
||||
end
|
||||
|
||||
context 'when user does not have a cluster and visits cluster index page' do
|
||||
before do
|
||||
visit project_clusters_path(project)
|
||||
|
||||
click_link 'Add an existing cluster'
|
||||
end
|
||||
|
||||
context 'when user filled form with valid parameters' do
|
||||
before do
|
||||
fill_in 'cluster_name', with: 'dev-cluster'
|
||||
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com'
|
||||
fill_in 'cluster_platform_kubernetes_attributes_token', with: 'my-token'
|
||||
click_button 'Add cluster'
|
||||
end
|
||||
|
||||
it 'user sees a cluster details page' do
|
||||
expect(page).to have_content('Enable cluster integration')
|
||||
expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
|
||||
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
|
||||
.to have_content('http://example.com')
|
||||
expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
|
||||
.to have_content('my-token')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user filled form with invalid parameters' do
|
||||
before do
|
||||
click_button 'Add cluster'
|
||||
end
|
||||
|
||||
it 'user sees a validation error' do
|
||||
expect(page).to have_css('#error_explanation')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does have a cluster and visits cluster page' do
|
||||
let(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
|
||||
|
||||
before do
|
||||
visit project_cluster_path(project, cluster)
|
||||
end
|
||||
|
||||
it 'user sees a cluster details page' do
|
||||
expect(page).to have_button('Save')
|
||||
end
|
||||
|
||||
context 'when user disables the cluster' do
|
||||
before do
|
||||
page.find(:css, '.js-toggle-cluster').click
|
||||
fill_in 'cluster_name', with: 'dev-cluster'
|
||||
click_button 'Save'
|
||||
end
|
||||
|
||||
it 'user sees the successful message' do
|
||||
expect(page).to have_content('Cluster was successfully updated.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user changes cluster parameters' do
|
||||
before do
|
||||
fill_in 'cluster_name', with: 'my-dev-cluster'
|
||||
fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
|
||||
click_button 'Save changes'
|
||||
end
|
||||
|
||||
it 'user sees the successful message' do
|
||||
expect(page).to have_content('Cluster was successfully updated.')
|
||||
expect(cluster.reload.name).to eq('my-dev-cluster')
|
||||
expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user destroy the cluster' do
|
||||
before do
|
||||
page.accept_confirm do
|
||||
click_link 'Remove integration'
|
||||
end
|
||||
end
|
||||
|
||||
it 'user sees creation form with the successful message' do
|
||||
expect(page).to have_content('Cluster integration was successfully removed.')
|
||||
expect(page).to have_link('Add an existing cluster')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,204 +3,23 @@ require 'spec_helper'
|
|||
feature 'Clusters', :js do
|
||||
include GoogleApi::CloudPlatformHelpers
|
||||
|
||||
let!(:project) { create(:project, :repository) }
|
||||
let!(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_master(user)
|
||||
gitlab_sign_in(user)
|
||||
end
|
||||
|
||||
context 'when user has signed in Google' do
|
||||
before do
|
||||
allow_any_instance_of(Projects::ClustersController)
|
||||
.to receive(:token_in_session).and_return('token')
|
||||
allow_any_instance_of(Projects::ClustersController)
|
||||
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
|
||||
end
|
||||
|
||||
context 'when user does not have a cluster and visits cluster index page' do
|
||||
before do
|
||||
visit project_clusters_path(project)
|
||||
|
||||
click_link 'Create on GKE'
|
||||
end
|
||||
|
||||
it 'user sees a new page' do
|
||||
expect(page).to have_button('Create cluster')
|
||||
end
|
||||
|
||||
context 'when user filled form with valid parameters' do
|
||||
before do
|
||||
double.tap do |dbl|
|
||||
allow(dbl).to receive(:status).and_return('RUNNING')
|
||||
allow(dbl).to receive(:self_link)
|
||||
.and_return('projects/gcp-project-12345/zones/us-central1-a/operations/ope-123')
|
||||
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
|
||||
.to receive(:projects_zones_clusters_create).and_return(dbl)
|
||||
end
|
||||
|
||||
allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
|
||||
|
||||
fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
|
||||
fill_in 'cluster_name', with: 'dev-cluster'
|
||||
click_button 'Create cluster'
|
||||
end
|
||||
|
||||
it 'user sees a cluster details page and creation status' do
|
||||
expect(page).to have_content('Cluster is being created on Google Container Engine...')
|
||||
|
||||
# Application Installation buttons
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button').text).to eq('Install')
|
||||
end
|
||||
|
||||
Clusters::Cluster.last.provider.make_created!
|
||||
|
||||
expect(page).to have_content('Cluster was successfully created on Google Container Engine')
|
||||
end
|
||||
|
||||
it 'user sees a error if something worng during creation' do
|
||||
expect(page).to have_content('Cluster is being created on Google Container Engine...')
|
||||
|
||||
Clusters::Cluster.last.provider.make_errored!('Something wrong!')
|
||||
|
||||
expect(page).to have_content('Something wrong!')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user filled form with invalid parameters' do
|
||||
before do
|
||||
click_button 'Create cluster'
|
||||
end
|
||||
|
||||
it 'user sees a validation error' do
|
||||
expect(page).to have_css('#error_explanation')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has a cluster and visits cluster index page' do
|
||||
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
|
||||
before do
|
||||
visit project_clusters_path(project)
|
||||
end
|
||||
|
||||
it 'user sees an cluster details page' do
|
||||
expect(page).to have_button('Save')
|
||||
expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
|
||||
|
||||
# Application Installation buttons
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to be_nil
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user installs application: Helm Tiller' do
|
||||
before do
|
||||
allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil)
|
||||
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
page.find(:css, '.js-cluster-application-install-button').click
|
||||
end
|
||||
end
|
||||
|
||||
it 'user sees status transition' do
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
# FE sends request and gets the response, then the buttons is "Install"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
|
||||
|
||||
Clusters::Cluster.last.application_helm.make_installing!
|
||||
|
||||
# FE starts polling and update the buttons to "Installing"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installing')
|
||||
|
||||
Clusters::Cluster.last.application_helm.make_installed!
|
||||
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installed')
|
||||
end
|
||||
|
||||
expect(page).to have_content('Helm Tiller was successfully installed on your cluster')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user installs application: Ingress' do
|
||||
before do
|
||||
allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil)
|
||||
# Helm Tiller needs to be installed before you can install Ingress
|
||||
create(:cluster_applications_helm, :installed, cluster: cluster)
|
||||
|
||||
visit project_clusters_path(project)
|
||||
|
||||
page.within('.js-cluster-application-row-ingress') do
|
||||
page.find(:css, '.js-cluster-application-install-button').click
|
||||
end
|
||||
end
|
||||
|
||||
it 'user sees status transition' do
|
||||
page.within('.js-cluster-application-row-ingress') do
|
||||
# FE sends request and gets the response, then the buttons is "Install"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
|
||||
|
||||
Clusters::Cluster.last.application_ingress.make_installing!
|
||||
|
||||
# FE starts polling and update the buttons to "Installing"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installing')
|
||||
|
||||
Clusters::Cluster.last.application_ingress.make_installed!
|
||||
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installed')
|
||||
end
|
||||
|
||||
expect(page).to have_content('Ingress was successfully installed on your cluster')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user disables the cluster' do
|
||||
before do
|
||||
page.find(:css, '.js-toggle-cluster').click
|
||||
click_button 'Save'
|
||||
end
|
||||
|
||||
it 'user sees the succeccful message' do
|
||||
expect(page).to have_content('Cluster was successfully updated.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user destory the cluster' do
|
||||
before do
|
||||
page.accept_confirm do
|
||||
click_link 'Remove integration'
|
||||
end
|
||||
end
|
||||
|
||||
it 'user sees creation form with the succeccful message' do
|
||||
expect(page).to have_content('Cluster integration was successfully removed.')
|
||||
expect(page).to have_link('Create on GKE')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has not signed in Google' do
|
||||
context 'when user does not have a cluster and visits cluster index page' do
|
||||
before do
|
||||
visit project_clusters_path(project)
|
||||
|
||||
click_link 'Create on GKE'
|
||||
end
|
||||
|
||||
it 'user sees a login page' do
|
||||
expect(page).to have_css('.signin-with-google')
|
||||
it 'user sees a new page' do
|
||||
expect(page).to have_button('Create cluster')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,6 +36,20 @@ describe('Clusters', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('showToken', () => {
|
||||
it('should update tye field type', () => {
|
||||
cluster.showTokenButton.click();
|
||||
expect(
|
||||
cluster.tokenField.getAttribute('type'),
|
||||
).toEqual('text');
|
||||
|
||||
cluster.showTokenButton.click();
|
||||
expect(
|
||||
cluster.tokenField.getAttribute('type'),
|
||||
).toEqual('password');
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkForNewInstalls', () => {
|
||||
const INITIAL_APP_MAP = {
|
||||
helm: { status: null, title: 'Helm Tiller' },
|
||||
|
@ -113,7 +127,7 @@ describe('Clusters', () => {
|
|||
});
|
||||
|
||||
describe('when cluster is created', () => {
|
||||
it('should show the success container', () => {
|
||||
it('should show the success container and fresh the page', () => {
|
||||
cluster.updateContainer(null, 'created');
|
||||
|
||||
expect(
|
||||
|
|
Loading…
Reference in a new issue