Remove GMAv1 Fluentd

Remove ability to install Fluentd from GMAv1.

Changelog: removed
parent 9850151f
......@@ -11,7 +11,7 @@ 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, FLUENTD } from './constants';
import { APPLICATION_STATUS, CROSSPLANE, KNATIVE } from './constants';
import eventHub from './event_hub';
import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store';
......@@ -42,7 +42,6 @@ export default class Clusters {
installElasticStackPath,
installCrossplanePath,
installPrometheusPath,
installFluentdPath,
managePrometheusPath,
clusterEnvironmentsPath,
hasRbac,
......@@ -98,7 +97,6 @@ export default class Clusters {
updateKnativeEndpoint: updateKnativePath,
installElasticStackEndpoint: installElasticStackPath,
clusterEnvironmentsEndpoint: clusterEnvironmentsPath,
installFluentdEndpoint: installFluentdPath,
});
this.installApplication = this.installApplication.bind(this);
......@@ -250,7 +248,6 @@ export default class Clusters {
eventHub.$on('setKnativeDomain', (data) => this.setKnativeDomain(data));
eventHub.$on('uninstallApplication', (data) => this.uninstallApplication(data));
eventHub.$on('setCrossplaneProviderStack', (data) => this.setCrossplaneProviderStack(data));
eventHub.$on('setFluentdSettings', (data) => this.setFluentdSettings(data));
// Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
......@@ -267,8 +264,6 @@ export default class Clusters {
eventHub.$off('setCrossplaneProviderStack');
// eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('uninstallApplication');
// eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setFluentdSettings');
}
initPolling(method, successCallback, errorCallback) {
......@@ -480,12 +475,6 @@ export default class Clusters {
});
}
setFluentdSettings(settings = {}) {
Object.entries(settings).forEach(([key, value]) => {
this.store.updateAppProperty(FLUENTD, key, value);
});
}
saveKnativeDomain(data) {
const appId = data.id;
this.store.updateApplication(appId);
......
......@@ -3,7 +3,6 @@ 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 fluentdLogo from 'images/cluster_app_logos/fluentd.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';
......@@ -15,7 +14,6 @@ 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 FluentdOutputSettings from './fluentd_output_settings.vue';
import KnativeDomainEditor from './knative_domain_editor.vue';
export default {
......@@ -27,7 +25,6 @@ export default {
GlLink,
KnativeDomainEditor,
CrossplaneProviderStack,
FluentdOutputSettings,
GlAlert,
},
props: {
......@@ -159,7 +156,6 @@ export default {
knativeLogo,
prometheusLogo,
elasticStackLogo,
fluentdLogo,
},
};
</script>
......@@ -629,48 +625,6 @@ export default {
</template>
</application-row>
<application-row
id="fluentd"
:logo-url="$options.logos.fluentdLogo"
:title="applications.fluentd.title"
:status="applications.fluentd.status"
:status-reason="applications.fluentd.statusReason"
:request-status="applications.fluentd.requestStatus"
:request-reason="applications.fluentd.requestReason"
:installed="applications.fluentd.installed"
:install-failed="applications.fluentd.installFailed"
:install-application-request-params="{
host: applications.fluentd.host,
port: applications.fluentd.port,
protocol: applications.fluentd.protocol,
cilium_log_enabled: applications.fluentd.ciliumLogEnabled,
}"
:uninstallable="applications.fluentd.uninstallable"
:uninstall-successful="applications.fluentd.uninstallSuccessful"
:uninstall-failed="applications.fluentd.uninstallFailed"
:updateable="false"
title-link="https://github.com/helm/charts/tree/master/stable/fluentd"
>
<template #description>
<p>
{{
s__(
`ClusterIntegration|Fluentd is an open source data collector, which lets you unify the data collection and consumption for a better use and understanding of data. It requires at least one of the following logs to be successfully installed.`,
)
}}
</p>
<fluentd-output-settings
:port="applications.fluentd.port"
:protocol="applications.fluentd.protocol"
:host="applications.fluentd.host"
:cilium-log-enabled="applications.fluentd.ciliumLogEnabled"
:status="applications.fluentd.status"
:update-failed="applications.fluentd.updateFailed"
/>
</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>
......
<script>
import { GlAlert, GlButton, GlDropdown, GlDropdownItem, GlFormCheckbox } from '@gitlab/ui';
import { mapValues } from 'lodash';
import { APPLICATION_STATUS, FLUENTD } from '~/clusters/constants';
import eventHub from '~/clusters/event_hub';
import { __ } from '~/locale';
const { UPDATING, UNINSTALLING, INSTALLING, INSTALLED, UPDATED } = APPLICATION_STATUS;
export default {
components: {
GlAlert,
GlButton,
GlDropdown,
GlDropdownItem,
GlFormCheckbox,
},
props: {
protocols: {
type: Array,
required: false,
default: () => ['TCP', 'UDP'],
},
status: {
type: String,
required: false,
default: '',
},
updateFailed: {
type: Boolean,
required: false,
},
protocol: {
type: String,
required: false,
default: () => __('Protocol'),
},
port: {
type: Number,
required: false,
default: 514,
},
host: {
type: String,
required: false,
default: '',
},
ciliumLogEnabled: {
type: Boolean,
required: false,
},
},
data() {
return {
currentServerSideSettings: {
host: null,
port: null,
protocol: null,
ciliumLogEnabled: null,
},
};
},
computed: {
isSaving() {
return [UPDATING].includes(this.status);
},
saveButtonDisabled() {
return [UNINSTALLING, UPDATING, INSTALLING].includes(this.status);
},
saveButtonLabel() {
return this.isSaving ? __('Saving') : __('Save changes');
},
/**
* Returns true either when:
* - The application is getting updated.
* - The user has changed some of the settings for an application which is
* neither getting installed nor updated.
*/
showButtons() {
return this.isSaving || (this.changedByUser && [INSTALLED, UPDATED].includes(this.status));
},
protocolName() {
if (this.protocol) {
return this.protocol.toUpperCase();
}
return __('Protocol');
},
changedByUser() {
return Object.entries(this.currentServerSideSettings).some(([key, value]) => {
return value !== null && value !== this[key];
});
},
},
watch: {
status() {
this.resetCurrentServerSideSettings();
},
},
methods: {
updateApplication() {
eventHub.$emit('updateApplication', {
id: FLUENTD,
params: {
port: this.port,
protocol: this.protocol,
host: this.host,
cilium_log_enabled: this.ciliumLogEnabled,
},
});
},
resetCurrentServerSideSettings() {
this.currentServerSideSettings = mapValues(this.currentServerSideSettings, () => {
return null;
});
},
resetStatus() {
const newSettings = mapValues(this.currentServerSideSettings, (value, key) => {
return value === null ? this[key] : value;
});
eventHub.$emit('setFluentdSettings', {
...newSettings,
isEditingSettings: false,
});
},
updateCurrentServerSideSettings(settings) {
Object.keys(settings).forEach((key) => {
if (this.currentServerSideSettings[key] === null) {
this.currentServerSideSettings[key] = this[key];
}
});
},
setFluentdSettings(settings) {
this.updateCurrentServerSideSettings(settings);
eventHub.$emit('setFluentdSettings', {
...settings,
isEditingSettings: true,
});
},
selectProtocol(protocol) {
this.setFluentdSettings({ protocol });
},
hostChanged(host) {
this.setFluentdSettings({ host });
},
portChanged(port) {
this.setFluentdSettings({ port: Number(port) });
},
ciliumLogChanged(ciliumLogEnabled) {
this.setFluentdSettings({ ciliumLogEnabled });
},
},
};
</script>
<template>
<div>
<gl-alert v-if="updateFailed" class="mb-3" variant="danger" :dismissible="false">
{{
s__(
'ClusterIntegration|Something went wrong while trying to save your settings. Please try again.',
)
}}
</gl-alert>
<div class="form-horizontal">
<div class="form-group">
<label for="fluentd-host">
<strong>{{ s__('ClusterIntegration|SIEM Hostname') }}</strong>
</label>
<input
id="fluentd-host"
:value="host"
type="text"
class="form-control"
@input="hostChanged($event.target.value)"
/>
</div>
<div class="form-group">
<label for="fluentd-port">
<strong>{{ s__('ClusterIntegration|SIEM Port') }}</strong>
</label>
<input
id="fluentd-port"
:value="port"
type="number"
class="form-control"
@input="portChanged($event.target.value)"
/>
</div>
<div class="form-group">
<label for="fluentd-protocol">
<strong>{{ s__('ClusterIntegration|SIEM Protocol') }}</strong>
</label>
<gl-dropdown :text="protocolName" class="w-100">
<gl-dropdown-item
v-for="(value, index) in protocols"
:key="index"
@click="selectProtocol(value.toLowerCase())"
>
{{ value }}
</gl-dropdown-item>
</gl-dropdown>
</div>
<div class="form-group flex flex-wrap">
<gl-form-checkbox :checked="ciliumLogEnabled" @input="ciliumLogChanged">
<strong>{{ s__('ClusterIntegration|Send Container Network Policies Logs') }}</strong>
</gl-form-checkbox>
</div>
<div v-if="showButtons" class="gl-mt-5 gl-display-flex">
<gl-button
ref="saveBtn"
class="gl-mr-3"
variant="success"
category="primary"
:loading="isSaving"
:disabled="saveButtonDisabled"
@click="updateApplication"
>
{{ saveButtonLabel }}
</gl-button>
<gl-button ref="cancelBtn" :disabled="saveButtonDisabled" @click="resetStatus">
{{ __('Cancel') }}
</gl-button>
</div>
</div>
</div>
</template>
......@@ -55,7 +55,6 @@ export const CERT_MANAGER = 'cert_manager';
export const CROSSPLANE = 'crossplane';
export const PROMETHEUS = 'prometheus';
export const ELASTIC_STACK = 'elastic_stack';
export const FLUENTD = 'fluentd';
export const APPLICATIONS = [
HELM,
......@@ -66,7 +65,6 @@ export const APPLICATIONS = [
CERT_MANAGER,
PROMETHEUS,
ELASTIC_STACK,
FLUENTD,
];
export const INGRESS_DOMAIN_SUFFIX = '.nip.io';
......
......@@ -13,7 +13,6 @@ export default class ClusterService {
jupyter: this.options.installJupyterEndpoint,
knative: this.options.installKnativeEndpoint,
elastic_stack: this.options.installElasticStackEndpoint,
fluentd: this.options.installFluentdEndpoint,
};
this.appUpdateEndpointMap = {
knative: this.options.updateKnativeEndpoint,
......
......@@ -13,7 +13,6 @@ import {
UPDATE_EVENT,
UNINSTALL_EVENT,
ELASTIC_STACK,
FLUENTD,
} from '../constants';
import transitionApplicationState from '../services/application_state_machine';
......@@ -102,15 +101,6 @@ export default class ClusterStore {
...applicationInitialState,
title: s__('ClusterIntegration|Elastic Stack'),
},
fluentd: {
...applicationInitialState,
title: s__('ClusterIntegration|Fluentd'),
host: null,
port: null,
protocol: null,
ciliumLogEnabled: null,
isEditingSettings: false,
},
cilium: {
...applicationInitialState,
title: s__('ClusterIntegration|GitLab Container Network Policies'),
......@@ -246,13 +236,6 @@ export default class ClusterStore {
} else if (appId === ELASTIC_STACK) {
this.state.applications.elastic_stack.version = version;
this.state.applications.elastic_stack.updateAvailable = updateAvailable;
} else if (appId === FLUENTD) {
if (!this.state.applications.fluentd.isEditingSettings) {
this.state.applications.fluentd.port = serverAppEntry.port;
this.state.applications.fluentd.host = serverAppEntry.host;
this.state.applications.fluentd.protocol = serverAppEntry.protocol;
this.state.applications.fluentd.ciliumLogEnabled = serverAppEntry.cilium_log_enabled;
}
}
});
}
......
......@@ -17,7 +17,6 @@
install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
update_knative_path: clusterable.update_applications_cluster_path(@cluster, :knative),
install_elastic_stack_path: clusterable.install_applications_cluster_path(@cluster, :elastic_stack),
install_fluentd_path: clusterable.install_applications_cluster_path(@cluster, :fluentd),
cluster_environments_path: cluster_environments_path,
toggle_status: @cluster.enabled? ? 'true': 'false',
has_rbac: has_rbac_enabled?(@cluster) ? 'true': 'false',
......
......@@ -7234,12 +7234,6 @@ msgstr ""
msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|Fluentd"
msgstr ""
msgid "ClusterIntegration|Fluentd is an open source data collector, which lets you unify the data collection and consumption for a better use and understanding of data. It requires at least one of the following logs to be successfully installed."
msgstr ""
msgid "ClusterIntegration|GitLab Agent managed clusters"
msgstr ""
......@@ -7558,15 +7552,6 @@ msgstr ""
msgid "ClusterIntegration|Request to begin uninstalling failed"
msgstr ""
msgid "ClusterIntegration|SIEM Hostname"
msgstr ""
msgid "ClusterIntegration|SIEM Port"
msgstr ""
msgid "ClusterIntegration|SIEM Protocol"
msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
......@@ -7663,9 +7648,6 @@ msgstr ""
msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Send Container Network Policies Logs"
msgstr ""
msgid "ClusterIntegration|Service Token"
msgstr ""
......@@ -7687,9 +7669,6 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
msgid "ClusterIntegration|Something went wrong while trying to save your settings. Please try again."
msgstr ""
msgid "ClusterIntegration|Something went wrong while uninstalling %{title}"
msgstr ""
......@@ -26618,9 +26597,6 @@ msgstr ""
msgid "Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to deploy your code!"
msgstr ""
msgid "Protocol"
msgstr ""
msgid "Provider"
msgstr ""
......
......@@ -2,7 +2,6 @@ import { shallowMount, mount } from '@vue/test-utils';
import ApplicationRow from '~/clusters/components/application_row.vue';
import Applications from '~/clusters/components/applications.vue';
import CrossplaneProviderStack from '~/clusters/components/crossplane_provider_stack.vue';
import FluentdOutputSettings from '~/clusters/components/fluentd_output_settings.vue';
import KnativeDomainEditor from '~/clusters/components/knative_domain_editor.vue';
import { CLUSTER_TYPE, PROVIDER_TYPE } from '~/clusters/constants';
import eventHub from '~/clusters/event_hub';
......@@ -71,9 +70,6 @@ describe('Applications', () => {
expect(wrapper.find('.js-cluster-application-row-elastic_stack').exists()).toBe(true);
});
it('renders a row for Fluentd', () => {
expect(wrapper.find('.js-cluster-application-row-fluentd').exists()).toBe(true);
});
it('renders a row for Cilium', () => {
expect(wrapper.find('.js-cluster-application-row-cilium').exists()).toBe(true);
});
......@@ -116,10 +112,6 @@ describe('Applications', () => {
expect(wrapper.find('.js-cluster-application-row-elastic_stack').exists()).toBe(true);
});
it('renders a row for Fluentd', () => {
expect(wrapper.find('.js-cluster-application-row-fluentd').exists()).toBe(true);
});
it('renders a row for Cilium', () => {
expect(wrapper.find('.js-cluster-application-row-cilium').exists()).toBe(true);
});
......@@ -162,10 +154,6 @@ describe('Applications', () => {
expect(wrapper.find('.js-cluster-application-row-elastic_stack').exists()).toBe(true);
});
it('renders a row for Fluentd', () => {
expect(wrapper.find('.js-cluster-application-row-fluentd').exists()).toBe(true);
});
it('renders a row for Cilium', () => {
expect(wrapper.find('.js-cluster-application-row-cilium').exists()).toBe(true);
});
......@@ -220,7 +208,6 @@ describe('Applications', () => {
jupyter: { title: 'JupyterHub', hostname: '' },
knative: { title: 'Knative', hostname: '' },
elastic_stack: { title: 'Elastic Stack' },
fluentd: { title: 'Fluentd' },
cilium: { title: 'GitLab Container Network Policies' },
},
});
......@@ -514,14 +501,6 @@ describe('Applications', () => {
});
});
describe('Fluentd application', () => {
beforeEach(() => createShallowComponent());
it('renders the correct Component', () => {
expect(wrapper.find(FluentdOutputSettings).exists()).toBe(true);
});
});
describe('Cilium application', () => {
it('shows the correct description', () => {
createComponent({ propsData: { ciliumHelpPath: 'cilium-help-path' } });
......
import { GlAlert, GlDropdown, GlFormCheckbox } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import FluentdOutputSettings from '~/clusters/components/fluentd_output_settings.vue';
import { APPLICATION_STATUS, FLUENTD } from '~/clusters/constants';
import eventHub from '~/clusters/event_hub';
const { UPDATING } = APPLICATION_STATUS;
describe('FluentdOutputSettings', () => {
let wrapper;
const defaultSettings = {
protocol: 'tcp',
host: '127.0.0.1',
port: 514,
ciliumLogEnabled: false,
};
const defaultProps = {
status: 'installable',
updateFailed: false,
...defaultSettings,
};
const createComponent = (props = {}) => {
wrapper = shallowMount(FluentdOutputSettings, {
propsData: {
...defaultProps,
...props,
},
});
};
const updateComponentPropsFromEvent = () => {
const { isEditingSettings, ...props } = eventHub.$emit.mock.calls[0][1];
wrapper.setProps(props);
};
const findSaveButton = () => wrapper.find({ ref: 'saveBtn' });
const findCancelButton = () => wrapper.find({ ref: 'cancelBtn' });
const findProtocolDropdown = () => wrapper.find(GlDropdown);
const findCheckbox = (name) =>
wrapper.findAll(GlFormCheckbox).wrappers.find((x) => x.text() === name);
const findHost = () => wrapper.find('#fluentd-host');
const findPort = () => wrapper.find('#fluentd-port');
const changeCheckbox = (checkbox) => {
const currentValue = checkbox.attributes('checked')?.toString() === 'true';
checkbox.vm.$emit('input', !currentValue);
};
const changeInput = ({ element }, val) => {
element.value = val;
element.dispatchEvent(new Event('input'));
};
const changePort = (val) => changeInput(findPort(), val);
const changeHost = (val) => changeInput(findHost(), val);
const changeProtocol = (idx) => findProtocolDropdown().vm.$children[idx].$emit('click');
const toApplicationSettings = ({ ciliumLogEnabled, ...settings }) => ({
...settings,
cilium_log_enabled: ciliumLogEnabled,
});
describe('when fluentd is installed', () => {
beforeEach(() => {
createComponent({ status: 'installed' });
jest.spyOn(eventHub, '$emit');
});
it('does not render save and cancel buttons', () => {
expect(findSaveButton().exists()).toBe(false);
expect(findCancelButton().exists()).toBe(false);
});
describe.each`
desc | changeFn | key | value
${'when protocol dropdown is triggered'} | ${() => changeProtocol(1)} | ${'protocol'} | ${'udp'}
${'when host is changed'} | ${() => changeHost('test-host')} | ${'host'} | ${'test-host'}
${'when port is changed'} | ${() => changePort(123)} | ${'port'} | ${123}
${'when ciliumLogEnabled changes'} | ${() => changeCheckbox(findCheckbox('Send Container Network Policies Logs'))} | ${'ciliumLogEnabled'} | ${!defaultSettings.ciliumLogEnabled}
`('$desc', ({ changeFn, key, value }) => {
beforeEach(() => {
changeFn();
});
it('triggers set event to be propagated with the current value', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('setFluentdSettings', {
[key]: value,
isEditingSettings: true,
});
});
describe('when value is updated from store', () => {
beforeEach(() => {
updateComponentPropsFromEvent();
});
it('enables save and cancel buttons', () => {
expect(findSaveButton().exists()).toBe(true);
expect(findSaveButton().attributes().disabled).toBeUndefined();
expect(findCancelButton().exists()).toBe(true);
expect(findCancelButton().attributes().disabled).toBeUndefined();
});
describe('and the save changes button is clicked', () => {
beforeEach(() => {
eventHub.$emit.mockClear();
findSaveButton().vm.$emit('click');
});
it('triggers save event and pass current values', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('updateApplication', {
id: FLUENTD,
params: toApplicationSettings({
...defaultSettings,
[key]: value,
}),
});
});
});
describe('and the cancel button is clicked', () => {
beforeEach(() => {
eventHub.$emit.mockClear();
findCancelButton().vm.$emit('click');
});
it('triggers reset event', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('setFluentdSettings', {
...defaultSettings,
isEditingSettings: false,
});
});
describe('when value is updated from store', () => {
beforeEach(() => {
updateComponentPropsFromEvent();
});
it('does not render save and cancel buttons', () => {
expect(findSaveButton().exists()).toBe(false);
expect(findCancelButton().exists()).toBe(false);
});
});
});
});
});
describe(`when fluentd status is ${UPDATING}`, () => {
beforeEach(() => {
createComponent({ installed: true, status: UPDATING });
});
it('renders loading spinner in save button', () => {
expect(findSaveButton().props('loading')).toBe(true);
});
it('renders disabled save button', () => {
expect(findSaveButton().props('disabled')).toBe(true);
});
it('renders save button with "Saving" label', () => {
expect(findSaveButton().text()).toBe('Saving');
});
});
describe('when fluentd fails to update', () => {
beforeEach(() => {
createComponent({ updateFailed: true });
});
it('displays a error message', () => {
expect(wrapper.find(GlAlert).exists()).toBe(true);
});
});
});
describe('when fluentd is not installed', () => {
beforeEach(() => {
createComponent();
});
it('does not render the save button', () => {
expect(findSaveButton().exists()).toBe(false);
expect(findCancelButton().exists()).toBe(false);
});
});
});
......@@ -161,7 +161,6 @@ const APPLICATIONS_MOCK_STATE = {
jupyter: { title: 'JupyterHub', status: 'installable', hostname: '' },
knative: { title: 'Knative ', status: 'installable', hostname: '' },
elastic_stack: { title: 'Elastic Stack', status: 'installable' },
fluentd: { title: 'Fluentd', status: 'installable' },
cilium: {
title: 'GitLab Container Network Policies',
status: 'not_installable',
......
......@@ -122,24 +122,6 @@ describe('Clusters Store', () => {
uninstallFailed: false,
validationError: null,
},
fluentd: {
title: 'Fluentd',
status: null,
statusReason: null,
requestReason: null,
port: null,
ciliumLogEnabled: null,
host: null,
protocol: null,
installable: true,
installed: false,
isEditingSettings: false,
installFailed: false,
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
jupyter: {
title: 'JupyterHub',
status: mockResponseData.applications[4].status,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment