Skip to content

Commit

Permalink
Add install/uninstall buttons to clusters in the cluster list
Browse files Browse the repository at this point in the history
- we now show all clusters
- for clusters which we've found epinio on... and they can... show uninstall action
- for cluster with no epinio on... and they can... show install button
  • Loading branch information
richard-cox committed Nov 20, 2023
1 parent e114983 commit 0b76beb
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 25 deletions.
6 changes: 0 additions & 6 deletions dashboard/pkg/epinio/config/epinio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,6 @@ export function init($plugin: any, store: any) {
labelKey: 'epinio.instances.tableHeaders.api',
sort: ['api'],
},
{
name: 'rancherCluster',
labelKey: 'epinio.instances.tableHeaders.cluster',
sort: ['mgmtCluster.nameDisplay'],
value: 'mgmtCluster.nameDisplay'
},
]);

headers(EPINIO_TYPES.CONFIGURATION, [
Expand Down
3 changes: 1 addition & 2 deletions dashboard/pkg/epinio/l10n/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ epinio:
instances:
header: Epinio instances
none:
header: No instances of Epinio were found
description: To view an Epinio cluster be sure to import a Cluster where one is installed
description: No instances were found<br><br>Epinio can be installed to known clusters using the action menu to the right of the cluster
tableHeaders:
api: URL
version: Version
Expand Down
107 changes: 102 additions & 5 deletions dashboard/pkg/epinio/models/cluster.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import Resource from '@shell/plugins/dashboard-store/resource-class';
import { EPINIO_TYPES } from '../types';
import epinioAuth, { EpinioAuthConfig, EpinioAuthLocalConfig, EpinioAuthTypes } from '../utils/auth';
import { NAME as APP } from '@shell/config/product/apps';
import { CATALOG } from '@shell/config/types';
import Vue from 'vue';

export const EpinioInfoPath = `/api/v1/info`;

const defaultEpinioChart = {
repo: 'rancher-charts',
name: 'epinio',
};

export default class EpinioCluster extends Resource {
type = EPINIO_TYPES.CLUSTER;

Expand All @@ -16,6 +24,9 @@ export default class EpinioCluster extends Resource {
api: string;
mgmtCluster: any;
oidcEnabled: boolean = false;
installed: boolean = false;
canInstall: boolean = false;
canUninstall: boolean = false;

constructor(data: {
id: string,
Expand All @@ -24,6 +35,7 @@ export default class EpinioCluster extends Resource {
loggedIn: boolean,
api: string,
mgmtCluster: any,
installed: boolean,
}, ctx: any) {
super(data, ctx);
this.id = data.id;
Expand All @@ -32,18 +44,74 @@ export default class EpinioCluster extends Resource {
this.api = data.api;
this.loggedIn = data.loggedIn;
this.mgmtCluster = data.mgmtCluster;
this.installed = data.installed;

if (this.installed) {
// Can they uninstall?
// Try to find the installed helm app and check permissions on it

debugger;
const url = `/k8s/clusters/${ data.mgmtCluster.id }/v1/catalog.cattle.io.apps/${ data.namespace }/${ defaultEpinioChart.name }?exclude=metadata.managedFields`;

ctx.$dispatch(`cluster/request`, { url }, { root: true })
.then((app: any) => {
Vue.set(this, 'canUninstall', !!app?.actions?.uninstall);
})
.catch(() => {
Vue.set(this, 'canUninstall', false);
});
} else {
// Can they install?

// Can they install charts in target repo
const url = `/k8s/clusters/${ data.mgmtCluster.id }/v1/catalog.cattle.io.clusterrepos/${ defaultEpinioChart.repo }?exclude=metadata.managedFields`;

ctx.$dispatch(`cluster/request`, { url }, { root: true })
.then((repo: any) => {
Vue.set(this, 'canInstall', !!repo?.actions?.install);
})
.catch(() => {
Vue.set(this, 'canInstall', false);
});

// Ideally we would also check if they can see the target chart to install, but lets go on the assumption epinio app will always be there
}
}

get availableActions() {
return [
{
const actions: any[] = [];

if (this.loggedIn) {
actions.push({
action: 'logOut',
enabled: this.loggedIn,
icon: 'icon icon-fw icon-chevron-right',
label: this.t('nav.userMenu.logOut'),
disabled: false,
},
];
});

actions.push({ divider: true });
}

if (this.installed) {
if (this.canUninstall) {
actions.push({
action: 'uninstall',
icon: 'icon icon-fw icon-minus',
label: this.t('asyncButton.uninstall.action'),
});
}
} else {
if (this.canInstall) {
actions.push({
action: 'install',
icon: 'icon icon-fw icon-plus',
label: this.t('asyncButton.install.action'),
disabled: !this.canInstall,
});
}
}

return actions;
}

get infoUrl() {
Expand Down Expand Up @@ -81,4 +149,33 @@ export default class EpinioCluster extends Resource {

return config;
}

install() {
// Take them to the default helm chart's detail page
this.currentRouter().push({
name: `c-cluster-apps-charts-chart`,
params: {
product: APP,
cluster: this.mgmtCluster.id,
},
query: {
'repo-type': 'cluster',
repo: defaultEpinioChart.repo,
chart: defaultEpinioChart.name,
}
});
}

uninstall() {
// Uninstall is an action from the apps list, so do our best to get the user there
this.currentRouter().push({
name: `c-cluster-product-resource`,
params: {
product: APP,
cluster: this.mgmtCluster.id,
resource: CATALOG.APP,
},
query: { q: defaultEpinioChart.name }
});
}
}
45 changes: 36 additions & 9 deletions dashboard/pkg/epinio/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import epinioAuth, { EpinioAuthTypes } from '../utils/auth';
import EpinioCluster, { EpinioInfoPath } from '../models/cluster';
import LoginDialog from '../components/LoginDialog.vue';
import Dialog from '@shell/components/Dialog.vue';
import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
import { Banner } from '@components/Banner';
interface Data {
clustersSchema: any;
Expand All @@ -20,7 +22,7 @@ interface Data {
// Data, Methods, Computed, Props
export default Vue.extend<Data, any, any, any>({
components: {
AsyncButton, Loading, Link, ResourceTable, LoginDialog, Dialog
AsyncButton, Loading, Link, ResourceTable, LoginDialog, Dialog, Banner
},
layout: 'plain',
Expand All @@ -37,6 +39,7 @@ export default Vue.extend<Data, any, any, any>({
version: null,
infoUrl: EpinioInfoPath,
currentCluster: {},
UNINSTALLED: STATES_ENUM.UNINSTALLED,
};
},
Expand All @@ -63,6 +66,10 @@ export default Vue.extend<Data, any, any, any>({
clusters() {
return this.$store.getters[`${ EPINIO_MGMT_STORE }/all`](EPINIO_TYPES.CLUSTER);
},
installedClusters() {
return this.clusters.find((c: EpinioCluster) => c.installed);
}
},
Expand All @@ -85,6 +92,11 @@ export default Vue.extend<Data, any, any, any>({
},
testCluster(c: EpinioCluster) {
if (!c.installed) {
this.setClusterState(c, STATES_ENUM.UNINSTALLED, { state: { transitioning: false } });
return;
}
// Call '/ready' on each cluster. If there's a network error there's a good chance the user has to permit an invalid cert
this.setClusterState(c, 'updating', {
state: {
Expand Down Expand Up @@ -145,19 +157,19 @@ export default Vue.extend<Data, any, any, any>({
v-if="$fetchState.pending"
mode="main"
/>
<div
v-else-if="clusters.length === 0"
class="root"
>
<h2>{{ t('epinio.instances.none.header') }}</h2>
<p>{{ t('epinio.instances.none.description') }}</p>
</div>
<div
v-else
class="root"
>
<div class="epinios-table">
<h2>{{ t('epinio.instances.header') }}</h2>
<div v-if="installedClusters.length === 0">
<Banner
class="none"
color="info"
:labelKey="'epinio.instances.none.description'"
/>
</div>
<ResourceTable
:rows="clusters"
:schema="clustersSchema"
Expand Down Expand Up @@ -185,8 +197,9 @@ export default Vue.extend<Data, any, any, any>({
</template>
<template #cell:api="{row}">
<div class="epinio-row">
<template v-if="row.state === UNINSTALLED" />
<Link
v-if="row.state !== 'available'"
v-else-if="row.state !== 'available'"
:row="row"
:value="{ text: row.api, url: row.infoUrl }"
/>
Expand Down Expand Up @@ -217,6 +230,20 @@ export default Vue.extend<Data, any, any, any>({
</div>
</template>

<style lang="scss">
div.root .epinios-table {
.banner__content {
span {
text-align: center;
}
align-items: center;
display: flex;
flex-direction: column;
}
}
</style>

<style lang="scss" scoped>
div.root {
Expand Down
15 changes: 12 additions & 3 deletions dashboard/pkg/epinio/utils/epinio-discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,19 @@ class EpinioDiscovery {
namespace,
api: url,
loggedIn: !!loggedIn,
mgmtCluster: c
}, { rootGetters: store.getters }));
mgmtCluster: c,
installed: true,
}, { rootGetters: store.getters, $dispatch: store.dispatch }));
} catch (err) {
console.debug(`Skipping epinio discovery for ${ c.spec.displayName }:`, err); // eslint-disable-line no-console
epinioClusters.push(new EpinioCluster({
id: c.id,
name: c.spec.displayName,
namespace: '',
api: '',
loggedIn: false,
mgmtCluster: c,
installed: false,
}, { rootGetters: store.getters, $dispatch: store.dispatch }));
}
}

Expand Down

0 comments on commit 0b76beb

Please sign in to comment.