diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 665a9c77822..489615f1f78 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -84,6 +84,9 @@ export default { ingressExternalIp() { return this.applications.ingress.externalIp; }, + certManagerInstalled() { + return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED; + }, ingressDescription() { const extraCostParagraph = sprintf( _.escape( @@ -130,9 +133,9 @@ export default { return sprintf( _.escape( s__( - `ClusterIntegration|cert-manager is a native Kubernetes certificate management controller that helps with issuing certificates. - Installing cert-manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates - are valid and up to date.`, + `ClusterIntegration|Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. + Installing Cert-Manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates + are valid and up-to-date.`, ), ), { @@ -259,6 +262,16 @@ export default { +

+ {{ + s__(`ClusterIntegration|Point a wildcard DNS to this + generated IP address in order to access + your application after it has been deployed.`) + }} + + {{ __('More information') }} + +

@@ -272,17 +285,6 @@ export default { {{ __('More information') }}

- -

- {{ - s__(`ClusterIntegration|Point a wildcard DNS to this - generated IP address in order to access - your application after it has been deployed.`) - }} - - {{ __('More information') }} - -

@@ -295,10 +297,41 @@ export default { :status-reason="applications.cert_manager.statusReason" :request-status="applications.cert_manager.requestStatus" :request-reason="applications.cert_manager.requestReason" + :install-application-request-params="{ email: applications.cert_manager.email }" :disabled="!helmInstalled" title-link="https://cert-manager.readthedocs.io/en/latest/#" > -
+ + +

+ {{ + s__(`ClusterIntegration|Replace this with your own hostname if you want. + If you do so, point hostname to Ingress IP Address from above.`) + }} + + {{ __('More information') }} + +

-

- {{ - s__(`ClusterIntegration|Replace this with your own hostname if you want. - If you do so, point hostname to Ingress IP Address from above.`) - }} - - {{ __('More information') }} - -

diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js index 15cf4a56138..e31afadf186 100644 --- a/app/assets/javascripts/clusters/constants.js +++ b/app/assets/javascripts/clusters/constants.js @@ -24,3 +24,4 @@ export const REQUEST_FAILURE = 'request-failure'; export const INGRESS = 'ingress'; export const JUPYTER = 'jupyter'; export const KNATIVE = 'knative'; +export const CERT_MANAGER = 'cert_manager'; diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index 2d69da8eaec..c750daab112 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -1,5 +1,5 @@ import { s__ } from '../../locale'; -import { INGRESS, JUPYTER, KNATIVE } from '../constants'; +import { INGRESS, JUPYTER, KNATIVE, CERT_MANAGER } from '../constants'; export default class ClusterStore { constructor() { @@ -30,6 +30,7 @@ export default class ClusterStore { statusReason: null, requestStatus: null, requestReason: null, + email: null, }, runner: { title: s__('ClusterIntegration|GitLab Runner'), @@ -103,6 +104,9 @@ export default class ClusterStore { if (appId === INGRESS) { this.state.applications.ingress.externalIp = serverAppEntry.external_ip; + } else if (appId === CERT_MANAGER) { + this.state.applications.cert_manager.email = + this.state.applications.cert_manager.email || serverAppEntry.email; } else if (appId === JUPYTER) { this.state.applications.jupyter.hostname = serverAppEntry.hostname || diff --git a/app/controllers/clusters/applications_controller.rb b/app/controllers/clusters/applications_controller.rb index 250f42f3096..c4e7fc950f9 100644 --- a/app/controllers/clusters/applications_controller.rb +++ b/app/controllers/clusters/applications_controller.rb @@ -23,6 +23,6 @@ class Clusters::ApplicationsController < Clusters::BaseController end def create_cluster_application_params - params.permit(:application, :hostname) + params.permit(:application, :hostname, :email) end end diff --git a/app/models/clusters/applications/cert_manager.rb b/app/models/clusters/applications/cert_manager.rb index 077e2bda143..74ef7c7e145 100644 --- a/app/models/clusters/applications/cert_manager.rb +++ b/app/models/clusters/applications/cert_manager.rb @@ -14,6 +14,10 @@ module Clusters default_value_for :version, VERSION + default_value_for :email do |cert_manager| + cert_manager.cluster&.user&.email + end + validates :email, presence: true def chart diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index 2bd17e58086..7b1a0be75ca 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -6,4 +6,5 @@ class ClusterApplicationEntity < Grape::Entity expose :status_reason expose :external_ip, if: -> (e, _) { e.respond_to?(:external_ip) } expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) } + expose :email, if: -> (e, _) { e.respond_to?(:email) } end diff --git a/app/services/clusters/applications/create_service.rb b/app/services/clusters/applications/create_service.rb index a89772e82dc..92c2c1b9834 100644 --- a/app/services/clusters/applications/create_service.rb +++ b/app/services/clusters/applications/create_service.rb @@ -20,7 +20,7 @@ module Clusters end if application.has_attribute?(:email) - application.email = current_user.email + application.email = params[:email] end if application.respond_to?(:oauth_application) diff --git a/changelogs/unreleased/cert-manager-email.yml b/changelogs/unreleased/cert-manager-email.yml new file mode 100644 index 00000000000..530608d9660 --- /dev/null +++ b/changelogs/unreleased/cert-manager-email.yml @@ -0,0 +1,5 @@ +--- +title: Ability to override email for cert-manager +merge_request: 23503 +author: Amit Rathi +type: added diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 6d05e2feeec..e40525d2577 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -267,7 +267,7 @@ deployments. | ----------- | :------------: | ----------- | --------------- | | [Helm Tiller](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. | n/a | | [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) | -| [Cert Manager](http://docs.cert-manager.io/en/latest/) | 11.6+ | Cert Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert Manager on your cluster will issue a certificate by [Let's Encrypt](https://letsencrypt.org/) and ensure that certificates are valid and up to date. The email address used by Let's Encrypt registration will be taken from the GitLab user that installed Cert Manager on the cluster. | [stable/cert-manager](https://github.com/helm/charts/tree/master/stable/cert-manager) | +| [Cert Manager](http://docs.cert-manager.io/en/latest/) | 11.6+ | Cert Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert Manager on your cluster will issue a certificate by [Let's Encrypt](https://letsencrypt.org/) and ensure that certificates are valid and up-to-date. | [stable/cert-manager](https://github.com/helm/charts/tree/master/stable/cert-manager) | | [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) | | [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) | | [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. You will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix). More information on creating executable runbooks can be found at [Nurtch Documentation](http://docs.nurtch.com/en/latest). **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) | diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d9707e17e4a..acc1a02d1d5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1473,6 +1473,9 @@ msgstr "" msgid "ClusterIntegration|Cert-Manager" msgstr "" +msgid "ClusterIntegration|Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert-Manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates are valid and up-to-date." +msgstr "" + msgid "ClusterIntegration|Certificate Authority bundle (PEM format)" msgstr "" @@ -1587,6 +1590,12 @@ msgstr "" msgid "ClusterIntegration|Integration status" msgstr "" +msgid "ClusterIntegration|Issuer Email" +msgstr "" + +msgid "ClusterIntegration|Issuers represent a certificate authority. You must provide an email address for your Issuer. " +msgstr "" + msgid "ClusterIntegration|Jupyter Hostname" msgstr "" @@ -1806,9 +1815,6 @@ msgstr "" msgid "ClusterIntegration|access to Google Kubernetes Engine" msgstr "" -msgid "ClusterIntegration|cert-manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing cert-manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates are valid and up to date." -msgstr "" - msgid "ClusterIntegration|check the pricing here" msgstr "" diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index 71d715237f5..8918a7b7b9c 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -70,6 +70,44 @@ describe 'Clusters Applications', :js do end end + context 'when user installs Cert Manager' do + before do + allow(ClusterInstallAppWorker).to receive(:perform_async) + allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) + allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) + + create(:clusters_applications_helm, :installed, cluster: cluster) + + page.within('.js-cluster-application-row-cert_manager') do + click_button 'Install' + end + end + + it 'shows status transition' do + def email_form_value + page.find('.js-email').value + end + + page.within('.js-cluster-application-row-cert_manager') do + expect(email_form_value).to eq(cluster.user.email) + expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install') + + page.find('.js-email').set("new_email@example.org") + Clusters::Cluster.last.application_cert_manager.make_installing! + + expect(email_form_value).to eq('new_email@example.org') + expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') + + Clusters::Cluster.last.application_cert_manager.make_installed! + + expect(email_form_value).to eq('new_email@example.org') + expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installed') + end + + expect(page).to have_content('Cert-Manager was successfully installed on your Kubernetes cluster') + end + end + context 'when user installs Ingress' do context 'when user installs application: Ingress' do before do diff --git a/spec/fixtures/api/schemas/cluster_status.json b/spec/fixtures/api/schemas/cluster_status.json index ccef17a6615..3d9e0628f63 100644 --- a/spec/fixtures/api/schemas/cluster_status.json +++ b/spec/fixtures/api/schemas/cluster_status.json @@ -32,7 +32,8 @@ }, "status_reason": { "type": ["string", "null"] }, "external_ip": { "type": ["string", "null"] }, - "hostname": { "type": ["string", "null"] } + "hostname": { "type": ["string", "null"] }, + "email": { "type": ["string", "null"] } }, "required" : [ "name", "status" ] } diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index e46edec9abb..14ef1193984 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -176,6 +176,54 @@ describe('Applications', () => { }); }); + describe('Cert-Manager application', () => { + describe('when not installed', () => { + it('renders email & allows editing', () => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller', status: 'installed' }, + ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + cert_manager: { + title: 'Cert-Manager', + email: 'before@example.com', + status: 'installable', + }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' }, + knative: { title: 'Knative', hostname: '', status: 'installable' }, + }, + }); + + expect(vm.$el.querySelector('.js-email').value).toEqual('before@example.com'); + expect(vm.$el.querySelector('.js-email').getAttribute('readonly')).toBe(null); + }); + }); + + describe('when installed', () => { + it('renders email in readonly', () => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller', status: 'installed' }, + ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + cert_manager: { + title: 'Cert-Manager', + email: 'after@example.com', + status: 'installed', + }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' }, + knative: { title: 'Knative', hostname: '', status: 'installable' }, + }, + }); + + expect(vm.$el.querySelector('.js-email').value).toEqual('after@example.com'); + expect(vm.$el.querySelector('.js-email').getAttribute('readonly')).toEqual('readonly'); + }); + }); + }); + describe('Jupyter application', () => { describe('with ingress installed with ip & jupyter installable', () => { it('renders hostname active input', () => { diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js index 540d7f30858..3c3d9977ffb 100644 --- a/spec/javascripts/clusters/services/mock_data.js +++ b/spec/javascripts/clusters/services/mock_data.js @@ -42,6 +42,7 @@ const CLUSTERS_MOCK_DATA = { name: 'cert_manager', status: APPLICATION_STATUS.ERROR, status_reason: 'Cannot connect', + email: 'test@example.com', }, ], }, @@ -86,6 +87,7 @@ const CLUSTERS_MOCK_DATA = { name: 'cert_manager', status: APPLICATION_STATUS.ERROR, status_reason: 'Cannot connect', + email: 'test@example.com', }, ], }, diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index 7ea0878ad45..1ca55549094 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -115,6 +115,7 @@ describe('Clusters Store', () => { statusReason: mockResponseData.applications[6].status_reason, requestStatus: null, requestReason: null, + email: mockResponseData.applications[6].email, }, }, }); diff --git a/spec/services/clusters/applications/create_service_spec.rb b/spec/services/clusters/applications/create_service_spec.rb index 0bd7719345e..1a2ca23748a 100644 --- a/spec/services/clusters/applications/create_service_spec.rb +++ b/spec/services/clusters/applications/create_service_spec.rb @@ -31,6 +31,31 @@ describe Clusters::Applications::CreateService do subject end + context 'cert manager application' do + let(:params) do + { + application: 'cert_manager', + email: 'test@example.com' + } + end + + before do + allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute) + end + + it 'creates the application' do + expect do + subject + + cluster.reload + end.to change(cluster, :application_cert_manager) + end + + it 'sets the email' do + expect(subject.email).to eq('test@example.com') + end + end + context 'jupyter application' do let(:params) do {