Skip to content

Commit

Permalink
Merge pull request rancher#12414 from torchiaf/12322-virtualization-page
Browse files Browse the repository at this point in the history
Support Harvester extension, add installation info
  • Loading branch information
torchiaf authored Oct 31, 2024
2 parents 10e5e5b + a8e9313 commit ee7654c
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 72 deletions.
14 changes: 14 additions & 0 deletions pkg/harvester-manager/l10n/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ harvesterManager:
description: Harvester is a modern Hyperconverged infrastructure (HCI) solution built for bare metal servers using enterprise-grade open source technologies including Kubernetes, Kubevirt and Longhorn.
plugins:
loadError: Error loading harvester plugin
extension:
install:
warning: "The Harvester UI Extension is not installed"
prompt: "UI Integration for Harvester is now provided via the new Harvester UI Extension, which needs to be installed."
steps:
repo:
1: Go to the
2: Apps Repositories
3: page and add the <a target="_blank" href="https://github.com/harvester/harvester-ui-extension" rel="noopener noreferrer nofollow">Harvester repository</a>
ui:
1: Go to the
2: Extensions
3: page and install the Harvester Extension
admin: Please contact your system administrator
rke:
templateError: Incorrect template format
affinity:
Expand Down
254 changes: 182 additions & 72 deletions pkg/harvester-manager/list/harvesterhci.io.management.cluster.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
<script>
import { mapGetters } from 'vuex';
import { isAdminUser } from '@shell/store/type-map';
import BrandImage from '@shell/components/BrandImage';
import TypeDescription from '@shell/components/TypeDescription';
import ResourceTable from '@shell/components/ResourceTable';
import Masthead from '@shell/components/ResourceList/Masthead';
import Loading from '@shell/components/Loading';
import { HARVESTER_NAME as VIRTUAL } from '@shell/config/features';
import { CAPI, HCI, MANAGEMENT } from '@shell/config/types';
import { CAPI, HCI, MANAGEMENT, CATALOG } from '@shell/config/types';
import { isHarvesterCluster } from '@shell/utils/cluster';
import { allHash } from '@shell/utils/promise';
import { NAME as APP_PRODUCT } from '@shell/config/product/apps';
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
import { HARVESTER_EXTENSION, HARVESTER_REPO } from '../types';
export default {
components: {
Expand All @@ -32,31 +37,63 @@ export default {
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
const hash = await allHash({
const _hash = {
hciClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.CLUSTER }),
mgmtClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: MANAGEMENT.CLUSTER })
});
mgmtClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: MANAGEMENT.CLUSTER }),
catalogLoad: this.$store.dispatch('catalog/load', { reset: true }),
};
if (this.$store.getters[`${ inStore }/schemaFor`](CATALOG.CLUSTER_REPO)) {
_hash.clusterrepos = this.$store.dispatch(`${ inStore }/findAll`, { type: CATALOG.CLUSTER_REPO, force: true });
}
const hash = await allHash(_hash);
this.hciClusters = hash.hciClusters;
this.mgmtClusters = hash.mgmtClusters;
this.clusterrepos = hash.clusterrepos;
},
data() {
const resource = CAPI.RANCHER_CLUSTER;
return {
navigating: false,
isAdmin: isAdminUser(this.$store.getters),
navigating: false,
VIRTUAL,
hciDashboard: HCI.DASHBOARD,
hciDashboard: HCI.DASHBOARD,
resource,
hResource: HCI.CLUSTER,
realSchema: this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER),
hciClusters: [],
mgmtClusters: []
hResource: HCI.CLUSTER,
realSchema: this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER),
hciClusters: [],
mgmtClusters: [],
clusterrepos: [],
clusterRepoLink: {
name: 'c-cluster-product-resource',
params: {
cluster: 'local',
product: APP_PRODUCT,
resource: CATALOG.CLUSTER_REPO
}
},
extensionsLink: {
name: 'c-cluster-uiplugins',
params: { cluster: BLANK_CLUSTER }
},
};
},
computed: {
...mapGetters({ uiplugins: 'uiplugins/plugins' }),
harvesterRepo() {
return this.clusterrepos?.find((c) => c.spec?.gitRepo?.includes(HARVESTER_REPO));
},
harvesterExtension() {
return this.uiplugins?.find((c) => c.name === HARVESTER_EXTENSION);
},
importLocation() {
return {
name: 'c-cluster-product-resource-create',
Expand Down Expand Up @@ -111,71 +148,74 @@ export default {
<template>
<Loading v-if="$fetchState.pending" />
<div v-else>
<Masthead
:schema="realSchema"
:resource="resource"
:is-creatable="false"
:type-display="typeDisplay"
>
<template #typeDescription>
<TypeDescription :resource="hResource" />
</template>
<template
v-if="canCreateCluster"
#extraActions
<div v-if="!!harvesterExtension">
<Masthead
:schema="realSchema"
:resource="resource"
:is-creatable="false"
:type-display="typeDisplay"
>
<router-link
:to="importLocation"
class="btn role-primary"
<template #typeDescription>
<TypeDescription :resource="hResource" />
</template>
<template
v-if="canCreateCluster"
#extraActions
>
{{ t('cluster.importAction') }}
</router-link>
</template>
</Masthead>
<ResourceTable
v-if="rows && rows.length"
:schema="schema"
:rows="rows"
:is-creatable="true"
:namespaced="false"
:use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
>
<template #col:name="{row}">
<td>
<span class="cluster-link">
<a
v-if="row.isReady"
class="link"
:disabled="navigating ? true : null"
@click="goToCluster(row)"
>{{ row.nameDisplay }}</a>
<span v-else>
{{ row.nameDisplay }}
<router-link
:to="importLocation"
class="btn role-primary"
>
{{ t('cluster.importAction') }}
</router-link>
</template>
</Masthead>
<ResourceTable
v-if="rows && rows.length"
:schema="schema"
:rows="rows"
:is-creatable="true"
:namespaced="false"
:use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
>
<template #col:name="{row}">
<td>
<span class="cluster-link">
<a
v-if="row.isReady"
class="link"
:disabled="navigating ? true : null"
@click="goToCluster(row)"
>{{ row.nameDisplay }}</a>
<span v-else>
{{ row.nameDisplay }}
</span>
<i
class="icon icon-spinner icon-spin ml-5"
:class="{'navigating': navigating === row.id}"
/>
</span>
<i
class="icon icon-spinner icon-spin ml-5"
:class="{'navigating': navigating === row.id}"
/>
</span>
</td>
</template>
</td>
</template>
<template #cell:harvester="{row}">
<router-link
class="btn btn-sm role-primary"
:to="row.detailLocation"
>
{{ t('harvesterManager.manage') }}
</router-link>
</template>
</ResourceTable>
<div v-else>
<div class="no-clusters">
{{ t('harvesterManager.cluster.none') }}
<template #cell:harvester="{row}">
<router-link
class="btn btn-sm role-primary"
:to="row.detailLocation"
>
{{ t('harvesterManager.manage') }}
</router-link>
</template>
</ResourceTable>
<div v-else>
<div class="no-clusters">
{{ t('harvesterManager.cluster.none') }}
</div>
<hr class="info-section">
</div>
<hr class="info-section">
</div>
<template v-if="!harvesterExtension || !rows || !rows.length">
<div class="logo">
<BrandImage
file-name="harvester.png"
Expand All @@ -185,10 +225,52 @@ export default {
<div class="tagline">
<div>{{ t('harvesterManager.cluster.description') }}</div>
</div>
<div class="tagline sub-tagline">
<div class="tagline">
<div v-clean-html="t('harvesterManager.cluster.learnMore', {}, true)" />
</div>
</div>
<template v-if="!harvesterExtension">
<div class="tagline extension-warning-panel">
<div class="extensions-separator"></div>
<div
v-clean-html="t('harvesterManager.extension.install.warning', {}, true)"
class="extension-warning"
/>
<div class="tagline">
<div
v-clean-html="t('harvesterManager.extension.install.prompt', {}, true)"
class="extension-prompt"
/>
</div>
</div>
<div
v-if="isAdmin"
class="extension-info"
>
<ol class="steps">
<li v-if="!harvesterRepo">
{{ t('harvesterManager.extension.install.steps.repo.1') }}
<router-link :to="clusterRepoLink">
{{ t('harvesterManager.extension.install.steps.repo.2') }}
</router-link>
<span v-clean-html="t('harvesterManager.extension.install.steps.repo.3', {}, true)"></span>
</li>
<li>
{{ t('harvesterManager.extension.install.steps.ui.1') }}
<router-link :to="extensionsLink">
{{ t('harvesterManager.extension.install.steps.ui.2') }}
</router-link>
{{ t('harvesterManager.extension.install.steps.ui.3') }}
</li>
</ol>
</div>
<div
v-else
class="tagline"
>
<div v-clean-html="t('harvesterManager.extension.install.admin', {}, true)" />
</div>
</template>
</template>
</div>
</template>
Expand Down Expand Up @@ -234,6 +316,34 @@ export default {
}
}
.extensions-separator {
border: 1px solid var(--border);
margin-bottom: 20px;
width: 50%;
}
.extension-warning-panel {
align-items: center;
flex-direction: column;
}
.extension-warning {
font-size: 24px !important;
font-weight: 400;
}
.extension-info {
display: flex;
justify-content: center;
.steps {
> li {
margin-top: 5px;
font-size: 14px;
}
}
}
.link {
cursor: pointer;
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/harvester-manager/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const HARVESTER_REPO = 'harvester-ui-extension';
export const HARVESTER_EXTENSION = 'harvester';

0 comments on commit ee7654c

Please sign in to comment.