diff options
11 files changed, 286 insertions, 168 deletions
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index 6ebd1ad109e..1667d027a84 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -36,6 +36,7 @@ export default class Clusters { installRunnerPath, installJupyterPath, installKnativePath, + updateKnativePath, installPrometheusPath, managePrometheusPath, hasRbac, @@ -62,6 +63,7 @@ export default class Clusters { installPrometheusEndpoint: installPrometheusPath, installJupyterEndpoint: installJupyterPath, installKnativeEndpoint: installKnativePath, + updateKnativeEndpoint: updateKnativePath, }); this.installApplication = this.installApplication.bind(this); @@ -129,6 +131,8 @@ export default class Clusters { eventHub.$on('upgradeApplication', data => this.upgradeApplication(data)); eventHub.$on('upgradeFailed', appId => this.upgradeFailed(appId)); eventHub.$on('dismissUpgradeSuccess', appId => this.dismissUpgradeSuccess(appId)); + eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data)); + eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data)); } removeListeners() { @@ -137,6 +141,8 @@ export default class Clusters { eventHub.$off('upgradeApplication', this.upgradeApplication); eventHub.$off('upgradeFailed', this.upgradeFailed); eventHub.$off('dismissUpgradeSuccess', this.dismissUpgradeSuccess); + eventHub.$off('saveKnativeDomain'); + eventHub.$off('setKnativeHostname'); } initPolling() { @@ -272,6 +278,18 @@ export default class Clusters { this.store.updateAppProperty(appId, 'requestStatus', null); } + saveKnativeDomain(data) { + const appId = data.id; + this.store.updateAppProperty(appId, 'status', APPLICATION_STATUS.UPDATING); + this.service.updateApplication(appId, data.params); + } + + setKnativeHostname(data) { + const appId = data.id; + this.store.updateAppProperty(appId, 'isEditingHostName', true); + this.store.updateAppProperty(appId, 'hostname', data.hostname); + } + destroy() { this.destroyed = true; diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue index 5952e93b9a7..19e5ac1567d 100644 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ b/app/assets/javascripts/clusters/components/application_row.vue @@ -191,14 +191,7 @@ export default { return this.status === APPLICATION_STATUS.UPDATE_ERRORED; }, upgradeFailureDescription() { - return sprintf( - s__( - 'ClusterIntegration|Something went wrong when upgrading %{title}. Please check the logs and try again.', - ), - { - title: this.title, - }, - ); + return s__('ClusterIntegration|Update failed. Please check the logs and try again.'); }, upgradeSuccessDescription() { return sprintf(s__('ClusterIntegration|%{title} upgraded successfully.'), { @@ -210,9 +203,9 @@ export default { if (this.upgradeAvailable && !this.upgradeFailed && !this.isUpgrading) { label = s__('ClusterIntegration|Upgrade'); } else if (this.isUpgrading) { - label = s__('ClusterIntegration|Upgrading'); + label = s__('ClusterIntegration|Updating'); } else if (this.upgradeFailed) { - label = s__('ClusterIntegration|Retry upgrade'); + label = s__('ClusterIntegration|Retry update'); } return label; @@ -224,6 +217,14 @@ export default { (this.upgradeRequested && !this.upgradeSuccessful) ); }, + shouldShowUpgradeDetails() { + // This method only returns true when; + // Upgrade was successful OR Upgrade failed + // AND new upgrade is unavailable AND version information is present. + return ( + (this.upgradeSuccessful || this.upgradeFailed) && !this.upgradeAvailable && this.version + ); + }, }, watch: { status() { @@ -303,7 +304,7 @@ export default { </div> <div - v-if="(upgradeSuccessful || upgradeFailed) && !upgradeAvailable" + v-if="shouldShowUpgradeDetails" class="form-text text-muted label p-0 js-cluster-application-upgrade-details" > {{ versionLabel }} diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 0cf187d4189..f74cd71de04 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -15,11 +15,14 @@ import { s__, sprintf } from '../../locale'; import applicationRow from './application_row.vue'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import { CLUSTER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants'; +import LoadingButton from '~/vue_shared/components/loading_button.vue'; +import eventHub from '~/clusters/event_hub'; export default { components: { applicationRow, clipboardButton, + LoadingButton, }, props: { type: { @@ -173,16 +176,55 @@ export default { jupyterHostname() { return this.applications.jupyter.hostname; }, + knative() { + return this.applications.knative; + }, knativeInstalled() { - return this.applications.knative.status === APPLICATION_STATUS.INSTALLED; + return ( + this.knative.status === APPLICATION_STATUS.INSTALLED || + this.knativeUpgrading || + this.knativeUpgradeFailed || + this.knative.status === APPLICATION_STATUS.UPDATED + ); + }, + knativeUpgrading() { + return ( + this.knative.status === APPLICATION_STATUS.UPDATING || + this.knative.status === APPLICATION_STATUS.SCHEDULED + ); + }, + knativeUpgradeFailed() { + return this.knative.status === APPLICATION_STATUS.UPDATE_ERRORED; }, knativeExternalIp() { - return this.applications.knative.externalIp; + return this.knative.externalIp; + }, + canUpdateKnativeEndpoint() { + return this.knativeExternalIp && !this.knativeUpgradeFailed && !this.knativeUpgrading; + }, + knativeHostname: { + get() { + return this.knative.hostname; + }, + set(hostname) { + eventHub.$emit('setKnativeHostname', { + id: 'knative', + hostname, + }); + }, }, }, created() { this.helmInstallIllustration = helmInstallIllustration; }, + methods: { + saveKnativeDomain() { + eventHub.$emit('saveKnativeDomain', { + id: 'knative', + params: { hostname: this.knative.hostname }, + }); + }, + }, }; </script> @@ -471,76 +513,88 @@ export default { }} </p> - <template v-if="knativeInstalled"> - <div class="form-group"> - <label for="knative-domainname"> - {{ s__('ClusterIntegration|Knative Domain Name:') }} - </label> - <input - id="knative-domainname" - v-model="applications.knative.hostname" - type="text" - class="form-control js-domainname" - readonly - /> - </div> - </template> - <template v-else-if="helmInstalled && rbac"> - <div class="form-group"> - <label for="knative-domainname"> - {{ s__('ClusterIntegration|Knative Domain Name:') }} - </label> - <input - id="knative-domainname" - v-model="applications.knative.hostname" - type="text" - class="form-control js-domainname" - /> - </div> - </template> - <template v-if="knativeInstalled"> - <div class="form-group"> - <label for="knative-ip-address"> - {{ s__('ClusterIntegration|Knative IP Address:') }} - </label> - <div v-if="knativeExternalIp" class="input-group"> + <div class="row"> + <template v-if="knativeInstalled || (helmInstalled && rbac)"> + <div + :class="{ 'col-md-6': knativeInstalled, 'col-12': helmInstalled && rbac }" + class="form-group col-sm-12 mb-0" + > + <label for="knative-domainname"> + <strong> + {{ s__('ClusterIntegration|Knative Domain Name:') }} + </strong> + </label> <input - id="knative-ip-address" - :value="knativeExternalIp" + id="knative-domainname" + v-model="knativeHostname" type="text" - class="form-control js-ip-address" - readonly + class="form-control js-knative-domainname" /> - <span class="input-group-append"> - <clipboard-button - :text="knativeExternalIp" - :title="s__('ClusterIntegration|Copy Knative IP Address to clipboard')" - class="input-group-text js-clipboard-btn" + </div> + </template> + <template v-if="knativeInstalled"> + <div class="form-group col-sm-12 col-md-6 pl-md-0 mb-0 mt-3 mt-md-0"> + <label for="knative-ip-address"> + <strong> + {{ s__('ClusterIntegration|Knative Endpoint:') }} + </strong> + </label> + <div v-if="knativeExternalIp" class="input-group"> + <input + id="knative-ip-address" + :value="knativeExternalIp" + type="text" + class="form-control js-knative-ip-address" + readonly /> - </span> + <span class="input-group-append"> + <clipboard-button + :text="knativeExternalIp" + :title="s__('ClusterIntegration|Copy Knative Endpoint to clipboard')" + class="input-group-text js-knative-ip-clipboard-btn" + /> + </span> + </div> + <input + v-else + type="text" + class="form-control js-knative-ip-address" + readonly + value="?" + /> </div> - <input v-else type="text" class="form-control js-ip-address" readonly value="?" /> - </div> - <p v-if="!knativeExternalIp" class="settings-message js-no-ip-message"> - {{ - s__(`ClusterIntegration|The IP address is in - the process of being assigned. Please check your Kubernetes - cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) - }} - </p> + <p class="form-text text-muted col-12"> + {{ + s__( + `ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint.`, + ) + }} + <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer"> + {{ __('More information') }} + </a> + </p> - <p> - {{ - s__(`ClusterIntegration|Point a wildcard DNS to this - generated IP address in order to access - your application after it has been deployed.`) - }} - <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer"> - {{ __('More information') }} - </a> - </p> - </template> + <p + v-if="!knativeExternalIp" + class="settings-message js-no-knative-ip-message mt-2 mr-3 mb-0 ml-3 " + > + {{ + s__(`ClusterIntegration|The IP address is in + the process of being assigned. Please check your Kubernetes + cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) + }} + </p> + + <button + v-if="canUpdateKnativeEndpoint" + class="btn btn-success js-knative-save-domain-button mt-3 ml-3" + @click="saveKnativeDomain" + > + {{ s__('ClusterIntegration|Save changes') }} + </button> + </template> + </div> </div> </application-row> </div> diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js index 89dda4b7902..dea33ac44c5 100644 --- a/app/assets/javascripts/clusters/services/clusters_service.js +++ b/app/assets/javascripts/clusters/services/clusters_service.js @@ -12,6 +12,9 @@ export default class ClusterService { jupyter: this.options.installJupyterEndpoint, knative: this.options.installKnativeEndpoint, }; + this.appUpdateEndpointMap = { + knative: this.options.updateKnativeEndpoint, + }; } fetchData() { @@ -22,6 +25,10 @@ export default class ClusterService { return axios.post(this.appInstallEndpointMap[appId], params); } + updateApplication(appId, params) { + return axios.patch(this.appUpdateEndpointMap[appId], params); + } + static updateCluster(endpoint, data) { return axios.put(endpoint, data); } diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index d309678be27..3f03a8512fc 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -66,6 +66,7 @@ export default class ClusterStore { requestStatus: null, requestReason: null, hostname: null, + isEditingHostName: false, externalIp: null, }, }, @@ -129,8 +130,10 @@ export default class ClusterStore { ? `jupyter.${this.state.applications.ingress.externalIp}.nip.io` : ''); } else if (appId === KNATIVE) { - this.state.applications.knative.hostname = - serverAppEntry.hostname || this.state.applications.knative.hostname; + if (!this.state.applications.knative.isEditingHostName) { + this.state.applications.knative.hostname = + serverAppEntry.hostname || this.state.applications.knative.hostname; + } this.state.applications.knative.externalIp = serverAppEntry.external_ip || this.state.applications.knative.externalIp; } else if (appId === RUNNER) { diff --git a/changelogs/unreleased/56937-edit-knative-domain.yml b/changelogs/unreleased/56937-edit-knative-domain.yml new file mode 100644 index 00000000000..7147a4e06b1 --- /dev/null +++ b/changelogs/unreleased/56937-edit-knative-domain.yml @@ -0,0 +1,5 @@ +--- +title: Edit Knative domain after it has been deployed +merge_request: 25386 +author: +type: added diff --git a/locale/gitlab.pot b/locale/gitlab.pot index cb599e4744c..ce29fc489be 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1683,7 +1683,7 @@ msgstr "" msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard" msgstr "" -msgid "ClusterIntegration|Copy Knative IP Address to clipboard" +msgid "ClusterIntegration|Copy Knative Endpoint to clipboard" msgstr "" msgid "ClusterIntegration|Copy Kubernetes cluster name" @@ -1806,7 +1806,7 @@ msgstr "" msgid "ClusterIntegration|Knative Domain Name:" msgstr "" -msgid "ClusterIntegration|Knative IP Address:" +msgid "ClusterIntegration|Knative Endpoint:" msgstr "" msgid "ClusterIntegration|Knative extends Kubernetes to provide a set of middleware components that are essential to build modern, source-centric, and container-based applications that can run anywhere: on premises, in the cloud, or even in a third-party data center." @@ -1926,7 +1926,7 @@ msgstr "" msgid "ClusterIntegration|Request to begin installing failed" msgstr "" -msgid "ClusterIntegration|Retry upgrade" +msgid "ClusterIntegration|Retry update" msgstr "" msgid "ClusterIntegration|Save changes" @@ -1971,9 +1971,6 @@ msgstr "" msgid "ClusterIntegration|Something went wrong on our end." msgstr "" -msgid "ClusterIntegration|Something went wrong when upgrading %{title}. Please check the logs and try again." -msgstr "" - msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine" msgstr "" @@ -1992,12 +1989,21 @@ msgstr "" msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters." msgstr "" +msgid "ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint." +msgstr "" + msgid "ClusterIntegration|Toggle Kubernetes cluster" msgstr "" msgid "ClusterIntegration|Token" msgstr "" +msgid "ClusterIntegration|Update failed. Please check the logs and try again." +msgstr "" + +msgid "ClusterIntegration|Updating" +msgstr "" + msgid "ClusterIntegration|Upgrade" msgstr "" diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js index 8cb9713964e..a2dd4e93daf 100644 --- a/spec/javascripts/clusters/components/application_row_spec.js +++ b/spec/javascripts/clusters/components/application_row_spec.js @@ -230,7 +230,7 @@ describe('Application Row', () => { expect(upgradeBtn.innerHTML).toContain('Upgrade'); }); - it('has enabled "Retry upgrade" when APPLICATION_STATUS.UPDATE_ERRORED', () => { + it('has enabled "Retry update" when APPLICATION_STATUS.UPDATE_ERRORED', () => { vm = mountComponent(ApplicationRow, { ...DEFAULT_APPLICATION_STATE, status: APPLICATION_STATUS.UPDATE_ERRORED, @@ -239,10 +239,10 @@ describe('Application Row', () => { expect(upgradeBtn).not.toBe(null); expect(vm.upgradeFailed).toBe(true); - expect(upgradeBtn.innerHTML).toContain('Retry upgrade'); + expect(upgradeBtn.innerHTML).toContain('Retry update'); }); - it('has disabled "Retry upgrade" when APPLICATION_STATUS.UPDATING', () => { + it('has disabled "Updating" when APPLICATION_STATUS.UPDATING', () => { vm = mountComponent(ApplicationRow, { ...DEFAULT_APPLICATION_STATE, status: APPLICATION_STATUS.UPDATING, @@ -251,7 +251,7 @@ describe('Application Row', () => { expect(upgradeBtn).not.toBe(null); expect(vm.isUpgrading).toBe(true); - expect(upgradeBtn.innerHTML).toContain('Upgrading'); + expect(upgradeBtn.innerHTML).toContain('Updating'); }); it('clicking upgrade button emits event', () => { @@ -295,7 +295,7 @@ describe('Application Row', () => { expect(failureMessage).not.toBe(null); expect(failureMessage.innerHTML).toContain( - 'Something went wrong when upgrading GitLab Runner. Please check the logs and try again.', + 'Update failed. Please check the logs and try again.', ); }); }); diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index 14ef1193984..8daf0282184 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -1,7 +1,9 @@ import Vue from 'vue'; import applications from '~/clusters/components/applications.vue'; import { CLUSTER_TYPE } from '~/clusters/constants'; +import eventHub from '~/clusters/event_hub'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import { APPLICATIONS_MOCK_STATE } from '../services/mock_data'; describe('Applications', () => { let vm; @@ -18,16 +20,8 @@ describe('Applications', () => { describe('Project cluster applications', () => { beforeEach(() => { vm = mountComponent(Applications, { + applications: APPLICATIONS_MOCK_STATE, type: CLUSTER_TYPE.PROJECT, - applications: { - helm: { title: 'Helm Tiller' }, - ingress: { title: 'Ingress' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub' }, - knative: { title: 'Knative' }, - }, }); }); @@ -64,15 +58,7 @@ describe('Applications', () => { beforeEach(() => { vm = mountComponent(Applications, { type: CLUSTER_TYPE.GROUP, - applications: { - helm: { title: 'Helm Tiller' }, - ingress: { title: 'Ingress' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub' }, - knative: { title: 'Knative' }, - }, + applications: APPLICATIONS_MOCK_STATE, }); }); @@ -111,17 +97,12 @@ describe('Applications', () => { it('renders ip address with a clipboard button', () => { vm = mountComponent(Applications, { applications: { + ...APPLICATIONS_MOCK_STATE, ingress: { title: 'Ingress', status: 'installed', externalIp: '0.0.0.0', }, - helm: { title: 'Helm Tiller' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', hostname: '' }, - knative: { title: 'Knative', hostname: '' }, }, }); @@ -137,16 +118,11 @@ describe('Applications', () => { it('renders an input text with a question mark and an alert text', () => { vm = mountComponent(Applications, { applications: { + ...APPLICATIONS_MOCK_STATE, ingress: { title: 'Ingress', status: 'installed', }, - helm: { title: 'Helm Tiller' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', hostname: '' }, - knative: { title: 'Knative', hostname: '' }, }, }); @@ -160,15 +136,7 @@ describe('Applications', () => { describe('before installing', () => { it('does not render the IP address', () => { vm = mountComponent(Applications, { - applications: { - helm: { title: 'Helm Tiller' }, - ingress: { title: 'Ingress' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', hostname: '' }, - knative: { title: 'Knative', hostname: '' }, - }, + applications: APPLICATIONS_MOCK_STATE, }); expect(vm.$el.textContent).not.toContain('Ingress IP Address'); @@ -181,17 +149,12 @@ describe('Applications', () => { 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' }, + ...APPLICATIONS_MOCK_STATE, 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' }, }, }); @@ -204,17 +167,12 @@ describe('Applications', () => { 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' }, + ...APPLICATIONS_MOCK_STATE, 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' }, }, }); @@ -229,13 +187,12 @@ describe('Applications', () => { it('renders hostname active input', () => { 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' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' }, - knative: { title: 'Knative', hostname: '', status: 'installable' }, + ...APPLICATIONS_MOCK_STATE, + ingress: { + title: 'Ingress', + status: 'installed', + externalIp: '1.1.1.1', + }, }, }); @@ -247,13 +204,8 @@ describe('Applications', () => { it('does not render hostname input', () => { vm = mountComponent(Applications, { applications: { - helm: { title: 'Helm Tiller', status: 'installed' }, + ...APPLICATIONS_MOCK_STATE, ingress: { title: 'Ingress', status: 'installed' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' }, - knative: { title: 'Knative', hostname: '', status: 'installable' }, }, }); @@ -265,13 +217,9 @@ describe('Applications', () => { it('renders readonly input', () => { vm = mountComponent(Applications, { applications: { - helm: { title: 'Helm Tiller', status: 'installed' }, + ...APPLICATIONS_MOCK_STATE, ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' }, - knative: { title: 'Knative', status: 'installed', hostname: '' }, }, }); @@ -282,15 +230,7 @@ describe('Applications', () => { describe('without ingress installed', () => { beforeEach(() => { vm = mountComponent(Applications, { - applications: { - helm: { title: 'Helm Tiller' }, - ingress: { title: 'Ingress' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', status: 'not_installable' }, - knative: { title: 'Knative' }, - }, + applications: APPLICATIONS_MOCK_STATE, }); }); @@ -310,4 +250,77 @@ describe('Applications', () => { }); }); }); + + describe('Knative application', () => { + describe('when installed', () => { + describe('with ip address', () => { + const props = { + applications: { + ...APPLICATIONS_MOCK_STATE, + knative: { + title: 'Knative', + hostname: 'example.com', + status: 'installed', + externalIp: '1.1.1.1', + }, + }, + }; + it('renders ip address with a clipboard button', () => { + vm = mountComponent(Applications, props); + + expect(vm.$el.querySelector('.js-knative-ip-address').value).toEqual('1.1.1.1'); + + expect( + vm.$el + .querySelector('.js-knative-ip-clipboard-btn') + .getAttribute('data-clipboard-text'), + ).toEqual('1.1.1.1'); + }); + + it('renders domain & allows editing', () => { + expect(vm.$el.querySelector('.js-knative-domainname').value).toEqual('example.com'); + expect(vm.$el.querySelector('.js-knative-domainname').getAttribute('readonly')).toBe( + null, + ); + }); + + it('renders an update/save Knative domain button', () => { + expect(vm.$el.querySelector('.js-knative-save-domain-button')).not.toBe(null); + }); + + it('emits event when clicking Save changes button', () => { + spyOn(eventHub, '$emit'); + vm = mountComponent(Applications, props); + + const saveButton = vm.$el.querySelector('.js-knative-save-domain-button'); + + saveButton.click(); + + expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', { + id: 'knative', + params: { hostname: 'example.com' }, + }); + }); + }); + + describe('without ip address', () => { + it('renders an input text with a question mark and an alert text', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + knative: { + title: 'Knative', + hostname: 'example.com', + status: 'installed', + }, + }, + }); + + expect(vm.$el.querySelector('.js-knative-ip-address').value).toEqual('?'); + + expect(vm.$el.querySelector('.js-no-knative-ip-message')).not.toBe(null); + }); + }); + }); + }); }); diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js index 3c3d9977ffb..3ace19c6401 100644 --- a/spec/javascripts/clusters/services/mock_data.js +++ b/spec/javascripts/clusters/services/mock_data.js @@ -115,4 +115,14 @@ const DEFAULT_APPLICATION_STATE = { requestReason: null, }; -export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE }; +const APPLICATIONS_MOCK_STATE = { + helm: { title: 'Helm Tiller', status: 'installable' }, + ingress: { title: 'Ingress', status: 'installable' }, + cert_manager: { title: 'Cert-Manager', status: 'installable' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', status: 'installable', hostname: '' }, + knative: { title: 'Knative ', status: 'installable', hostname: '' }, +}; + +export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE, APPLICATIONS_MOCK_STATE }; diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index 37a4d6614f6..09bcdf91d91 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -111,6 +111,7 @@ describe('Clusters Store', () => { requestStatus: null, requestReason: null, hostname: null, + isEditingHostName: false, externalIp: null, }, cert_manager: { |