Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Harvester extension, add installation info #12414

Merged
merged 9 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 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,19 @@ 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: "Harvester extension is not installed"
steps:
repo:
1: Go to
2: Manage Repositories
a110605 marked this conversation as resolved.
Show resolved Hide resolved
3: and add the Harvester repository
ui:
1: Go to
2: Extensions
3: page and install the Harvester Extension
admin: Please contact your system administrator
rke:
templateError: Incorrect template format
affinity:
Expand Down
236 changes: 164 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.name === 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,45 @@ 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">
<div
v-clean-html="t('harvesterManager.extension.install.warning', {}, true)"
class="extension-warning"
/>
</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>
{{ t('harvesterManager.extension.install.steps.repo.3') }}
</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 +309,23 @@ export default {
}
}

.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';
Loading