diff options
Diffstat (limited to 'app/assets/javascripts/clusters')
12 files changed, 5 insertions, 2424 deletions
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index 762b37a8216..c2c035963f4 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -1,18 +1,14 @@ import { GlToast } from '@gitlab/ui'; import Visibility from 'visibilityjs'; import Vue from 'vue'; +import createFlash from '~/flash'; import AccessorUtilities from '~/lib/utils/accessor'; import initProjectSelectDropdown from '~/project_select'; -import initServerlessSurveyBanner from '~/serverless/survey_banner'; -import { deprecatedCreateFlash as Flash } from '../flash'; import Poll from '../lib/utils/poll'; -import { s__, sprintf } from '../locale'; +import { s__ } from '../locale'; import PersistentUserCallout from '../persistent_user_callout'; import initSettingsPanels from '../settings_panels'; -import Applications from './components/applications.vue'; import RemoveClusterConfirmation from './components/remove_cluster_confirmation.vue'; -import { APPLICATION_STATUS, CROSSPLANE, KNATIVE } from './constants'; -import eventHub from './event_hub'; import ClustersService from './services/clusters_service'; import ClustersStore from './stores/clusters_store'; @@ -20,46 +16,20 @@ const Environments = () => import('ee_component/clusters/components/environments Vue.use(GlToast); -/** - * Cluster page has 2 separate parts: - * Toggle button and applications section - * - * - Polling status while creating or scheduled - * - Update status area with the response result - */ - export default class Clusters { constructor() { const { statusPath, - installHelmPath, - installIngressPath, - installCertManagerPath, - installRunnerPath, - installJupyterPath, - installKnativePath, - updateKnativePath, - installElasticStackPath, - installCrossplanePath, - installPrometheusPath, - managePrometheusPath, clusterEnvironmentsPath, hasRbac, providerType, - preInstalledKnative, - clusterType, clusterStatus, clusterStatusReason, helpPath, - helmHelpPath, - ingressHelpPath, - ingressDnsHelpPath, environmentsHelpPath, clustersHelpPath, deployBoardsHelpPath, - cloudRunHelpPath, clusterId, - ciliumHelpPath, } = document.querySelector('.js-edit-cluster-form').dataset; this.clusterId = clusterId; @@ -69,38 +39,19 @@ export default class Clusters { this.store = new ClustersStore(); this.store.setHelpPaths({ helpPath, - helmHelpPath, - ingressHelpPath, - ingressDnsHelpPath, environmentsHelpPath, clustersHelpPath, deployBoardsHelpPath, - cloudRunHelpPath, - ciliumHelpPath, }); - this.store.setManagePrometheusPath(managePrometheusPath); this.store.updateStatus(clusterStatus); this.store.updateStatusReason(clusterStatusReason); this.store.updateProviderType(providerType); - this.store.updatePreInstalledKnative(preInstalledKnative); this.store.updateRbac(hasRbac); this.service = new ClustersService({ endpoint: statusPath, - installHelmEndpoint: installHelmPath, - installIngressEndpoint: installIngressPath, - installCertManagerEndpoint: installCertManagerPath, - installCrossplaneEndpoint: installCrossplanePath, - installRunnerEndpoint: installRunnerPath, - installPrometheusEndpoint: installPrometheusPath, - installJupyterEndpoint: installJupyterPath, - installKnativeEndpoint: installKnativePath, - updateKnativeEndpoint: updateKnativePath, - installElasticStackEndpoint: installElasticStackPath, clusterEnvironmentsEndpoint: clusterEnvironmentsPath, }); - this.installApplication = this.installApplication.bind(this); - this.errorContainer = document.querySelector('.js-cluster-error'); this.successContainer = document.querySelector('.js-cluster-success'); this.creatingContainer = document.querySelector('.js-cluster-creating'); @@ -109,14 +60,12 @@ export default class Clusters { '.js-cluster-authentication-failure', ); this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason'); - this.successApplicationContainer = document.querySelector('.js-cluster-application-notice'); this.tokenField = document.querySelector('.js-cluster-token'); initProjectSelectDropdown(); Clusters.initDismissableCallout(); initSettingsPanels(); - this.initApplications(clusterType); this.initEnvironments(); if (clusterEnvironmentsPath && this.environments) { @@ -143,38 +92,6 @@ export default class Clusters { this.initRemoveClusterActions(); } - initApplications(type) { - const { store } = this; - const el = document.querySelector('#js-cluster-applications'); - - this.applications = new Vue({ - el, - data() { - return { - state: store.state, - }; - }, - render(createElement) { - return createElement(Applications, { - props: { - type, - applications: this.state.applications, - helpPath: this.state.helpPath, - helmHelpPath: this.state.helmHelpPath, - ingressHelpPath: this.state.ingressHelpPath, - managePrometheusPath: this.state.managePrometheusPath, - ingressDnsHelpPath: this.state.ingressDnsHelpPath, - cloudRunHelpPath: this.state.cloudRunHelpPath, - providerType: this.state.providerType, - preInstalledKnative: this.state.preInstalledKnative, - rbac: this.state.rbac, - ciliumHelpPath: this.state.ciliumHelpPath, - }, - }); - }, - }); - } - initEnvironments() { const { store } = this; const el = document.querySelector('#js-cluster-environments'); @@ -242,30 +159,11 @@ export default class Clusters { } addListeners() { - eventHub.$on('installApplication', this.installApplication); - eventHub.$on('updateApplication', (data) => this.updateApplication(data)); - eventHub.$on('saveKnativeDomain', (data) => this.saveKnativeDomain(data)); - eventHub.$on('setKnativeDomain', (data) => this.setKnativeDomain(data)); - eventHub.$on('uninstallApplication', (data) => this.uninstallApplication(data)); - eventHub.$on('setCrossplaneProviderStack', (data) => this.setCrossplaneProviderStack(data)); // Add event listener to all the banner close buttons this.addBannerCloseHandler(this.unreachableContainer, 'unreachable'); this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure'); } - removeListeners() { - eventHub.$off('installApplication', this.installApplication); - eventHub.$off('updateApplication', this.updateApplication); - // eslint-disable-next-line @gitlab/no-global-event-off - eventHub.$off('saveKnativeDomain'); - // eslint-disable-next-line @gitlab/no-global-event-off - eventHub.$off('setKnativeDomain'); - // eslint-disable-next-line @gitlab/no-global-event-off - eventHub.$off('setCrossplaneProviderStack'); - // eslint-disable-next-line @gitlab/no-global-event-off - eventHub.$off('uninstallApplication'); - } - initPolling(method, successCallback, errorCallback) { this.poll = new Poll({ resource: this.service, @@ -298,21 +196,17 @@ export default class Clusters { } static handleError() { - Flash(s__('ClusterIntegration|Something went wrong on our end.')); + createFlash({ + message: s__('ClusterIntegration|Something went wrong on our end.'), + }); } handleClusterStatusSuccess(data) { const prevStatus = this.store.state.status; - const prevApplicationMap = { ...this.store.state.applications }; this.store.updateStateFromServer(data.data); - this.checkForNewInstalls(prevApplicationMap, this.store.state.applications); this.updateContainer(prevStatus, this.store.state.status, this.store.state.statusReason); - - if (this.store.state.applications[KNATIVE]?.status === APPLICATION_STATUS.INSTALLED) { - initServerlessSurveyBanner(); - } } hideAll() { @@ -323,27 +217,6 @@ export default class Clusters { this.authenticationFailureContainer.classList.add('hidden'); } - checkForNewInstalls(prevApplicationMap, newApplicationMap) { - const appTitles = Object.keys(newApplicationMap) - .filter( - (appId) => - newApplicationMap[appId].status === APPLICATION_STATUS.INSTALLED && - prevApplicationMap[appId].status !== APPLICATION_STATUS.INSTALLED && - prevApplicationMap[appId].status !== null, - ) - .map((appId) => newApplicationMap[appId].title); - - if (appTitles.length > 0) { - const text = sprintf( - s__('ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster'), - { - appList: appTitles.join(', '), - }, - ); - Flash(text, 'notice', this.successApplicationContainer); - } - } - setBannerDismissedState(status, isDismissed) { if (AccessorUtilities.isLocalStorageAccessSafe()) { window.localStorage.setItem(this.clusterBannerDismissedKey, `${status}_${isDismissed}`); @@ -416,91 +289,9 @@ export default class Clusters { } } - installApplication({ id: appId, params }) { - return Clusters.validateInstallation(appId, params) - .then(() => { - this.store.updateAppProperty(appId, 'requestReason', null); - this.store.updateAppProperty(appId, 'statusReason', null); - this.store.installApplication(appId); - - // eslint-disable-next-line promise/no-nesting - this.service.installApplication(appId, params).catch(() => { - this.store.notifyInstallFailure(appId); - this.store.updateAppProperty( - appId, - 'requestReason', - s__('ClusterIntegration|Request to begin installing failed'), - ); - }); - }) - .catch((error) => this.store.updateAppProperty(appId, 'validationError', error)); - } - - static validateInstallation(appId, params) { - return new Promise((resolve, reject) => { - if (appId === CROSSPLANE && !params.stack) { - reject(s__('ClusterIntegration|Select a stack to install Crossplane.')); - return; - } - - if (appId === KNATIVE && !params.hostname && !params.pages_domain_id) { - reject(s__('ClusterIntegration|You must specify a domain before you can install Knative.')); - return; - } - - resolve(); - }); - } - - uninstallApplication({ id: appId }) { - this.store.updateAppProperty(appId, 'requestReason', null); - this.store.updateAppProperty(appId, 'statusReason', null); - - this.store.uninstallApplication(appId); - - return this.service.uninstallApplication(appId).catch(() => { - this.store.notifyUninstallFailure(appId); - this.store.updateAppProperty( - appId, - 'requestReason', - s__('ClusterIntegration|Request to begin uninstalling failed'), - ); - }); - } - - updateApplication({ id: appId, params }) { - this.store.updateApplication(appId); - this.service.installApplication(appId, params).catch(() => { - this.store.notifyUpdateFailure(appId); - }); - } - - saveKnativeDomain(data) { - const appId = data.id; - this.store.updateApplication(appId); - this.service.updateApplication(appId, data.params).catch(() => { - this.store.notifyUpdateFailure(appId); - }); - } - - setKnativeDomain({ id: appId, domain, domainId }) { - this.store.updateAppProperty(appId, 'isEditingDomain', true); - this.store.updateAppProperty(appId, 'hostname', domain); - this.store.updateAppProperty(appId, 'pagesDomain', domainId ? { id: domainId, domain } : null); - this.store.updateAppProperty(appId, 'validationError', null); - } - - setCrossplaneProviderStack(data) { - const appId = data.id; - this.store.updateAppProperty(appId, 'stack', data.stack.code); - this.store.updateAppProperty(appId, 'validationError', null); - } - destroy() { this.destroyed = true; - this.removeListeners(); - if (this.poll) { this.poll.stop(); } @@ -508,7 +299,5 @@ export default class Clusters { if (this.environments) { this.environments.$destroy(); } - - this.applications.$destroy(); } } diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue deleted file mode 100644 index a53b63ea592..00000000000 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ /dev/null @@ -1,478 +0,0 @@ -<script> -import { GlLink, GlModalDirective, GlSprintf, GlButton, GlAlert } from '@gitlab/ui'; -import { s__, __, sprintf } from '~/locale'; -import identicon from '../../vue_shared/components/identicon.vue'; -import { APPLICATION_STATUS, ELASTIC_STACK } from '../constants'; -import eventHub from '../event_hub'; -import UninstallApplicationButton from './uninstall_application_button.vue'; -import UninstallApplicationConfirmationModal from './uninstall_application_confirmation_modal.vue'; -import UpdateApplicationConfirmationModal from './update_application_confirmation_modal.vue'; - -export default { - components: { - GlButton, - identicon, - GlLink, - GlAlert, - GlSprintf, - UninstallApplicationButton, - UninstallApplicationConfirmationModal, - UpdateApplicationConfirmationModal, - }, - directives: { - GlModalDirective, - }, - props: { - id: { - type: String, - required: true, - }, - title: { - type: String, - required: true, - }, - titleLink: { - type: String, - required: false, - default: '', - }, - manageLink: { - type: String, - required: false, - default: '', - }, - logoUrl: { - type: String, - required: false, - default: '', - }, - disabled: { - type: Boolean, - required: false, - default: false, - }, - installable: { - type: Boolean, - required: false, - default: true, - }, - uninstallable: { - type: Boolean, - required: false, - default: false, - }, - status: { - type: String, - required: false, - default: '', - }, - statusReason: { - type: String, - required: false, - default: '', - }, - requestReason: { - type: String, - required: false, - default: '', - }, - installed: { - type: Boolean, - required: false, - default: false, - }, - installFailed: { - type: Boolean, - required: false, - default: false, - }, - version: { - type: String, - required: false, - default: '', - }, - chartRepo: { - type: String, - required: false, - default: '', - }, - updateAvailable: { - type: Boolean, - required: false, - }, - updateable: { - type: Boolean, - default: true, - required: false, - }, - updateSuccessful: { - type: Boolean, - required: false, - default: false, - }, - updateFailed: { - type: Boolean, - required: false, - default: false, - }, - uninstallFailed: { - type: Boolean, - required: false, - default: false, - }, - uninstallSuccessful: { - type: Boolean, - required: false, - default: false, - }, - installApplicationRequestParams: { - type: Object, - required: false, - default: () => ({}), - }, - }, - computed: { - isUnknownStatus() { - return !this.isKnownStatus && this.status !== null; - }, - isKnownStatus() { - return Object.values(APPLICATION_STATUS).includes(this.status); - }, - isInstalling() { - return this.status === APPLICATION_STATUS.INSTALLING; - }, - isExternallyInstalled() { - return this.status === APPLICATION_STATUS.EXTERNALLY_INSTALLED; - }, - canInstall() { - return ( - this.status === APPLICATION_STATUS.NOT_INSTALLABLE || - this.status === APPLICATION_STATUS.INSTALLABLE || - this.status === APPLICATION_STATUS.UNINSTALLED || - this.isUnknownStatus - ); - }, - hasLogo() { - return Boolean(this.logoUrl); - }, - identiconId() { - // generate a deterministic integer id for the identicon background - return this.id.charCodeAt(0); - }, - rowJsClass() { - return `js-cluster-application-row-${this.id}`; - }, - displayUninstallButton() { - return this.installed && this.uninstallable; - }, - displayInstallButton() { - return !this.installed || !this.uninstallable; - }, - installButtonLoading() { - return !this.status || this.isInstalling; - }, - installButtonDisabled() { - // Applications installed through the management project can - // only be installed through the CI pipeline. Installation should - // be disable in all states. - if (!this.installable) return true; - - // Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but - // we already made a request to install and are just waiting for the real-time - // to sync up. - if (this.isInstalling) return true; - - if (!this.isKnownStatus) return false; - - return ( - this.status !== APPLICATION_STATUS.INSTALLABLE && this.status !== APPLICATION_STATUS.ERROR - ); - }, - installButtonLabel() { - let label; - if (this.canInstall) { - label = __('Install'); - } else if (this.isInstalling) { - label = __('Installing'); - } else if (this.installed) { - label = __('Installed'); - } else if (this.isExternallyInstalled) { - label = __('Externally installed'); - } - - return label; - }, - buttonGridCellClass() { - return this.showManageButton || this.status === APPLICATION_STATUS.EXTERNALLY_INSTALLED - ? 'section-25' - : 'section-15'; - }, - showManageButton() { - return this.manageLink && this.status === APPLICATION_STATUS.INSTALLED; - }, - manageButtonLabel() { - return __('Manage'); - }, - hasError() { - return this.installFailed || this.uninstallFailed; - }, - generalErrorDescription() { - let errorDescription; - - if (this.installFailed) { - errorDescription = s__('ClusterIntegration|Something went wrong while installing %{title}'); - } else if (this.uninstallFailed) { - errorDescription = s__( - 'ClusterIntegration|Something went wrong while uninstalling %{title}', - ); - } - - return sprintf(errorDescription, { title: this.title }); - }, - updateFailureDescription() { - return s__('ClusterIntegration|Update failed. Please check the logs and try again.'); - }, - updateSuccessDescription() { - return sprintf(s__('ClusterIntegration|%{title} updated successfully.'), { - title: this.title, - }); - }, - updateButtonLabel() { - let label; - if (this.updateAvailable && !this.updateFailed && !this.isUpdating) { - label = __('Update'); - } else if (this.isUpdating) { - label = __('Updating'); - } else if (this.updateFailed) { - label = __('Retry update'); - } - - return label; - }, - updatingNeedsConfirmation() { - if (this.version) { - const majorVersion = parseInt(this.version.split('.')[0], 10); - - if (!Number.isNaN(majorVersion)) { - return this.id === ELASTIC_STACK && majorVersion < 3; - } - } - - return false; - }, - isUpdating() { - // Since upgrading is handled asynchronously on the backend we need this check to prevent any delay on the frontend - return this.status === APPLICATION_STATUS.UPDATING; - }, - shouldShowUpdateDetails() { - // This method only returns true when; - // Update was successful OR Update failed - // AND new update is unavailable AND version information is present. - return (this.updateSuccessful || this.updateFailed) && !this.updateAvailable && this.version; - }, - uninstallSuccessDescription() { - return sprintf(s__('ClusterIntegration|%{title} uninstalled successfully.'), { - title: this.title, - }); - }, - updateModalId() { - return `update-${this.id}`; - }, - uninstallModalId() { - return `uninstall-${this.id}`; - }, - }, - watch: { - updateSuccessful(updateSuccessful) { - if (updateSuccessful) { - this.$toast.show(this.updateSuccessDescription); - } - }, - uninstallSuccessful(uninstallSuccessful) { - if (uninstallSuccessful) { - this.$toast.show(this.uninstallSuccessDescription); - } - }, - }, - methods: { - installClicked() { - if (this.disabled || this.installButtonDisabled) return; - - eventHub.$emit('installApplication', { - id: this.id, - params: this.installApplicationRequestParams, - }); - }, - updateConfirmed() { - if (this.isUpdating) return; - - eventHub.$emit('updateApplication', { - id: this.id, - params: this.installApplicationRequestParams, - }); - }, - uninstallConfirmed() { - eventHub.$emit('uninstallApplication', { - id: this.id, - }); - }, - }, -}; -</script> - -<template> - <div - :class="[ - rowJsClass, - installed && 'cluster-application-installed', - disabled && 'cluster-application-disabled', - ]" - class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span" - :data-qa-selector="id" - > - <div class="gl-responsive-table-row-layout" role="row"> - <div class="table-section gl-mr-3 section-align-top" role="gridcell"> - <img - v-if="hasLogo" - :src="logoUrl" - :alt="`${title} logo`" - class="cluster-application-logo avatar s40" - /> - <identicon v-else :entity-id="identiconId" :entity-name="title" size-class="s40" /> - </div> - <div class="table-section cluster-application-description section-wrap" role="gridcell"> - <strong> - <a - v-if="titleLink" - :href="titleLink" - target="_blank" - rel="noopener noreferrer" - class="js-cluster-application-title" - >{{ title }}</a - > - <span v-else class="js-cluster-application-title">{{ title }}</span> - </strong> - <slot name="installed-via"></slot> - <div> - <slot name="description"></slot> - </div> - <div v-if="hasError" class="cluster-application-error text-danger gl-mt-3"> - <p class="js-cluster-application-general-error-message gl-mb-0"> - {{ generalErrorDescription }} - </p> - <ul v-if="statusReason || requestReason"> - <li v-if="statusReason" class="js-cluster-application-status-error-message"> - {{ statusReason }} - </li> - <li v-if="requestReason" class="js-cluster-application-request-error-message"> - {{ requestReason }} - </li> - </ul> - </div> - - <div v-if="updateable"> - <div - v-if="shouldShowUpdateDetails" - class="form-text text-muted label p-0 js-cluster-application-update-details" - > - <template v-if="updateFailed">{{ __('Update failed') }}</template> - <template v-else-if="isUpdating">{{ __('Updating') }}</template> - <template v-else> - <gl-sprintf :message="__('Updated to %{linkStart}chart v%{linkEnd}')"> - <template #link="{ content }"> - <gl-link - :href="chartRepo" - target="_blank" - class="js-cluster-application-update-version" - >{{ content }}{{ version }}</gl-link - > - </template> - </gl-sprintf> - </template> - </div> - - <gl-alert - v-if="updateFailed && !isUpdating" - variant="danger" - :dismissible="false" - class="gl-mt-3 gl-mb-0 js-cluster-application-update-details" - > - {{ updateFailureDescription }} - </gl-alert> - <template v-if="updateAvailable || updateFailed || isUpdating"> - <template v-if="updatingNeedsConfirmation"> - <gl-button - v-gl-modal-directive="updateModalId" - class="js-cluster-application-update-button mt-2" - variant="info" - category="primary" - :loading="isUpdating" - :disabled="isUpdating" - data-qa-selector="update_button_with_confirmation" - :data-qa-application="id" - > - {{ updateButtonLabel }} - </gl-button> - <update-application-confirmation-modal - :application="id" - :application-title="title" - @confirm="updateConfirmed()" - /> - </template> - - <gl-button - v-else - class="js-cluster-application-update-button mt-2" - variant="info" - category="primary" - :loading="isUpdating" - :disabled="isUpdating" - data-qa-selector="update_button" - :data-qa-application="id" - @click="updateConfirmed" - > - {{ updateButtonLabel }} - </gl-button> - </template> - </div> - </div> - <div - :class="[buttonGridCellClass, 'table-section', 'table-button-footer', 'section-align-top']" - role="gridcell" - > - <div v-if="showManageButton" class="btn-group table-action-buttons"> - <a :href="manageLink" :class="{ disabled: disabled }" class="btn">{{ - manageButtonLabel - }}</a> - </div> - <div class="btn-group table-action-buttons"> - <gl-button - v-if="displayInstallButton" - :loading="installButtonLoading" - :disabled="disabled || installButtonDisabled" - class="js-cluster-application-install-button" - variant="default" - data-qa-selector="install_button" - :data-qa-application="id" - @click="installClicked" - > - {{ installButtonLabel }} - </gl-button> - <uninstall-application-button - v-if="displayUninstallButton" - v-gl-modal-directive="uninstallModalId" - :status="status" - data-qa-selector="uninstall_button" - :data-qa-application="id" - class="js-cluster-application-uninstall-button" - /> - <uninstall-application-confirmation-modal - :application="id" - :application-title="title" - @confirm="uninstallConfirmed()" - /> - </div> - </div> - </div> - </div> -</template> diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue deleted file mode 100644 index ddee1711975..00000000000 --- a/app/assets/javascripts/clusters/components/applications.vue +++ /dev/null @@ -1,662 +0,0 @@ -<script> -import { GlLoadingIcon, GlSprintf, GlLink, GlAlert } from '@gitlab/ui'; -import certManagerLogo from 'images/cluster_app_logos/cert_manager.png'; -import crossplaneLogo from 'images/cluster_app_logos/crossplane.png'; -import elasticStackLogo from 'images/cluster_app_logos/elastic_stack.png'; -import gitlabLogo from 'images/cluster_app_logos/gitlab.png'; -import helmLogo from 'images/cluster_app_logos/helm.png'; -import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png'; -import knativeLogo from 'images/cluster_app_logos/knative.png'; -import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png'; -import prometheusLogo from 'images/cluster_app_logos/prometheus.png'; -import eventHub from '~/clusters/event_hub'; -import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; -import { CLUSTER_TYPE, PROVIDER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants'; -import applicationRow from './application_row.vue'; -import CrossplaneProviderStack from './crossplane_provider_stack.vue'; -import KnativeDomainEditor from './knative_domain_editor.vue'; - -export default { - components: { - applicationRow, - clipboardButton, - GlLoadingIcon, - GlSprintf, - GlLink, - KnativeDomainEditor, - CrossplaneProviderStack, - GlAlert, - }, - props: { - type: { - type: String, - required: false, - default: CLUSTER_TYPE.PROJECT, - }, - applications: { - type: Object, - required: false, - default: () => ({}), - }, - helpPath: { - type: String, - required: false, - default: '', - }, - helmHelpPath: { - type: String, - required: false, - default: '', - }, - ingressHelpPath: { - type: String, - required: false, - default: '', - }, - ingressDnsHelpPath: { - type: String, - required: false, - default: '', - }, - - cloudRunHelpPath: { - type: String, - required: false, - default: '', - }, - managePrometheusPath: { - type: String, - required: false, - default: '', - }, - providerType: { - type: String, - required: false, - default: '', - }, - preInstalledKnative: { - type: Boolean, - required: false, - default: false, - }, - rbac: { - type: Boolean, - required: false, - default: false, - }, - ciliumHelpPath: { - type: String, - required: false, - default: '', - }, - }, - computed: { - ingressId() { - return INGRESS; - }, - ingressInstalled() { - return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED; - }, - ingressExternalEndpoint() { - return this.applications.ingress.externalIp || this.applications.ingress.externalHostname; - }, - certManagerInstalled() { - return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED; - }, - jupyterInstalled() { - return this.applications.jupyter.status === APPLICATION_STATUS.INSTALLED; - }, - jupyterHostname() { - return this.applications.jupyter.hostname; - }, - knative() { - return this.applications.knative; - }, - crossplane() { - return this.applications.crossplane; - }, - cloudRun() { - return this.providerType === PROVIDER_TYPE.GCP && this.preInstalledKnative; - }, - ingress() { - return this.applications.ingress; - }, - }, - methods: { - saveKnativeDomain() { - eventHub.$emit('saveKnativeDomain', { - id: 'knative', - params: { - hostname: this.applications.knative.hostname, - pages_domain_id: this.applications.knative.pagesDomain?.id, - }, - }); - }, - setKnativeDomain({ domainId, domain }) { - eventHub.$emit('setKnativeDomain', { - id: 'knative', - domainId, - domain, - }); - }, - setCrossplaneProviderStack(stack) { - eventHub.$emit('setCrossplaneProviderStack', { - id: 'crossplane', - stack, - }); - }, - }, - logos: { - gitlabLogo, - helmLogo, - jupyterhubLogo, - kubernetesLogo, - certManagerLogo, - crossplaneLogo, - knativeLogo, - prometheusLogo, - elasticStackLogo, - }, -}; -</script> - -<template> - <section id="cluster-applications"> - <p class="gl-mb-0"> - {{ - s__(`ClusterIntegration|Choose which applications to install on your Kubernetes cluster.`) - }} - <gl-link :href="helpPath">{{ __('More information') }}</gl-link> - </p> - - <div class="cluster-application-list gl-mt-3"> - <application-row - v-if="applications.helm.installed || applications.helm.uninstalling" - id="helm" - :logo-url="$options.logos.helmLogo" - :title="applications.helm.title" - :status="applications.helm.status" - :status-reason="applications.helm.statusReason" - :request-status="applications.helm.requestStatus" - :request-reason="applications.helm.requestReason" - :installed="applications.helm.installed" - :install-failed="applications.helm.installFailed" - :uninstallable="applications.helm.uninstallable" - :uninstall-successful="applications.helm.uninstallSuccessful" - :uninstall-failed="applications.helm.uninstallFailed" - title-link="https://v2.helm.sh/" - > - <template #description> - <p> - {{ - s__(`ClusterIntegration|Can be safely removed. Prior to GitLab - 13.2, GitLab used a remote Tiller server to manage the - applications. GitLab no longer uses this server. - Uninstalling this server will not affect your other - applications. This row will disappear afterwards.`) - }} - <gl-link :href="helmHelpPath">{{ __('More information') }}</gl-link> - </p> - </template> - </application-row> - <application-row - :id="ingressId" - :logo-url="$options.logos.kubernetesLogo" - :title="applications.ingress.title" - :status="applications.ingress.status" - :status-reason="applications.ingress.statusReason" - :request-status="applications.ingress.requestStatus" - :request-reason="applications.ingress.requestReason" - :installed="applications.ingress.installed" - :install-failed="applications.ingress.installFailed" - :uninstallable="applications.ingress.uninstallable" - :uninstall-successful="applications.ingress.uninstallSuccessful" - :uninstall-failed="applications.ingress.uninstallFailed" - :updateable="false" - title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/" - > - <template #description> - <p> - {{ - s__(`ClusterIntegration|Ingress gives you a way to route - requests to services based on the request host or path, - centralizing a number of services into a single entrypoint.`) - }} - </p> - - <template v-if="ingressInstalled"> - <div class="form-group"> - <label for="ingress-endpoint">{{ s__('ClusterIntegration|Ingress Endpoint') }}</label> - <div class="input-group"> - <template v-if="ingressExternalEndpoint"> - <input - id="ingress-endpoint" - :value="ingressExternalEndpoint" - type="text" - class="form-control js-endpoint" - readonly - /> - <span class="input-group-append"> - <clipboard-button - :text="ingressExternalEndpoint" - :title="s__('ClusterIntegration|Copy Ingress Endpoint')" - class="input-group-text js-clipboard-btn" - /> - </span> - </template> - <template v-else> - <input type="text" class="form-control js-endpoint" readonly /> - <gl-loading-icon - class="position-absolute align-self-center ml-2 js-ingress-ip-loading-icon" - /> - </template> - </div> - <p class="form-text text-muted"> - {{ - s__(`ClusterIntegration|Point a wildcard DNS to this - generated endpoint in order to access - your application after it has been deployed.`) - }} - <gl-link :href="ingressDnsHelpPath" target="_blank"> - {{ __('More information') }} - </gl-link> - </p> - </div> - - <p v-if="!ingressExternalEndpoint" class="settings-message js-no-endpoint-message"> - {{ - s__(`ClusterIntegration|The endpoint is in - the process of being assigned. Please check your Kubernetes - cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) - }} - <gl-link :href="ingressDnsHelpPath" target="_blank"> - {{ __('More information') }} - </gl-link> - </p> - </template> - <template v-else> - <gl-alert variant="info" :dismissible="false"> - <span data-testid="ingressCostWarning"> - <gl-sprintf - :message=" - s__( - 'ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{linkStart}pricing%{linkEnd}.', - ) - " - > - <template #link="{ content }"> - <gl-link href="https://cloud.google.com/compute/pricing#lb" target="_blank">{{ - content - }}</gl-link> - </template> - </gl-sprintf> - </span> - </gl-alert> - </template> - </template> - </application-row> - <application-row - id="cert_manager" - :logo-url="$options.logos.certManagerLogo" - :title="applications.cert_manager.title" - :status="applications.cert_manager.status" - :status-reason="applications.cert_manager.statusReason" - :request-status="applications.cert_manager.requestStatus" - :request-reason="applications.cert_manager.requestReason" - :installed="applications.cert_manager.installed" - :install-failed="applications.cert_manager.installFailed" - :install-application-request-params="{ email: applications.cert_manager.email }" - :uninstallable="applications.cert_manager.uninstallable" - :uninstall-successful="applications.cert_manager.uninstallSuccessful" - :uninstall-failed="applications.cert_manager.uninstallFailed" - title-link="https://cert-manager.readthedocs.io/en/latest/#" - > - <template #description> - <p data-testid="certManagerDescription"> - <gl-sprintf - :message=" - 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 %{linkStart}Let's Encrypt%{linkEnd} and ensure that certificates - are valid and up-to-date.`) - " - > - <template #link="{ content }"> - <gl-link href="https://letsencrypt.org/" target="_blank">{{ content }}</gl-link> - </template> - </gl-sprintf> - </p> - <div class="form-group"> - <label for="cert-manager-issuer-email"> - {{ s__('ClusterIntegration|Issuer Email') }} - </label> - <div class="input-group"> - <!-- eslint-disable vue/no-mutating-props --> - <input - id="cert-manager-issuer-email" - v-model="applications.cert_manager.email" - :readonly="certManagerInstalled" - type="text" - class="form-control js-email" - /> - <!-- eslint-enable vue/no-mutating-props --> - </div> - <p class="form-text text-muted"> - {{ - s__(`ClusterIntegration|Issuers represent a certificate authority. - You must provide an email address for your Issuer.`) - }} - <gl-link - href="http://docs.cert-manager.io/en/latest/reference/issuers.html?highlight=email" - target="_blank" - >{{ __('More information') }}</gl-link - > - </p> - </div> - </template> - </application-row> - <application-row - id="prometheus" - :logo-url="$options.logos.prometheusLogo" - :title="applications.prometheus.title" - :manage-link="managePrometheusPath" - :status="applications.prometheus.status" - :status-reason="applications.prometheus.statusReason" - :request-status="applications.prometheus.requestStatus" - :request-reason="applications.prometheus.requestReason" - :installed="applications.prometheus.installed" - :install-failed="applications.prometheus.installFailed" - :uninstallable="applications.prometheus.uninstallable" - :uninstall-successful="applications.prometheus.uninstallSuccessful" - :uninstall-failed="applications.prometheus.uninstallFailed" - title-link="https://prometheus.io/docs/introduction/overview/" - > - <template #description> - <span data-testid="prometheusDescription"> - <gl-sprintf - :message=" - s__(`ClusterIntegration|Prometheus is an open-source monitoring system - with %{linkStart}GitLab Integration%{linkEnd} to monitor deployed applications.`) - " - > - <template #link="{ content }"> - <gl-link - href="https://docs.gitlab.com/ee/user/project/integrations/prometheus.html" - target="_blank" - >{{ content }}</gl-link - > - </template> - </gl-sprintf> - </span> - </template> - </application-row> - <application-row - id="runner" - :logo-url="$options.logos.gitlabLogo" - :title="applications.runner.title" - :status="applications.runner.status" - :status-reason="applications.runner.statusReason" - :request-status="applications.runner.requestStatus" - :request-reason="applications.runner.requestReason" - :version="applications.runner.version" - :chart-repo="applications.runner.chartRepo" - :update-available="applications.runner.updateAvailable" - :installed="applications.runner.installed" - :install-failed="applications.runner.installFailed" - :update-successful="applications.runner.updateSuccessful" - :update-failed="applications.runner.updateFailed" - :uninstallable="applications.runner.uninstallable" - :uninstall-successful="applications.runner.uninstallSuccessful" - :uninstall-failed="applications.runner.uninstallFailed" - title-link="https://docs.gitlab.com/runner/" - > - <template #description> - {{ - s__(`ClusterIntegration|GitLab Runner connects to the - repository and executes CI/CD jobs, - pushing results back and deploying - applications to production.`) - }} - </template> - </application-row> - <application-row - id="crossplane" - :logo-url="$options.logos.crossplaneLogo" - :title="applications.crossplane.title" - :status="applications.crossplane.status" - :status-reason="applications.crossplane.statusReason" - :request-status="applications.crossplane.requestStatus" - :request-reason="applications.crossplane.requestReason" - :installed="applications.crossplane.installed" - :install-failed="applications.crossplane.installFailed" - :uninstallable="applications.crossplane.uninstallable" - :uninstall-successful="applications.crossplane.uninstallSuccessful" - :uninstall-failed="applications.crossplane.uninstallFailed" - :install-application-request-params="{ stack: applications.crossplane.stack }" - title-link="https://crossplane.io" - > - <template #description> - <p data-testid="crossplaneDescription"> - <gl-sprintf - :message=" - s__( - `ClusterIntegration|Crossplane enables declarative provisioning of managed services from your cloud of choice using %{codeStart}kubectl%{codeEnd} or %{linkStart}GitLab Integration%{linkEnd}. - Crossplane runs inside your Kubernetes cluster and supports secure connectivity and secrets management between app containers and the cloud services they depend on.`, - ) - " - > - <template #code="{ content }"> - <code>{{ content }}</code> - </template> - <template #link="{ content }"> - <gl-link - href="https://docs.gitlab.com/ee/user/clusters/applications.html#crossplane" - target="_blank" - >{{ content }}</gl-link - > - </template> - </gl-sprintf> - </p> - <div class="form-group"> - <CrossplaneProviderStack :crossplane="crossplane" @set="setCrossplaneProviderStack" /> - </div> - </template> - </application-row> - - <application-row - id="jupyter" - :logo-url="$options.logos.jupyterhubLogo" - :title="applications.jupyter.title" - :status="applications.jupyter.status" - :status-reason="applications.jupyter.statusReason" - :request-status="applications.jupyter.requestStatus" - :request-reason="applications.jupyter.requestReason" - :installed="applications.jupyter.installed" - :install-failed="applications.jupyter.installFailed" - :uninstallable="applications.jupyter.uninstallable" - :uninstall-successful="applications.jupyter.uninstallSuccessful" - :uninstall-failed="applications.jupyter.uninstallFailed" - :install-application-request-params="{ hostname: applications.jupyter.hostname }" - title-link="https://jupyterhub.readthedocs.io/en/stable/" - > - <template #description> - <p> - {{ - s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns, - manages, and proxies multiple instances of the single-user - Jupyter notebook server. JupyterHub can be used to serve - notebooks to a class of students, a corporate data science group, - or a scientific research group.`) - }} - <gl-sprintf - :message=" - s__( - 'ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed.', - ) - " - > - <template #bold="{ content }"> - <b>{{ content }}</b> - </template> - </gl-sprintf> - </p> - - <template v-if="ingressExternalEndpoint"> - <div class="form-group"> - <label for="jupyter-hostname">{{ s__('ClusterIntegration|Jupyter Hostname') }}</label> - - <div class="input-group"> - <!-- eslint-disable vue/no-mutating-props --> - <input - id="jupyter-hostname" - v-model="applications.jupyter.hostname" - :readonly="jupyterInstalled" - type="text" - class="form-control js-hostname" - /> - <!-- eslint-enable vue/no-mutating-props --> - <span class="input-group-append"> - <clipboard-button - :text="jupyterHostname" - :title="s__('ClusterIntegration|Copy Jupyter Hostname')" - class="js-clipboard-btn" - /> - </span> - </div> - - <p v-if="ingressInstalled" class="form-text text-muted"> - {{ - s__(`ClusterIntegration|Replace this with your own hostname if you want. - If you do so, point hostname to Ingress IP Address from above.`) - }} - <gl-link :href="ingressDnsHelpPath" target="_blank"> - {{ __('More information') }} - </gl-link> - </p> - </div> - </template> - </template> - </application-row> - <application-row - id="knative" - :logo-url="$options.logos.knativeLogo" - :title="applications.knative.title" - :status="applications.knative.status" - :status-reason="applications.knative.statusReason" - :request-status="applications.knative.requestStatus" - :request-reason="applications.knative.requestReason" - :installed="applications.knative.installed" - :install-failed="applications.knative.installFailed" - :install-application-request-params="{ - hostname: applications.knative.hostname, - pages_domain_id: applications.knative.pagesDomain && applications.knative.pagesDomain.id, - }" - :uninstallable="applications.knative.uninstallable" - :uninstall-successful="applications.knative.uninstallSuccessful" - :uninstall-failed="applications.knative.uninstallFailed" - :updateable="false" - v-bind="applications.knative" - title-link="https://github.com/knative/docs" - > - <template #description> - <gl-alert v-if="!rbac" variant="info" class="rbac-notice gl-my-3" :dismissible="false"> - {{ - s__(`ClusterIntegration|You must have an RBAC-enabled cluster - to install Knative.`) - }} - <gl-link :href="helpPath" target="_blank">{{ __('More information') }}</gl-link> - </gl-alert> - <p> - {{ - s__(`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.`) - }} - </p> - - <knative-domain-editor - v-if="(knative.installed || rbac) && !preInstalledKnative" - :knative="knative" - :ingress-dns-help-path="ingressDnsHelpPath" - @save="saveKnativeDomain" - @set="setKnativeDomain" - /> - </template> - <template v-if="cloudRun" #installed-via> - <span data-testid="installed-via"> - <gl-sprintf - :message="s__('ClusterIntegration|installed via %{linkStart}Cloud Run%{linkEnd}')" - > - <template #link="{ content }"> - <gl-link :href="cloudRunHelpPath" target="_blank">{{ content }}</gl-link> - </template> - </gl-sprintf> - </span> - </template> - </application-row> - <application-row - id="elastic_stack" - :logo-url="$options.logos.elasticStackLogo" - :title="applications.elastic_stack.title" - :status="applications.elastic_stack.status" - :status-reason="applications.elastic_stack.statusReason" - :request-status="applications.elastic_stack.requestStatus" - :request-reason="applications.elastic_stack.requestReason" - :version="applications.elastic_stack.version" - :chart-repo="applications.elastic_stack.chartRepo" - :update-available="applications.elastic_stack.updateAvailable" - :installed="applications.elastic_stack.installed" - :install-failed="applications.elastic_stack.installFailed" - :update-successful="applications.elastic_stack.updateSuccessful" - :update-failed="applications.elastic_stack.updateFailed" - :uninstallable="applications.elastic_stack.uninstallable" - :uninstall-successful="applications.elastic_stack.uninstallSuccessful" - :uninstall-failed="applications.elastic_stack.uninstallFailed" - title-link="https://gitlab.com/gitlab-org/charts/elastic-stack" - > - <template #description> - <p> - {{ - s__( - `ClusterIntegration|The elastic stack collects logs from all pods in your cluster`, - ) - }} - </p> - </template> - </application-row> - - <div class="gl-mt-7 gl-border-1 gl-border-t-solid gl-border-gray-100"> - <!-- This empty div serves as a separator. The applications below can be externally installed using a cluster-management project. --> - </div> - - <application-row - id="cilium" - :title="applications.cilium.title" - :logo-url="$options.logos.gitlabLogo" - :status="applications.cilium.status" - :status-reason="applications.cilium.statusReason" - :installable="applications.cilium.installable" - :uninstallable="applications.cilium.uninstallable" - :installed="applications.cilium.installed" - :install-failed="applications.cilium.installFailed" - :title-link="ciliumHelpPath" - > - <template #description> - <p data-testid="ciliumDescription"> - <gl-sprintf - :message=" - s__( - 'ClusterIntegration|Protect your clusters with GitLab Container Network Policies by enforcing how pods communicate with each other and other network endpoints. %{linkStart}Learn more about configuring Network Policies here.%{linkEnd}', - ) - " - > - <template #link="{ content }"> - <gl-link :href="ciliumHelpPath" target="_blank">{{ content }}</gl-link> - </template> - </gl-sprintf> - </p> - </template> - </application-row> - </div> - </section> -</template> diff --git a/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue b/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue deleted file mode 100644 index 6b99bb09504..00000000000 --- a/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue +++ /dev/null @@ -1,93 +0,0 @@ -<script> -import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui'; -import { s__ } from '../../locale'; - -export default { - name: 'CrossplaneProviderStack', - components: { - GlDropdown, - GlDropdownItem, - GlIcon, - }, - props: { - stacks: { - type: Array, - required: false, - default: () => [ - { - name: s__('Google Cloud Platform'), - code: 'gcp', - }, - { - name: s__('Amazon Web Services'), - code: 'aws', - }, - { - name: s__('Microsoft Azure'), - code: 'azure', - }, - { - name: s__('Rook'), - code: 'rook', - }, - ], - }, - crossplane: { - type: Object, - required: true, - }, - }, - computed: { - dropdownText() { - const result = this.stacks.reduce((map, obj) => { - // eslint-disable-next-line no-param-reassign - map[obj.code] = obj.name; - return map; - }, {}); - const { stack } = this.crossplane; - if (stack !== '') { - return result[stack]; - } - return s__('Select Stack'); - }, - validationError() { - return this.crossplane.validationError; - }, - }, - methods: { - selectStack(stack) { - this.$emit('set', stack); - }, - }, -}; -</script> - -<template> - <div> - <label> - {{ s__('ClusterIntegration|Enabled stack') }} - </label> - <gl-dropdown - :disabled="crossplane.installed" - :text="dropdownText" - toggle-class="dropdown-menu-toggle gl-field-error-outline" - class="w-100" - :class="{ 'gl-show-field-errors': validationError }" - > - <gl-dropdown-item v-for="stack in stacks" :key="stack.code" @click="selectStack(stack)"> - <span class="ml-1">{{ stack.name }}</span> - </gl-dropdown-item> - </gl-dropdown> - <span v-if="validationError" class="gl-field-error">{{ validationError }}</span> - <p class="form-text text-muted"> - {{ s__(`You must select a stack for configuring your cloud provider. Learn more about`) }} - <a - href="https://crossplane.io/docs/master/stacks-guide.html" - target="_blank" - rel="noopener noreferrer" - >{{ __('Crossplane') }} - <gl-icon name="external-link" class="vertical-align-middle" /> - </a> - </p> - </div> -</template> diff --git a/app/assets/javascripts/clusters/components/knative_domain_editor.vue b/app/assets/javascripts/clusters/components/knative_domain_editor.vue deleted file mode 100644 index 89446680173..00000000000 --- a/app/assets/javascripts/clusters/components/knative_domain_editor.vue +++ /dev/null @@ -1,232 +0,0 @@ -<script> -import { - GlDropdown, - GlDropdownDivider, - GlDropdownItem, - GlLoadingIcon, - GlSearchBoxByType, - GlSprintf, - GlButton, - GlAlert, -} from '@gitlab/ui'; -import { APPLICATION_STATUS } from '~/clusters/constants'; -import { __, s__ } from '~/locale'; - -import ClipboardButton from '../../vue_shared/components/clipboard_button.vue'; - -const { UPDATING, UNINSTALLING } = APPLICATION_STATUS; - -export default { - components: { - GlButton, - ClipboardButton, - GlLoadingIcon, - GlDropdown, - GlDropdownDivider, - GlDropdownItem, - GlSearchBoxByType, - GlSprintf, - GlAlert, - }, - props: { - knative: { - type: Object, - required: true, - }, - ingressDnsHelpPath: { - type: String, - default: '', - required: false, - }, - }, - data() { - return { - searchQuery: '', - }; - }, - computed: { - saveButtonDisabled() { - return [UNINSTALLING, UPDATING].includes(this.knative.status); - }, - saving() { - return [UPDATING].includes(this.knative.status); - }, - saveButtonLabel() { - return this.saving ? __('Saving') : __('Save changes'); - }, - knativeInstalled() { - return this.knative.installed; - }, - knativeExternalEndpoint() { - return this.knative.externalIp || this.knative.externalHostname; - }, - knativeUpdateSuccessful() { - return this.knative.updateSuccessful; - }, - knativeHostname: { - get() { - return this.knative.hostname; - }, - set(hostname) { - this.selectCustomDomain(hostname); - }, - }, - domainDropdownText() { - return this.knativeHostname || s__('ClusterIntegration|Select existing domain or use new'); - }, - availableDomains() { - return this.knative.availableDomains || []; - }, - filteredDomains() { - const query = this.searchQuery.toLowerCase(); - return this.availableDomains.filter(({ domain }) => domain.toLowerCase().includes(query)); - }, - showDomainsDropdown() { - return this.availableDomains.length > 0; - }, - validationError() { - return this.knative.validationError; - }, - }, - watch: { - knativeUpdateSuccessful(updateSuccessful) { - if (updateSuccessful) { - this.$toast.show(s__('ClusterIntegration|Knative domain name was updated successfully.')); - } - }, - }, - methods: { - selectDomain({ id, domain }) { - this.$emit('set', { domain, domainId: id }); - }, - selectCustomDomain(domain) { - this.$emit('set', { domain, domainId: null }); - }, - }, -}; -</script> - -<template> - <div class="row"> - <gl-alert - v-if="knative.updateFailed" - class="gl-mb-5 col-12 js-cluster-knative-domain-name-failure-message" - variant="danger" - > - {{ s__('ClusterIntegration|Something went wrong while updating Knative domain name.') }} - </gl-alert> - - <div - :class="{ 'col-md-6': knativeInstalled, 'col-12': !knativeInstalled }" - class="form-group col-sm-12 mb-0" - > - <label for="knative-domainname"> - <strong>{{ s__('ClusterIntegration|Knative Domain Name:') }}</strong> - </label> - - <gl-dropdown - v-if="showDomainsDropdown" - :text="domainDropdownText" - toggle-class="dropdown-menu-toggle" - class="w-100 mb-2" - > - <gl-search-box-by-type - v-model.trim="searchQuery" - :placeholder="s__('ClusterIntegration|Search domains')" - /> - <gl-dropdown-item - v-for="domain in filteredDomains" - :key="domain.id" - @click="selectDomain(domain)" - > - <span class="ml-1">{{ domain.domain }}</span> - </gl-dropdown-item> - <template v-if="searchQuery"> - <gl-dropdown-divider /> - <gl-dropdown-item key="custom-domain" @click="selectCustomDomain(searchQuery)"> - <span class="ml-1"> - <gl-sprintf :message="s__('ClusterIntegration|Use %{query}')"> - <template #query> - <code>{{ searchQuery }}</code> - </template> - </gl-sprintf> - </span> - </gl-dropdown-item> - </template> - </gl-dropdown> - - <input - v-else - id="knative-domainname" - v-model="knativeHostname" - type="text" - class="form-control js-knative-domainname" - /> - - <span v-if="validationError" class="gl-field-error">{{ validationError }}</span> - </div> - - <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-endpoint"> - <strong>{{ s__('ClusterIntegration|Knative Endpoint:') }}</strong> - </label> - <div v-if="knativeExternalEndpoint" class="input-group"> - <input - id="knative-endpoint" - :value="knativeExternalEndpoint" - type="text" - class="form-control js-knative-endpoint" - readonly - /> - <span class="input-group-append"> - <clipboard-button - :text="knativeExternalEndpoint" - :title="s__('ClusterIntegration|Copy Knative Endpoint')" - class="input-group-text js-knative-endpoint-clipboard-btn" - /> - </span> - </div> - <div v-else class="input-group"> - <input type="text" class="form-control js-endpoint" readonly /> - <gl-loading-icon - class="position-absolute align-self-center ml-2 js-knative-ip-loading-icon" - /> - </div> - </div> - - <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 - v-if="!knativeExternalEndpoint" - class="settings-message js-no-knative-endpoint-message mt-2 mr-3 mb-0 ml-3" - > - {{ - s__(`ClusterIntegration|The endpoint 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> - - <gl-button - class="js-knative-save-domain-button gl-mt-5 gl-ml-5" - variant="success" - category="primary" - :loading="saving" - :disabled="saveButtonDisabled" - @click="$emit('save')" - > - {{ saveButtonLabel }} - </gl-button> - </template> - </div> -</template> diff --git a/app/assets/javascripts/clusters/components/uninstall_application_button.vue b/app/assets/javascripts/clusters/components/uninstall_application_button.vue deleted file mode 100644 index 73191d6d84d..00000000000 --- a/app/assets/javascripts/clusters/components/uninstall_application_button.vue +++ /dev/null @@ -1,36 +0,0 @@ -<script> -import { GlButton } from '@gitlab/ui'; -import { APPLICATION_STATUS } from '~/clusters/constants'; -import { __ } from '~/locale'; - -const { UPDATING, UNINSTALLING } = APPLICATION_STATUS; - -export default { - components: { - GlButton, - }, - props: { - status: { - type: String, - required: true, - }, - }, - computed: { - disabled() { - return [UNINSTALLING, UPDATING].includes(this.status); - }, - loading() { - return this.status === UNINSTALLING; - }, - label() { - return this.loading ? __('Uninstalling') : __('Uninstall'); - }, - }, -}; -</script> - -<template> - <gl-button :disabled="disabled" variant="default" :loading="loading"> - {{ label }} - </gl-button> -</template> diff --git a/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue b/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue deleted file mode 100644 index 2a197e40b60..00000000000 --- a/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue +++ /dev/null @@ -1,101 +0,0 @@ -<script> -import { GlModal, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; -import trackUninstallButtonClickMixin from 'ee_else_ce/clusters/mixins/track_uninstall_button_click'; -import { sprintf, s__ } from '~/locale'; -import { - HELM, - INGRESS, - CERT_MANAGER, - PROMETHEUS, - RUNNER, - KNATIVE, - JUPYTER, - ELASTIC_STACK, -} from '../constants'; - -const CUSTOM_APP_WARNING_TEXT = { - [HELM]: sprintf( - s__( - 'ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected.', - ), - { - gitlabManagedAppsNamespace: '<code>gitlab-managed-apps</code>', - }, - false, - ), - [INGRESS]: s__( - 'ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored.', - ), - [CERT_MANAGER]: s__( - 'ClusterIntegration|The associated private key will be deleted and cannot be restored.', - ), - [PROMETHEUS]: s__('ClusterIntegration|All data will be deleted and cannot be restored.'), - [RUNNER]: s__('ClusterIntegration|Any running pipelines will be canceled.'), - [KNATIVE]: s__( - 'ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications.', - ), - [JUPYTER]: s__( - 'ClusterIntegration|All data not committed to GitLab will be deleted and cannot be restored.', - ), - [ELASTIC_STACK]: s__('ClusterIntegration|All data will be deleted and cannot be restored.'), -}; - -export default { - components: { - GlModal, - }, - directives: { - SafeHtml, - }, - mixins: [trackUninstallButtonClickMixin], - props: { - application: { - type: String, - required: true, - }, - applicationTitle: { - type: String, - required: true, - }, - }, - computed: { - title() { - return sprintf(s__('ClusterIntegration|Uninstall %{appTitle}'), { - appTitle: this.applicationTitle, - }); - }, - warningText() { - return sprintf( - s__('ClusterIntegration|You are about to uninstall %{appTitle} from your cluster.'), - { - appTitle: this.applicationTitle, - }, - ); - }, - customAppWarningText() { - return CUSTOM_APP_WARNING_TEXT[this.application]; - }, - modalId() { - return `uninstall-${this.application}`; - }, - }, - methods: { - confirmUninstall() { - this.trackUninstallButtonClick(this.application); - this.$emit('confirm'); - }, - }, -}; -</script> -<template> - <gl-modal - ok-variant="danger" - cancel-variant="light" - :ok-title="title" - :modal-id="modalId" - :title="title" - @ok="confirmUninstall()" - > - {{ warningText }} <span v-safe-html="customAppWarningText"></span> - </gl-modal> -</template> diff --git a/app/assets/javascripts/clusters/components/update_application_confirmation_modal.vue b/app/assets/javascripts/clusters/components/update_application_confirmation_modal.vue deleted file mode 100644 index 0aedc6e84fa..00000000000 --- a/app/assets/javascripts/clusters/components/update_application_confirmation_modal.vue +++ /dev/null @@ -1,66 +0,0 @@ -<script> -/* eslint-disable vue/no-v-html */ -import { GlModal } from '@gitlab/ui'; -import { sprintf, s__ } from '~/locale'; -import { ELASTIC_STACK } from '../constants'; - -const CUSTOM_APP_WARNING_TEXT = { - [ELASTIC_STACK]: s__( - 'ClusterIntegration|Your Elasticsearch cluster will be re-created during this upgrade. Your logs will be re-indexed, and you will lose historical logs from hosts terminated in the last 30 days.', - ), -}; - -export default { - components: { - GlModal, - }, - props: { - application: { - type: String, - required: true, - }, - applicationTitle: { - type: String, - required: true, - }, - }, - computed: { - title() { - return sprintf(s__('ClusterIntegration|Update %{appTitle}'), { - appTitle: this.applicationTitle, - }); - }, - warningText() { - return sprintf( - s__('ClusterIntegration|You are about to update %{appTitle} on your cluster.'), - { - appTitle: this.applicationTitle, - }, - ); - }, - customAppWarningText() { - return CUSTOM_APP_WARNING_TEXT[this.application]; - }, - modalId() { - return `update-${this.application}`; - }, - }, - methods: { - confirmUpdate() { - this.$emit('confirm'); - }, - }, -}; -</script> -<template> - <gl-modal - ok-variant="danger" - cancel-variant="light" - :ok-title="title" - :modal-id="modalId" - :title="title" - @ok="confirmUpdate()" - > - {{ warningText }} <span v-html="customAppWarningText"></span> - </gl-modal> -</template> diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js index 846e5950b8b..c6ca895778d 100644 --- a/app/assets/javascripts/clusters/constants.js +++ b/app/assets/javascripts/clusters/constants.js @@ -10,64 +10,7 @@ export const PROVIDER_TYPE = { GCP: 'gcp', }; -// These need to match what is returned from the server -export const APPLICATION_STATUS = { - NO_STATUS: null, - NOT_INSTALLABLE: 'not_installable', - INSTALLABLE: 'installable', - SCHEDULED: 'scheduled', - INSTALLING: 'installing', - INSTALLED: 'installed', - UPDATING: 'updating', - UPDATED: 'updated', - UPDATE_ERRORED: 'update_errored', - UNINSTALLING: 'uninstalling', - UNINSTALL_ERRORED: 'uninstall_errored', - ERROR: 'errored', - PRE_INSTALLED: 'pre_installed', - UNINSTALLED: 'uninstalled', - EXTERNALLY_INSTALLED: 'externally_installed', -}; - -/* - * The application cannot be in any of the following states without - * not being installed. - */ -export const APPLICATION_INSTALLED_STATUSES = [ - APPLICATION_STATUS.INSTALLED, - APPLICATION_STATUS.UPDATING, - APPLICATION_STATUS.UNINSTALLING, - APPLICATION_STATUS.PRE_INSTALLED, -]; - // These are only used client-side -export const UPDATE_EVENT = 'update'; -export const INSTALL_EVENT = 'install'; -export const UNINSTALL_EVENT = 'uninstall'; - -export const HELM = 'helm'; -export const INGRESS = 'ingress'; -export const JUPYTER = 'jupyter'; -export const KNATIVE = 'knative'; -export const RUNNER = 'runner'; -export const CERT_MANAGER = 'cert_manager'; -export const CROSSPLANE = 'crossplane'; -export const PROMETHEUS = 'prometheus'; -export const ELASTIC_STACK = 'elastic_stack'; - -export const APPLICATIONS = [ - HELM, - INGRESS, - JUPYTER, - KNATIVE, - RUNNER, - CERT_MANAGER, - PROMETHEUS, - ELASTIC_STACK, -]; - -export const INGRESS_DOMAIN_SUFFIX = '.nip.io'; - export const LOGGING_MODE = 'logging'; export const BLOCKING_MODE = 'blocking'; diff --git a/app/assets/javascripts/clusters/services/application_state_machine.js b/app/assets/javascripts/clusters/services/application_state_machine.js deleted file mode 100644 index 2ff604af9a7..00000000000 --- a/app/assets/javascripts/clusters/services/application_state_machine.js +++ /dev/null @@ -1,250 +0,0 @@ -import { APPLICATION_STATUS, UPDATE_EVENT, INSTALL_EVENT, UNINSTALL_EVENT } from '../constants'; - -const { - NO_STATUS, - SCHEDULED, - NOT_INSTALLABLE, - INSTALLABLE, - INSTALLING, - INSTALLED, - ERROR, - UPDATING, - UPDATED, - UPDATE_ERRORED, - UNINSTALLING, - UNINSTALL_ERRORED, - PRE_INSTALLED, - UNINSTALLED, - EXTERNALLY_INSTALLED, -} = APPLICATION_STATUS; - -const applicationStateMachine = { - /* When the application initially loads, it will have `NO_STATUS` - * It will transition from `NO_STATUS` once the async backend call is completed - */ - [NO_STATUS]: { - on: { - [SCHEDULED]: { - target: INSTALLING, - }, - [NOT_INSTALLABLE]: { - target: NOT_INSTALLABLE, - }, - [INSTALLABLE]: { - target: INSTALLABLE, - }, - [INSTALLING]: { - target: INSTALLING, - }, - [INSTALLED]: { - target: INSTALLED, - }, - [ERROR]: { - target: INSTALLABLE, - effects: { - installFailed: true, - }, - }, - [UPDATING]: { - target: UPDATING, - }, - [UPDATED]: { - target: INSTALLED, - }, - [UPDATE_ERRORED]: { - target: INSTALLED, - effects: { - updateFailed: true, - }, - }, - [UNINSTALLING]: { - target: UNINSTALLING, - }, - [UNINSTALL_ERRORED]: { - target: INSTALLED, - effects: { - uninstallFailed: true, - }, - }, - [PRE_INSTALLED]: { - target: PRE_INSTALLED, - }, - [UNINSTALLED]: { - target: UNINSTALLED, - }, - [EXTERNALLY_INSTALLED]: { - target: EXTERNALLY_INSTALLED, - }, - }, - }, - [NOT_INSTALLABLE]: { - on: { - [INSTALLABLE]: { - target: INSTALLABLE, - }, - }, - }, - [INSTALLABLE]: { - on: { - [INSTALL_EVENT]: { - target: INSTALLING, - effects: { - installFailed: false, - }, - }, - [NOT_INSTALLABLE]: { - target: NOT_INSTALLABLE, - }, - [INSTALLED]: { - target: INSTALLED, - effects: { - installFailed: false, - }, - }, - [UNINSTALLED]: { - target: UNINSTALLED, - effects: { - installFailed: false, - }, - }, - }, - }, - [INSTALLING]: { - on: { - [INSTALLED]: { - target: INSTALLED, - }, - [ERROR]: { - target: INSTALLABLE, - effects: { - installFailed: true, - }, - }, - }, - }, - [INSTALLED]: { - on: { - [UPDATE_EVENT]: { - target: UPDATING, - effects: { - updateFailed: false, - updateSuccessful: false, - }, - }, - [NOT_INSTALLABLE]: { - target: NOT_INSTALLABLE, - }, - [UNINSTALL_EVENT]: { - target: UNINSTALLING, - effects: { - uninstallFailed: false, - uninstallSuccessful: false, - }, - }, - [UNINSTALLED]: { - target: UNINSTALLED, - }, - [ERROR]: { - target: INSTALLABLE, - effects: { - installFailed: true, - }, - }, - }, - }, - [PRE_INSTALLED]: { - on: { - [UPDATE_EVENT]: { - target: UPDATING, - effects: { - updateFailed: false, - updateSuccessful: false, - }, - }, - [NOT_INSTALLABLE]: { - target: NOT_INSTALLABLE, - }, - [UNINSTALL_EVENT]: { - target: UNINSTALLING, - effects: { - uninstallFailed: false, - uninstallSuccessful: false, - }, - }, - }, - }, - [UPDATING]: { - on: { - [UPDATED]: { - target: INSTALLED, - effects: { - updateSuccessful: true, - }, - }, - [UPDATE_ERRORED]: { - target: INSTALLED, - effects: { - updateFailed: true, - }, - }, - }, - }, - [UNINSTALLING]: { - on: { - [INSTALLABLE]: { - target: INSTALLABLE, - effects: { - uninstallSuccessful: true, - }, - }, - [NOT_INSTALLABLE]: { - target: NOT_INSTALLABLE, - effects: { - uninstallSuccessful: true, - }, - }, - [UNINSTALL_ERRORED]: { - target: INSTALLED, - effects: { - uninstallFailed: true, - }, - }, - }, - }, - [UNINSTALLED]: { - on: { - [INSTALLED]: { - target: INSTALLED, - }, - [ERROR]: { - target: INSTALLABLE, - effects: { - installFailed: true, - }, - }, - }, - }, -}; - -/** - * Determines an application new state based on the application current state - * and an event. If the application current state cannot handle a given event, - * the current state is returned. - * - * @param {*} application - * @param {*} event - */ -const transitionApplicationState = (application, event) => { - const stateMachine = applicationStateMachine[application.status]; - const newState = stateMachine !== undefined ? stateMachine.on[event] : false; - - return newState - ? { - ...application, - status: newState.target, - ...newState.effects, - } - : application; -}; - -export default transitionApplicationState; diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js index 333fb293a15..7300bb3137a 100644 --- a/app/assets/javascripts/clusters/services/clusters_service.js +++ b/app/assets/javascripts/clusters/services/clusters_service.js @@ -3,38 +3,12 @@ import axios from '../../lib/utils/axios_utils'; export default class ClusterService { constructor(options = {}) { this.options = options; - this.appInstallEndpointMap = { - helm: this.options.installHelmEndpoint, - ingress: this.options.installIngressEndpoint, - cert_manager: this.options.installCertManagerEndpoint, - crossplane: this.options.installCrossplaneEndpoint, - runner: this.options.installRunnerEndpoint, - prometheus: this.options.installPrometheusEndpoint, - jupyter: this.options.installJupyterEndpoint, - knative: this.options.installKnativeEndpoint, - elastic_stack: this.options.installElasticStackEndpoint, - }; - this.appUpdateEndpointMap = { - knative: this.options.updateKnativeEndpoint, - }; } fetchClusterStatus() { return axios.get(this.options.endpoint); } - installApplication(appId, params) { - return axios.post(this.appInstallEndpointMap[appId], params); - } - - updateApplication(appId, params) { - return axios.patch(this.appUpdateEndpointMap[appId], params); - } - - uninstallApplication(appId, params) { - return axios.delete(this.appInstallEndpointMap[appId], params); - } - fetchClusterEnvironments() { return axios.get(this.options.clusterEnvironmentsEndpoint); } diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index 50689a6142f..db6e7bad6cc 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -1,112 +1,16 @@ import { parseBoolean } from '../../lib/utils/common_utils'; -import { s__ } from '../../locale'; -import { - INGRESS, - JUPYTER, - KNATIVE, - CERT_MANAGER, - CROSSPLANE, - RUNNER, - APPLICATION_INSTALLED_STATUSES, - APPLICATION_STATUS, - INSTALL_EVENT, - UPDATE_EVENT, - UNINSTALL_EVENT, - ELASTIC_STACK, -} from '../constants'; -import transitionApplicationState from '../services/application_state_machine'; - -const isApplicationInstalled = (appStatus) => APPLICATION_INSTALLED_STATUSES.includes(appStatus); - -const applicationInitialState = { - status: null, - statusReason: null, - requestReason: null, - installable: true, - installed: false, - installFailed: false, - uninstallable: false, - uninstallFailed: false, - uninstallSuccessful: false, - validationError: null, -}; export default class ClusterStore { constructor() { this.state = { helpPath: null, - helmHelpPath: null, - ingressHelpPath: null, environmentsHelpPath: null, clustersHelpPath: null, deployBoardsHelpPath: null, - cloudRunHelpPath: null, status: null, providerType: null, - preInstalledKnative: false, rbac: false, statusReason: null, - applications: { - helm: { - ...applicationInitialState, - title: s__('ClusterIntegration|Legacy Helm Tiller server'), - }, - ingress: { - ...applicationInitialState, - title: s__('ClusterIntegration|Ingress'), - externalIp: null, - externalHostname: null, - updateFailed: false, - updateAvailable: false, - }, - cert_manager: { - ...applicationInitialState, - title: s__('ClusterIntegration|Cert-Manager'), - email: null, - }, - crossplane: { - ...applicationInitialState, - title: s__('ClusterIntegration|Crossplane'), - stack: null, - }, - runner: { - ...applicationInitialState, - title: s__('ClusterIntegration|GitLab Runner'), - version: null, - chartRepo: 'https://gitlab.com/gitlab-org/charts/gitlab-runner', - updateAvailable: null, - updateSuccessful: false, - updateFailed: false, - }, - prometheus: { - ...applicationInitialState, - title: s__('ClusterIntegration|Prometheus'), - }, - jupyter: { - ...applicationInitialState, - title: s__('ClusterIntegration|JupyterHub'), - hostname: null, - }, - knative: { - ...applicationInitialState, - title: s__('ClusterIntegration|Knative'), - hostname: null, - isEditingDomain: false, - externalIp: null, - externalHostname: null, - updateSuccessful: false, - updateFailed: false, - }, - elastic_stack: { - ...applicationInitialState, - title: s__('ClusterIntegration|Elastic Stack'), - }, - cilium: { - ...applicationInitialState, - title: s__('ClusterIntegration|GitLab Container Network Policies'), - installable: false, - }, - }, environments: [], fetchingEnvironments: false, }; @@ -118,10 +22,6 @@ export default class ClusterStore { }); } - setManagePrometheusPath(managePrometheusPath) { - this.state.managePrometheusPath = managePrometheusPath; - } - updateStatus(status) { this.state.status = status; } @@ -130,10 +30,6 @@ export default class ClusterStore { this.state.providerType = providerType; } - updatePreInstalledKnative(preInstalledKnative) { - this.state.preInstalledKnative = parseBoolean(preInstalledKnative); - } - updateRbac(rbac) { this.state.rbac = parseBoolean(rbac); } @@ -142,112 +38,9 @@ export default class ClusterStore { this.state.statusReason = reason; } - installApplication(appId) { - this.handleApplicationEvent(appId, INSTALL_EVENT); - } - - notifyInstallFailure(appId) { - this.handleApplicationEvent(appId, APPLICATION_STATUS.ERROR); - } - - updateApplication(appId) { - this.handleApplicationEvent(appId, UPDATE_EVENT); - } - - notifyUpdateFailure(appId) { - this.handleApplicationEvent(appId, APPLICATION_STATUS.UPDATE_ERRORED); - } - - uninstallApplication(appId) { - this.handleApplicationEvent(appId, UNINSTALL_EVENT); - } - - notifyUninstallFailure(appId) { - this.handleApplicationEvent(appId, APPLICATION_STATUS.UNINSTALL_ERRORED); - } - - handleApplicationEvent(appId, event) { - const currentAppState = this.state.applications[appId]; - - this.state.applications[appId] = transitionApplicationState(currentAppState, event); - } - - updateAppProperty(appId, prop, value) { - this.state.applications[appId][prop] = value; - } - updateStateFromServer(serverState = {}) { this.state.status = serverState.status; this.state.statusReason = serverState.status_reason; - - serverState.applications.forEach((serverAppEntry) => { - const { - name: appId, - status, - status_reason: statusReason, - version, - update_available: updateAvailable, - can_uninstall: uninstallable, - } = serverAppEntry; - const currentApplicationState = this.state.applications[appId] || {}; - const nextApplicationState = transitionApplicationState(currentApplicationState, status); - - this.state.applications[appId] = { - ...currentApplicationState, - ...nextApplicationState, - statusReason, - installed: isApplicationInstalled(nextApplicationState.status), - uninstallable, - }; - - if (appId === INGRESS) { - this.state.applications.ingress.externalIp = serverAppEntry.external_ip; - this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname; - this.state.applications.ingress.updateAvailable = updateAvailable; - } else if (appId === CERT_MANAGER) { - this.state.applications.cert_manager.email = - this.state.applications.cert_manager.email || serverAppEntry.email; - } else if (appId === CROSSPLANE) { - this.state.applications.crossplane.stack = - this.state.applications.crossplane.stack || serverAppEntry.stack; - } else if (appId === JUPYTER) { - this.state.applications.jupyter.hostname = this.updateHostnameIfUnset( - this.state.applications.jupyter.hostname, - serverAppEntry.hostname, - 'jupyter', - ); - } else if (appId === KNATIVE) { - if (serverAppEntry.available_domains) { - this.state.applications.knative.availableDomains = serverAppEntry.available_domains; - } - if (!this.state.applications.knative.isEditingDomain) { - this.state.applications.knative.pagesDomain = - serverAppEntry.pages_domain || this.state.applications.knative.pagesDomain; - 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; - this.state.applications.knative.externalHostname = - serverAppEntry.external_hostname || this.state.applications.knative.externalHostname; - } else if (appId === RUNNER) { - this.state.applications.runner.version = version; - this.state.applications.runner.updateAvailable = updateAvailable; - } else if (appId === ELASTIC_STACK) { - this.state.applications.elastic_stack.version = version; - this.state.applications.elastic_stack.updateAvailable = updateAvailable; - } - }); - } - - updateHostnameIfUnset(current, updated, fallback) { - return ( - current || - updated || - (this.state.applications.ingress.externalIp - ? `${fallback}.${this.state.applications.ingress.externalIp}.nip.io` - : '') - ); } toggleFetchEnvironments(isFetching) { |