Commit 77b75a15 authored by Daniel Tian's avatar Daniel Tian Committed by ap4y

Add Cilium app to Kubernetes app list

This MR adds a new cluster application to the applications
component. Cilium application is different from the rest of the list
since it doesn't have a dependency on Helm and has specific state
transitions.
parent 6d4eb46b
......@@ -67,6 +67,7 @@ export default class Clusters {
deployBoardsHelpPath,
cloudRunHelpPath,
clusterId,
ciliumHelpPath,
} = document.querySelector('.js-edit-cluster-form').dataset;
this.clusterId = clusterId;
......@@ -83,6 +84,7 @@ export default class Clusters {
clustersHelpPath,
deployBoardsHelpPath,
cloudRunHelpPath,
ciliumHelpPath,
);
this.store.setManagePrometheusPath(managePrometheusPath);
this.store.updateStatus(clusterStatus);
......@@ -179,6 +181,7 @@ export default class Clusters {
providerType: this.state.providerType,
preInstalledKnative: this.state.preInstalledKnative,
rbac: this.state.rbac,
ciliumHelpPath: this.state.ciliumHelpPath,
},
});
},
......
......@@ -52,6 +52,11 @@ export default {
required: false,
default: false,
},
installable: {
type: Boolean,
required: false,
default: true,
},
uninstallable: {
type: Boolean,
required: false,
......@@ -141,6 +146,7 @@ export default {
return (
this.status === APPLICATION_STATUS.NOT_INSTALLABLE ||
this.status === APPLICATION_STATUS.INSTALLABLE ||
this.status === APPLICATION_STATUS.UNINSTALLED ||
this.isUnknownStatus
);
},
......@@ -164,14 +170,20 @@ export default {
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) ||
this.isInstalling) &&
this.isKnownStatus
this.status !== APPLICATION_STATUS.INSTALLABLE && this.status !== APPLICATION_STATUS.ERROR
);
},
installButtonLabel() {
......
......@@ -88,6 +88,11 @@ export default {
required: false,
default: false,
},
ciliumHelpPath: {
type: String,
required: false,
default: '',
},
},
computed: {
managedAppsLocalTillerEnabled() {
......@@ -687,6 +692,39 @@ export default {
/>
</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 between applications that have a dependency on Helm and those that can be enabled without Helm. -->
</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>
......@@ -25,6 +25,7 @@ export const APPLICATION_STATUS = {
UNINSTALL_ERRORED: 'uninstall_errored',
ERROR: 'errored',
PRE_INSTALLED: 'pre_installed',
UNINSTALLED: 'uninstalled',
};
/*
......
......@@ -14,6 +14,7 @@ const {
UNINSTALLING,
UNINSTALL_ERRORED,
PRE_INSTALLED,
UNINSTALLED,
} = APPLICATION_STATUS;
const applicationStateMachine = {
......@@ -67,6 +68,9 @@ const applicationStateMachine = {
[PRE_INSTALLED]: {
target: PRE_INSTALLED,
},
[UNINSTALLED]: {
target: UNINSTALLED,
},
},
},
[NOT_INSTALLABLE]: {
......@@ -87,9 +91,17 @@ const applicationStateMachine = {
[NOT_INSTALLABLE]: {
target: NOT_INSTALLABLE,
},
// This is possible in artificial environments for E2E testing
[INSTALLED]: {
target: INSTALLED,
effects: {
installFailed: false,
},
},
[UNINSTALLED]: {
target: UNINSTALLED,
effects: {
installFailed: false,
},
},
},
},
......@@ -125,6 +137,15 @@ const applicationStateMachine = {
uninstallSuccessful: false,
},
},
[UNINSTALLED]: {
target: UNINSTALLED,
},
[ERROR]: {
target: INSTALLABLE,
effects: {
installFailed: true,
},
},
},
},
[PRE_INSTALLED]: {
......@@ -180,6 +201,19 @@ const applicationStateMachine = {
},
},
},
[UNINSTALLED]: {
on: {
[INSTALLED]: {
target: INSTALLED,
},
[ERROR]: {
target: INSTALLABLE,
effects: {
installFailed: true,
},
},
},
},
};
/**
......
......@@ -23,6 +23,7 @@ const applicationInitialState = {
status: null,
statusReason: null,
requestReason: null,
installable: true,
installed: false,
installFailed: false,
uninstallable: false,
......@@ -114,6 +115,11 @@ export default class ClusterStore {
ciliumLogEnabled: null,
isEditingSettings: false,
},
cilium: {
...applicationInitialState,
title: s__('ClusterIntegration|GitLab Container Network Policies'),
installable: false,
},
},
environments: [],
fetchingEnvironments: false,
......@@ -129,6 +135,7 @@ export default class ClusterStore {
clustersHelpPath,
deployBoardsHelpPath,
cloudRunHelpPath,
ciliumHelpPath,
) {
this.state.helpPath = helpPath;
this.state.ingressHelpPath = ingressHelpPath;
......@@ -138,6 +145,7 @@ export default class ClusterStore {
this.state.clustersHelpPath = clustersHelpPath;
this.state.deployBoardsHelpPath = deployBoardsHelpPath;
this.state.cloudRunHelpPath = cloudRunHelpPath;
this.state.ciliumHelpPath = ciliumHelpPath;
}
setManagePrometheusPath(managePrometheusPath) {
......
......@@ -35,7 +35,8 @@
deploy_boards_help_path: help_page_path('user/project/deploy_boards.md', anchor: 'enabling-deploy-boards'),
cloud_run_help_path: help_page_path('user/project/clusters/add_remove_clusters.md', anchor: 'cloud-run-for-anthos'),
manage_prometheus_path: manage_prometheus_path,
cluster_id: @cluster.id } }
cluster_id: @cluster.id,
cilium_help_path: help_page_path('user/clusters/applications.md', anchor: 'install-cilium-using-gitlab-cicd')} }
.js-cluster-application-notice
.flash-container
......
---
title: Add cilium to Kubernetes apps list
merge_request: 33703
author:
type: added
......@@ -5307,6 +5307,9 @@ 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 Container Network Policies"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
msgstr ""
......@@ -5565,6 +5568,9 @@ msgstr ""
msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{linkStart}GitLab Integration%{linkEnd} to monitor deployed applications."
msgstr ""
msgid "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}"
msgstr ""
msgid "ClusterIntegration|Provider details"
msgstr ""
......
......@@ -17,6 +17,22 @@ exports[`Applications Cert-Manager application shows the correct description 1`]
</p>
`;
exports[`Applications Cilium application shows the correct description 1`] = `
<p
data-testid="ciliumDescription"
>
Protect your clusters with GitLab Container Network Policies by enforcing how pods communicate with each other and other network endpoints.
<a
class="gl-link"
href="cilium-help-path"
rel="noopener"
target="_blank"
>
Learn more about configuring Network Policies here.
</a>
</p>
`;
exports[`Applications Crossplane application shows the correct description 1`] = `
<p
data-testid="crossplaneDescription"
......
......@@ -83,6 +83,12 @@ describe('Application Row', () => {
checkButtonState('Installing', true, true);
});
it('has disabled "Install" when APPLICATION_STATUS.UNINSTALLED', () => {
mountComponent({ status: APPLICATION_STATUS.UNINSTALLED });
checkButtonState('Install', false, true);
});
it('has disabled "Installed" when application is installed and not uninstallable', () => {
mountComponent({
status: APPLICATION_STATUS.INSTALLED,
......@@ -112,6 +118,15 @@ describe('Application Row', () => {
checkButtonState('Install', false, false);
});
it('has disabled "Install" when installation disabled', () => {
mountComponent({
status: APPLICATION_STATUS.INSTALLABLE,
installable: false,
});
checkButtonState('Install', false, true);
});
it('has enabled "Install" when REQUEST_FAILURE (so you can try installing again)', () => {
mountComponent({ status: APPLICATION_STATUS.INSTALLABLE });
......
......@@ -17,7 +17,7 @@ describe('Applications', () => {
gon.features.managedAppsLocalTiller = false;
});
const createApp = ({ applications, type } = {}, isShallow) => {
const createApp = ({ applications, type, props } = {}, isShallow) => {
const mountMethod = isShallow ? shallowMount : mount;
wrapper = mountMethod(Applications, {
......@@ -25,6 +25,7 @@ describe('Applications', () => {
propsData: {
type,
applications: { ...APPLICATIONS_MOCK_STATE, ...applications },
...props,
},
});
};
......@@ -79,6 +80,9 @@ describe('Applications', () => {
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);
});
});
describe('Group cluster applications', () => {
......@@ -125,6 +129,10 @@ describe('Applications', () => {
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);
});
});
describe('Instance cluster applications', () => {
......@@ -171,6 +179,10 @@ describe('Applications', () => {
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);
});
});
describe('Helm application', () => {
......@@ -249,6 +261,7 @@ describe('Applications', () => {
knative: { title: 'Knative', hostname: '' },
elastic_stack: { title: 'Elastic Stack' },
fluentd: { title: 'Fluentd' },
cilium: { title: 'GitLab Container Network Policies' },
},
});
......@@ -365,7 +378,11 @@ describe('Applications', () => {
it('renders readonly input', () => {
createApp({
applications: {
ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
ingress: {
title: 'Ingress',
status: 'installed',
externalIp: '1.1.1.1',
},
jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' },
},
});
......@@ -552,4 +569,11 @@ describe('Applications', () => {
expect(wrapper.find(FluentdOutputSettings).exists()).toBe(true);
});
});
describe('Cilium application', () => {
it('shows the correct description', () => {
createApp({ props: { ciliumHelpPath: 'cilium-help-path' } });
expect(findByTestId('ciliumDescription').element).toMatchSnapshot();
});
});
});
......@@ -19,6 +19,7 @@ const {
UPDATE_ERRORED,
UNINSTALLING,
UNINSTALL_ERRORED,
UNINSTALLED,
} = APPLICATION_STATUS;
const NO_EFFECTS = 'no effects';
......@@ -40,6 +41,7 @@ describe('applicationStateMachine', () => {
${INSTALLED} | ${UPDATE_ERRORED} | ${{ updateFailed: true }}
${UNINSTALLING} | ${UNINSTALLING} | ${NO_EFFECTS}
${INSTALLED} | ${UNINSTALL_ERRORED} | ${{ uninstallFailed: true }}
${UNINSTALLED} | ${UNINSTALLED} | ${NO_EFFECTS}
`(`transitions to $expectedState on $event event and applies $effects`, data => {
const { expectedState, event, effects } = data;
const currentAppState = {
......@@ -74,8 +76,9 @@ describe('applicationStateMachine', () => {
it.each`
expectedState | event | effects
${INSTALLING} | ${INSTALL_EVENT} | ${{ installFailed: false }}
${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
${INSTALLED} | ${INSTALLED} | ${{ installFailed: false }}
${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
${UNINSTALLED} | ${UNINSTALLED} | ${{ installFailed: false }}
`(`transitions to $expectedState on $event event and applies $effects`, data => {
const { expectedState, event, effects } = data;
const currentAppState = {
......@@ -113,6 +116,8 @@ describe('applicationStateMachine', () => {
${UPDATING} | ${UPDATE_EVENT} | ${{ updateFailed: false, updateSuccessful: false }}
${UNINSTALLING} | ${UNINSTALL_EVENT} | ${{ uninstallFailed: false, uninstallSuccessful: false }}
${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
${UNINSTALLED} | ${UNINSTALLED} | ${NO_EFFECTS}
${INSTALLABLE} | ${ERROR} | ${{ installFailed: true }}
`(`transitions to $expectedState on $event event and applies $effects`, data => {
const { expectedState, event, effects } = data;
const currentAppState = {
......@@ -162,6 +167,23 @@ describe('applicationStateMachine', () => {
});
});
describe(`current state is ${UNINSTALLED}`, () => {
it.each`
expectedState | event | effects
${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
${INSTALLABLE} | ${ERROR} | ${{ installFailed: true }}
`(`transitions to $expectedState on $event event and applies $effects`, data => {
const { expectedState, event, effects } = data;
const currentAppState = {
status: UNINSTALLED,
};
expect(transitionApplicationState(currentAppState, event)).toEqual({
status: expectedState,
...noEffectsToEmptyObject(effects),
});
});
});
describe('current state is undefined', () => {
it('returns the current state without having any effects', () => {
const currentAppState = {};
......
......@@ -151,7 +151,11 @@ const DEFAULT_APPLICATION_STATE = {
const APPLICATIONS_MOCK_STATE = {
helm: { title: 'Helm Tiller', status: 'installable' },
ingress: { title: 'Ingress', status: 'installable', modsecurity_enabled: false },
ingress: {
title: 'Ingress',
status: 'installable',
modsecurity_enabled: false,
},
crossplane: { title: 'Crossplane', status: 'installable', stack: '' },
cert_manager: { title: 'Cert-Manager', status: 'installable' },
runner: { title: 'GitLab Runner' },
......@@ -160,6 +164,10 @@ const APPLICATIONS_MOCK_STATE = {
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',
},
};
export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE, APPLICATIONS_MOCK_STATE };
......@@ -66,6 +66,7 @@ describe('Clusters Store', () => {
status: mockResponseData.applications[0].status,
statusReason: mockResponseData.applications[0].status_reason,
requestReason: null,
installable: true,
installed: false,
installFailed: false,
uninstallable: false,
......@@ -80,6 +81,7 @@ describe('Clusters Store', () => {
requestReason: null,
externalIp: null,
externalHostname: null,
installable: true,
installed: false,
isEditingModSecurityEnabled: false,
isEditingModSecurityMode: false,
......@@ -100,6 +102,7 @@ describe('Clusters Store', () => {
version: mockResponseData.applications[2].version,
updateAvailable: mockResponseData.applications[2].update_available,
chartRepo: 'https://gitlab.com/gitlab-org/charts/gitlab-runner',
installable: true,
installed: false,
installFailed: false,
updateFailed: false,
......@@ -114,6 +117,7 @@ describe('Clusters Store', () => {
status: APPLICATION_STATUS.INSTALLABLE,
statusReason: mockResponseData.applications[3].status_reason,
requestReason: null,
installable: true,
installed: false,
installFailed: true,
uninstallable: false,
......@@ -130,6 +134,7 @@ describe('Clusters Store', () => {
ciliumLogEnabled: null,
host: null,
protocol: null,
installable: true,
installed: false,
isEditingSettings: false,
installFailed: false,
......@@ -145,6 +150,7 @@ describe('Clusters Store', () => {
statusReason: mockResponseData.applications[4].status_reason,
requestReason: null,
hostname: '',
installable: true,
installed: false,
installFailed: false,
uninstallable: false,
......@@ -161,6 +167,7 @@ describe('Clusters Store', () => {
isEditingDomain: false,
externalIp: null,
externalHostname: null,
installable: true,
installed: false,
installFailed: false,
uninstallable: false,
......@@ -177,6 +184,7 @@ describe('Clusters Store', () => {
statusReason: mockResponseData.applications[6].status_reason,
requestReason: null,
email: mockResponseData.applications[6].email,
installable: true,
installed: false,
uninstallable: false,
uninstallSuccessful: false,
......@@ -189,6 +197,7 @@ describe('Clusters Store', () => {
installFailed: true,
statusReason: mockResponseData.applications[7].status_reason,
requestReason: null,
installable: true,
installed: false,
uninstallable: false,
uninstallSuccessful: false,
......@@ -201,12 +210,26 @@ describe('Clusters Store', () => {
installFailed: true,
statusReason: mockResponseData.applications[8].status_reason,
requestReason: null,
installable: true,
installed: false,
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
cilium: {
title: 'GitLab Container Network Policies',
status: null,
statusReason: null,
requestReason: null,
installable: false,
installed: false,
installFailed: false,
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
},
},
environments: [],
fetchingEnvironments: false,
......
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