diff --git a/app/controllers/projects/clusters/gcp_controller.rb b/app/controllers/projects/clusters/gcp_controller.rb new file mode 100644 index 00000000000..9bf22721541 --- /dev/null +++ b/app/controllers/projects/clusters/gcp_controller.rb @@ -0,0 +1,78 @@ +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] + before_action :authorize_update_cluster!, only: [:update] + before_action :authorize_admin_cluster!, only: [:destroy] + + 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 + @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 + puts "NOT PERSISTED" + 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: :user, + 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 diff --git a/app/controllers/projects/clusters/kubernetes_controller.rb b/app/controllers/projects/clusters/kubernetes_controller.rb new file mode 100644 index 00000000000..e0ae5e846c6 --- /dev/null +++ b/app/controllers/projects/clusters/kubernetes_controller.rb @@ -0,0 +1,39 @@ +class Projects::Clusters::KubernetesController < 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(nil) + + 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 diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb index 9a56c9de858..0d6c5831b8d 100644 --- a/app/controllers/projects/clusters_controller.rb +++ b/app/controllers/projects/clusters_controller.rb @@ -1,8 +1,7 @@ class Projects::ClustersController < Projects::ApplicationController - before_action :cluster, except: [:login, :index, :new, :new_gcp, :create] + before_action :cluster, except: [:login, :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] @@ -14,39 +13,6 @@ 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 @@ -88,49 +54,13 @@ 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) || render_404 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 - end - end - def authorize_update_cluster! access_denied! unless can?(current_user, :update_cluster, cluster) end diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 6d7fb4b7dbf..e13e367fb92 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -28,6 +28,8 @@ module Clusters validates :name, cluster_name: true validate :restrict_modification, on: :update + validates_associated :provider_gcp, :platform_kubernetes + delegate :status, to: :provider, allow_nil: true delegate :status_reason, to: :provider, allow_nil: true delegate :on_creation?, to: :provider, allow_nil: true @@ -70,6 +72,10 @@ module Clusters return platform_kubernetes if kubernetes? end + def managed? + !user? + end + def first_project return @first_project if defined?(@first_project) diff --git a/app/views/projects/clusters/_form.html.haml b/app/views/projects/clusters/_form.html.haml index 1f8ae463d0f..acd4e5c058d 100644 --- a/app/views/projects/clusters/_form.html.haml +++ b/app/views/projects/clusters/_form.html.haml @@ -1,35 +1,21 @@ -.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_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')) - = 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' += 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') + = platform_kubernetes_field.text_field :api_url, class: 'form-control', disabled: @cluster.managed? - = 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 + = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)') + = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: Clusters::Platforms::Kubernetes.namespace_for_project(@project), disabled: @cluster.managed? - .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' + .form-group + = field.submit s_('ClusterIntegration|Save cluster'), class: 'btn btn-save' diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml new file mode 100644 index 00000000000..139796cf798 --- /dev/null +++ b/app/views/projects/clusters/gcp/_form.html.haml @@ -0,0 +1,34 @@ +.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: providers_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' + + = 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-4' + + .form-group + = field.submit s_('ClusterIntegration|Create cluster'), class: 'btn btn-save' diff --git a/app/views/projects/clusters/login.html.haml b/app/views/projects/clusters/gcp/login.html.haml similarity index 88% rename from app/views/projects/clusters/login.html.haml rename to app/views/projects/clusters/gcp/login.html.haml index fde030b500b..978adc12ca0 100644 --- a/app/views/projects/clusters/login.html.haml +++ b/app/views/projects/clusters/gcp/login.html.haml @@ -3,9 +3,9 @@ .row.prepend-top-default .col-sm-4 - = render 'sidebar' + = render 'projects/clusters/sidebar' .col-sm-8 - = render 'header' + = render 'projects/clusters/header' .row .col-sm-8.col-sm-offset-4.signin-with-google - if @authorize_url diff --git a/app/views/projects/clusters/new_gcp.html.haml b/app/views/projects/clusters/gcp/new.html.haml similarity index 60% rename from app/views/projects/clusters/new_gcp.html.haml rename to app/views/projects/clusters/gcp/new.html.haml index 48e6b6ae8e8..75a1ad6e4b2 100644 --- a/app/views/projects/clusters/new_gcp.html.haml +++ b/app/views/projects/clusters/gcp/new.html.haml @@ -3,8 +3,8 @@ .row.prepend-top-default .col-sm-4 - = render 'sidebar' + = render 'projects/clusters/sidebar' .col-sm-8 - = render 'header' + = render 'projects/clusters/header' = render 'form' diff --git a/app/views/projects/clusters/kubernetes/_form.html.haml b/app/views/projects/clusters/kubernetes/_form.html.haml new file mode 100644 index 00000000000..e6c89513aa8 --- /dev/null +++ b/app/views/projects/clusters/kubernetes/_form.html.haml @@ -0,0 +1,31 @@ +.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: platforms_kubernetes_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' + + = 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' + + .form-group + = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token') + = platform_kubernetes_field.text_field :token, class: 'form-control' + + .form-group + = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|Token') + = platform_kubernetes_field.text_field :ca_cert, class: 'form-control' + + .form-group + = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)') + = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: Clusters::Platforms::Kubernetes.namespace_for_project(@project) + + .form-group + = field.submit s_('ClusterIntegration|Create cluster'), class: 'btn btn-save' diff --git a/app/views/projects/clusters/kubernetes/new.html.haml b/app/views/projects/clusters/kubernetes/new.html.haml new file mode 100644 index 00000000000..75a1ad6e4b2 --- /dev/null +++ b/app/views/projects/clusters/kubernetes/new.html.haml @@ -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/header' + += render 'form' diff --git a/app/views/projects/clusters/new.html.haml b/app/views/projects/clusters/new.html.haml index 665120c7e49..5ab349f8e93 100644 --- a/app/views/projects/clusters/new.html.haml +++ b/app/views/projects/clusters/new.html.haml @@ -14,7 +14,7 @@ - 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') + %p= s_('ClusterIntegration| Create a new cluster on Google 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| Enter the details for an existing Kubernetes cluster') + = link_to s_('ClusterIntegration|Add an existing cluster'), platforms_kubernetes_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20' diff --git a/app/views/projects/clusters/show.html.haml b/app/views/projects/clusters/show.html.haml index b7671f5e3c4..48fc18af832 100644 --- a/app/views/projects/clusters/show.html.haml +++ b/app/views/projects/clusters/show.html.haml @@ -66,14 +66,7 @@ %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')) + = render 'form' %section.settings.no-animate#js-cluster-advanced-settings{ class: ('expanded' if expanded) } .settings-header diff --git a/config/routes/project.rb b/config/routes/project.rb index bdafaba3ab3..d2fb0c3ee12 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -183,10 +183,14 @@ 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 + get '/platforms/kubernetes/new', to: 'clusters/kubernetes#new' + post '/platforms/kubernetes', to: 'clusters/kubernetes#create' + + get '/providers/gcp/new', to: 'clusters/gcp#new' + get '/providers/gcp/login', to: 'clusters/gcp#login' + post '/providers/gcp', to: 'clusters/gcp#create' end member do