Commit dbe8ae64 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'ee-57602-create-cluster-validations' into 'master'

Port of 57602-create-cluster-validations to EE

See merge request gitlab-org/gitlab-ee!10467
parents cd9c5e72 29bff59d
......@@ -15,7 +15,7 @@ export default class GlFieldErrors {
initValidators() {
// register selectors here as needed
const validateSelectors = [':text', ':password', '[type=email]']
const validateSelectors = [':text', ':password', '[type=email]', '[type=url]', '[type=number]']
.map(selector => `input${selector}`)
.join(',');
......
......@@ -204,8 +204,10 @@ label {
margin-top: #{$grid-size / 2};
}
.gl-field-error {
.gl-field-error,
.invalid-feedback {
color: $red-500;
font-size: $gl-font-size;
}
.gl-show-field-errors {
......
......@@ -7,25 +7,27 @@
- help_link_end = ' %{external_link_icon}</a>'.html_safe % { external_link_icon: external_link_icon }
%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 Kubernetes cluster integration.').html_safe % { link_to_help_page: link_to_help_page}
- 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 Kubernetes cluster integration.').html_safe % { link_to_help_page: link_to_help_page }
%p= link_to('Select a different Google account', @authorize_url)
= form_for @gcp_cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: clusterable.create_gcp_clusters_path, as: :cluster do |field|
= form_errors(@gcp_cluster)
.form-group
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
= bootstrap_form_for @gcp_cluster, html: { class: 'gl-show-field-errors js-gke-cluster-creation prepend-top-20',
data: { token: token_in_session } }, url: clusterable.create_gcp_clusters_path, as: :cluster do |field|
= field.text_field :name, required: true, title: s_('ClusterIntegration|Cluster name is required.'),
label: s_('ClusterIntegration|Kubernetes cluster name'), label_class: 'label-bold'
- if has_multiple_clusters?
.form-group
= field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
= field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope')
= field.form_group :environment_scope, label: { text: s_('ClusterIntegration|Environment scope'),
class: 'label-bold' } do
= field.text_field :environment_scope, required: true, class: 'form-control',
title: 'Environment scope is required.', wrapper: false
.form-text.text-muted= s_("ClusterIntegration|Choose which of your environments will use this cluster.")
= field.fields_for :provider_gcp, @gcp_cluster.provider_gcp do |provider_gcp_field|
.form-group
= provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-bold'
= provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'),
class: 'label-bold'
.js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } }
= provider_gcp_field.hidden_field :gcp_project_id
.dropdown
......@@ -47,9 +49,9 @@
%p.form-text.text-muted
= s_('ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: zones_link_url }, help_link_end: help_link_end }
.form-group
= provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-bold'
= provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3'
= provider_gcp_field.number_field :num_nodes, required: true, placeholder: '3',
title: s_('ClusterIntegration|Number of nodes must be a numerical value.'),
label: s_('ClusterIntegration|Number of nodes'), label_class: 'label-bold'
.form-group
= provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-bold'
......@@ -64,13 +66,14 @@
= s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
.form-group
.form-check
= provider_gcp_field.check_box :legacy_abac, { class: 'form-check-input' }, false, true
= provider_gcp_field.label :legacy_abac, s_('ClusterIntegration|RBAC-enabled cluster'), class: 'form-check-label label-bold'
.form-text.text-muted
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-core-only'), target: '_blank'
= provider_gcp_field.check_box :legacy_abac, { label: s_('ClusterIntegration|RBAC-enabled cluster'),
label_class: 'label-bold' }, false, true
.form-text.text-muted
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md',
anchor: 'role-based-access-control-rbac-core-only'), target: '_blank'
.form-group
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'),
class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
= form_for @user_cluster, url: clusterable.create_user_clusters_path, as: :cluster do |field|
= form_errors(@user_cluster)
.form-group
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
= bootstrap_form_for @user_cluster, html: { class: 'gl-show-field-errors' },
url: clusterable.create_user_clusters_path, as: :cluster do |field|
= field.text_field :name, required: true, title: s_('ClusterIntegration|Cluster name is required.'),
label: s_('ClusterIntegration|Kubernetes cluster name'), label_class: 'label-bold'
- if has_multiple_clusters?
.form-group
= field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
= field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope')
.form-text.text-muted= s_("ClusterIntegration|Choose which of your environments will use this cluster.")
= field.form_group :environment_scope, label: { text: s_('ClusterIntegration|Environment scope'),
class: 'label-bold' } do
= field.text_field :environment_scope, required: true,
title: 'Environment scope is required.', wrapper: false
.form-text.text-muted
= s_("ClusterIntegration|Choose which of your environments will use this cluster.")
= field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold'
= 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'), class: 'label-bold'
= 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'), class: 'label-bold'
= platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off'
= platform_kubernetes_field.url_field :api_url, required: true,
title: s_('ClusterIntegration|API URL should be a valid http/https url.'),
label: s_('ClusterIntegration|API URL'), label_class: 'label-bold'
= platform_kubernetes_field.text_area :ca_cert,
placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)'),
label: s_('ClusterIntegration|CA Certificate'), label_class: 'label-bold'
= platform_kubernetes_field.text_field :token, required: true,
title: s_('ClusterIntegration|Service token is required.'), label: s_('ClusterIntegration|Service Token'),
autocomplete: 'off', label_class: 'label-bold'
- if @user_cluster.allow_user_defined_namespace?
.form-group
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
= platform_kubernetes_field.text_field :namespace,
label: s_('ClusterIntegration|Project namespace (optional, unique)'), label_class: 'label-bold'
.form-group
.form-check
= platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input qa-rbac-checkbox' }, 'rbac', 'abac'
= platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster'), class: 'form-check-label label-bold'
.form-text.text-muted
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-core-only'), target: '_blank'
= platform_kubernetes_field.form_group :authorization_type do
= platform_kubernetes_field.check_box :authorization_type,
{ class: 'qa-rbac-checkbox', label: s_('ClusterIntegration|RBAC-enabled cluster'),
label_class: 'label-bold', inline: true }, 'rbac', 'abac'
.form-text.text-muted
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md',
anchor: 'role-based-access-control-rbac-core-only'), target: '_blank'
.form-group
= field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
= form_for cluster, url: update_cluster_url_path, as: :cluster do |field|
= form_errors(cluster)
.form-group
- if cluster.read_only_kubernetes_platform_fields?
%label.append-bottom-10{ for: 'cluster-name' }
= s_('ClusterIntegration|Kubernetes cluster name')
.input-group
%input.form-control.cluster-name.js-select-on-focus{ value: cluster.name, readonly: true }
%span.input-group-append
= clipboard_button(text: cluster.name, title: s_('ClusterIntegration|Copy Kubernetes cluster name'), class: 'input-group-text btn-default')
- else
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
.input-group
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
= bootstrap_form_for cluster, url: update_cluster_url_path, html: { class: 'gl-show-field-errors' },
as: :cluster do |field|
- copy_name_btn = clipboard_button(text: cluster.name, title: s_('ClusterIntegration|Copy Kubernetes cluster name'),
class: 'input-group-text btn-default') unless !cluster.read_only_kubernetes_platform_fields?
= field.text_field :name, class: 'js-select-on-focus cluster-name', required: true,
title: s_('ClusterIntegration|Cluster name is required.'),
readonly: cluster.read_only_kubernetes_platform_fields?,
label: s_('ClusterIntegration|Kubernetes cluster name'), label_class: 'label-bold',
input_group_class: 'gl-field-error-anchor', append: copy_name_btn
= field.fields_for :platform_kubernetes, platform do |platform_field|
.form-group
= platform_field.label :api_url, s_('ClusterIntegration|API URL')
.input-group
= platform_field.text_field :api_url, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|API URL'), readonly: cluster.read_only_kubernetes_platform_fields?
- if cluster.read_only_kubernetes_platform_fields?
%span.input-group-append
= clipboard_button(text: platform.api_url, title: s_('ClusterIntegration|Copy API URL'), class: 'input-group-text btn-default')
- copy_api_url = clipboard_button(text: platform.api_url, title: s_('ClusterIntegration|Copy API URL'),
class: 'input-group-text btn-default') unless !cluster.read_only_kubernetes_platform_fields?
= platform_field.text_field :api_url, class: 'js-select-on-focus', required: true,
title: s_('ClusterIntegration|API URL should be a valid http/https url.'),
readonly: cluster.read_only_kubernetes_platform_fields?,
label: s_('ClusterIntegration|API URL'), label_class: 'label-bold',
input_group_class: 'gl-field-error-anchor', append: copy_api_url
- copy_ca_cert_btn = clipboard_button(text: platform.ca_cert, title: s_('ClusterIntegration|Copy CA Certificate'),
class: 'input-group-text btn-default') unless !cluster.read_only_kubernetes_platform_fields?
= platform_field.text_area :ca_cert, class: 'js-select-on-focus', rows: '5',
readonly: cluster.read_only_kubernetes_platform_fields?,
placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)'),
label: s_('ClusterIntegration|CA Certificate'), label_class: 'label-bold',
input_group_class: 'gl-field-error-anchor', append: copy_ca_cert_btn
.form-group
= platform_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
.input-group
= platform_field.text_area :ca_cert, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)'), readonly: cluster.read_only_kubernetes_platform_fields?
- if cluster.read_only_kubernetes_platform_fields?
%span.input-group-append.clipboard-addon
= clipboard_button(text: platform.ca_cert, title: s_('ClusterIntegration|Copy CA Certificate'), class: 'input-group-text btn-blank')
- show_token_btn = (platform_field.button s_('ClusterIntegration|Show'),
type: 'button', class: 'js-show-cluster-token btn btn-default')
- copy_token_btn = clipboard_button(text: platform.token, title: s_('ClusterIntegration|Copy Service Token'),
class: 'input-group-text btn-default') unless !cluster.read_only_kubernetes_platform_fields?
.form-group
= platform_field.label :token, s_('ClusterIntegration|Token')
.input-group
= platform_field.text_field :token, class: 'form-control js-cluster-token js-select-on-focus', type: 'password', placeholder: s_('ClusterIntegration|Token'), readonly: cluster.read_only_kubernetes_platform_fields?
%span.input-group-append
%button.btn.btn-default.input-group-text.js-show-cluster-token{ type: 'button' }
= s_('ClusterIntegration|Show')
- if cluster.read_only_kubernetes_platform_fields?
= clipboard_button(text: platform.token, title: s_('ClusterIntegration|Copy Token'), class: 'btn-default')
= platform_field.text_field :token, type: 'password', class: 'js-select-on-focus js-cluster-token',
required: true, title: s_('ClusterIntegration|Service token is required.'),
readonly: cluster.read_only_kubernetes_platform_fields?,
label: s_('ClusterIntegration|Service Token'), label_class: 'label-bold',
input_group_class: 'gl-field-error-anchor', append: show_token_btn + copy_token_btn
- if cluster.allow_user_defined_namespace?
.form-group
= platform_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
= platform_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
= platform_field.text_field :namespace, label: s_('ClusterIntegration|Project namespace (optional, unique)'),
label_class: 'label-bold'
.form-group
.form-check
= platform_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
= platform_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster'), class: 'form-check-label label-bold'
.form-text.text-muted
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
= platform_field.form_group :authorization_type do
= platform_field.check_box :authorization_type, { disabled: true, label: s_('ClusterIntegration|RBAC-enabled cluster'),
label_class: 'label-bold', inline: true }, 'rbac', 'abac'
.form-text.text-muted
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
.form-group
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
---
title: Display cluster form validation error messages inline
merge_request: 26502
author:
type: changed
......@@ -13,12 +13,12 @@ module EE
def unique_environment_scope
if project && project.clusters.where(environment_scope: environment_scope).where.not(id: id).exists?
errors.add(:base, "cannot add duplicated environment scope")
errors.add(:environment_scope, "cannot add duplicated environment scope")
return false
end
if group && group.clusters.where(environment_scope: environment_scope).where.not(id: id).exists?
errors.add(:base, 'cannot add duplicated environment scope')
errors.add(:environment_scope, 'cannot add duplicated environment scope')
return false
end
......
......@@ -64,6 +64,9 @@ describe 'EE Clusters', :js do
click_link 'Add existing cluster'
fill_in 'cluster_name', with: 'staging-cluster'
fill_in 'cluster_environment_scope', with: '*'
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'https://0.0.0.0'
fill_in 'cluster_platform_kubernetes_attributes_token', with: 'token'
click_button 'Add Kubernetes cluster'
end
......
......@@ -2256,6 +2256,9 @@ msgstr ""
msgid "ClusterIntegration|API URL"
msgstr ""
msgid "ClusterIntegration|API URL should be a valid http/https url."
msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
......@@ -2319,6 +2322,9 @@ msgstr ""
msgid "ClusterIntegration|Cluster health"
msgstr ""
msgid "ClusterIntegration|Cluster name is required."
msgstr ""
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
......@@ -2340,7 +2346,7 @@ msgstr ""
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
msgid "ClusterIntegration|Copy Token"
msgid "ClusterIntegration|Copy Service Token"
msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
......@@ -2538,6 +2544,9 @@ msgstr ""
msgid "ClusterIntegration|Number of nodes"
msgstr ""
msgid "ClusterIntegration|Number of nodes must be a numerical value."
msgstr ""
msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
......@@ -2550,9 +2559,6 @@ msgstr ""
msgid "ClusterIntegration|Project cluster"
msgstr ""
msgid "ClusterIntegration|Project namespace"
msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
......@@ -2619,7 +2625,10 @@ msgstr ""
msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
msgid "ClusterIntegration|Service Token"
msgstr ""
msgid "ClusterIntegration|Service token is required."
msgstr ""
msgid "ClusterIntegration|Show"
......@@ -2652,9 +2661,6 @@ msgstr ""
msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
msgid "ClusterIntegration|Update failed. Please check the logs and try again."
msgstr ""
......
......@@ -6,7 +6,7 @@ module QA
class AddExisting < Page::Base
view 'app/views/clusters/clusters/user/_form.html.haml' do
element :cluster_name, 'text_field :name' # rubocop:disable QA/ElementWithPattern
element :api_url, 'text_field :api_url' # rubocop:disable QA/ElementWithPattern
element :api_url, 'url_field :api_url' # rubocop:disable QA/ElementWithPattern
element :ca_certificate, 'text_area :ca_cert' # rubocop:disable QA/ElementWithPattern
element :token, 'text_field :token' # rubocop:disable QA/ElementWithPattern
element :add_cluster_button, "submit s_('ClusterIntegration|Add Kubernetes cluster')" # rubocop:disable QA/ElementWithPattern
......
......@@ -69,7 +69,7 @@ describe 'User Cluster', :js do
end
it 'user sees a validation error' do
expect(page).to have_css('#error_explanation')
expect(page).to have_css('.gl-field-error')
end
end
end
......
......@@ -92,7 +92,7 @@ describe 'Gcp Cluster', :js do
end
it 'user sees a validation error' do
expect(page).to have_css('#error_explanation')
expect(page).to have_css('.gl-field-error')
end
end
end
......
......@@ -53,7 +53,7 @@ describe 'User Cluster', :js do
end
it 'user sees a validation error' do
expect(page).to have_css('#error_explanation')
expect(page).to have_css('.gl-field-error')
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment