diff --git a/dashboard/pkg/epinio/l10n/en-us.yaml b/dashboard/pkg/epinio/l10n/en-us.yaml
index 9ed006e..2318f25 100644
--- a/dashboard/pkg/epinio/l10n/en-us.yaml
+++ b/dashboard/pkg/epinio/l10n/en-us.yaml
@@ -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
Epinio can be installed to known clusters using the action menu to the right of the cluster
tableHeaders:
api: URL
version: Version
diff --git a/dashboard/pkg/epinio/models/cluster.ts b/dashboard/pkg/epinio/models/cluster.ts
index b974cc6..31f9370 100644
--- a/dashboard/pkg/epinio/models/cluster.ts
+++ b/dashboard/pkg/epinio/models/cluster.ts
@@ -2,9 +2,17 @@ import Resource from '@shell/plugins/dashboard-store/resource-class';
import { EPINIO_TYPES } from '../types';
import epinioAuth, { EpinioAuthConfig, EpinioAuthLocalConfig, EpinioAuthTypes } from '../utils/auth';
import { dashboardUrl } from '../utils/embedded-helpers';
+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;
@@ -17,6 +25,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,
@@ -25,6 +36,7 @@ export default class EpinioCluster extends Resource {
loggedIn: boolean,
api: string,
mgmtCluster: any,
+ installed: boolean,
}, ctx: any) {
super(data, ctx);
this.id = data.id;
@@ -33,18 +45,73 @@ 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
+
+ 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() {
@@ -82,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 }
+ });
+ }
}
diff --git a/dashboard/pkg/epinio/package.json b/dashboard/pkg/epinio/package.json
index 32e5c7c..d51d892 100644
--- a/dashboard/pkg/epinio/package.json
+++ b/dashboard/pkg/epinio/package.json
@@ -2,7 +2,7 @@
"name": "epinio",
"description": "Application Development Engine for Kubernetes",
"icon": "https://raw.githubusercontent.com/rancher/dashboard/0b6cbe93e9ed3292294da178f119a500cc494db9/pkg/epinio/assets/logo-epinio.svg",
- "version": "1.11.0-2",
+ "version": "1.11.0-3",
"private": false,
"rancher": true,
"license": "Apache-2.0",
diff --git a/dashboard/pkg/epinio/pages/index.vue b/dashboard/pkg/epinio/pages/index.vue
index b89858f..4d88fdd 100644
--- a/dashboard/pkg/epinio/pages/index.vue
+++ b/dashboard/pkg/epinio/pages/index.vue
@@ -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;
@@ -20,7 +22,7 @@ interface Data {
// Data, Methods, Computed, Props
export default Vue.extend({
components: {
- AsyncButton, Loading, Link, ResourceTable, LoginDialog, Dialog
+ AsyncButton, Loading, Link, ResourceTable, LoginDialog, Dialog, Banner
},
layout: 'plain',
@@ -37,6 +39,7 @@ export default Vue.extend({
version: null,
infoUrl: EpinioInfoPath,
currentCluster: {},
+ UNINSTALLED: STATES_ENUM.UNINSTALLED,
};
},
@@ -63,6 +66,10 @@ export default Vue.extend({
clusters() {
return this.$store.getters[`${ EPINIO_MGMT_STORE }/all`](EPINIO_TYPES.CLUSTER);
+ },
+
+ installedClusters() {
+ return this.clusters.find((c: EpinioCluster) => c.installed);
}
},
@@ -85,6 +92,11 @@ export default Vue.extend({
},
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: {
@@ -145,19 +157,19 @@ export default Vue.extend({
v-if="$fetchState.pending"
mode="main"
/>
-