mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-04 21:26:08 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e33cc457e9 | ||
|
|
0208a65052 | ||
|
|
b5af90e475 | ||
|
|
a0e81cffff | ||
|
|
d774b323d1 | ||
|
|
fe0dc396dc | ||
|
|
fbd384d923 | ||
|
|
9a89f45a2a | ||
|
|
b8a1403655 | ||
|
|
5b0202a13b | ||
|
|
3d5a8556f2 | ||
|
|
6782edf7b0 | ||
|
|
b8a132c998 | ||
|
|
bc761e0747 | ||
|
|
ce2ead4426 | ||
|
|
4fe5c2e740 | ||
|
|
8a5f425b74 | ||
|
|
a7bb16636f | ||
|
|
63994c1896 | ||
|
|
7745f9805c | ||
|
|
36dd2b4541 | ||
|
|
4a19223afe | ||
|
|
4f44be4826 | ||
|
|
8b226980f3 | ||
|
|
51cf301d18 | ||
|
|
264eadb7dd | ||
|
|
c3cf1eb9a4 | ||
|
|
de10007dcc | ||
|
|
5bb40649e9 | ||
|
|
dd04d405f0 |
@@ -2,10 +2,36 @@
|
|||||||
|
|
||||||
Rancher Extension used in [rancher/dashboard](https://github.com/rancher/dashboard) for [Harvester](https://harvesterhci.io) Dashboard UI.
|
Rancher Extension used in [rancher/dashboard](https://github.com/rancher/dashboard) for [Harvester](https://harvesterhci.io) Dashboard UI.
|
||||||
|
|
||||||
|
`
|
||||||
|
From Rancher 2.10.0, Harvester integration is provided by this new Harvester UI Extension.
|
||||||
|
`
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install the Harvester UI Extension in Rancher 2.10.0 and newer:
|
||||||
|
|
||||||
|
- Go to 'Extensions' in the Rancher UI (click on the jigsaw icon in the side bar)
|
||||||
|
- From the three-dots menu in the upper right of the page, open the menu and select 'Manage Repositories'
|
||||||
|
- From the Repositories page, click 'Create'
|
||||||
|
- For name, enter 'harvester-ui-extension'
|
||||||
|
- For Target, choose 'Git repository'
|
||||||
|
- For Git Repo URL, enter `https://github.com/harvester/harvester-ui-extension`
|
||||||
|
- For Git Branch, enter `gh-pages`
|
||||||
|
- Click 'Create`
|
||||||
|
- From the Repositories list, wait until the new Repository added updates to the 'Active' state
|
||||||
|
- Go back to 'Extensions'
|
||||||
|
- Click on the 'Available' tab if not already selected
|
||||||
|
- Click on 'Install' for the Harvester UI Extension
|
||||||
|
|
||||||
## Running for Development
|
## Running for Development
|
||||||
|
|
||||||
This is what you probably want to get started.
|
This is what you probably want to get started.
|
||||||
|
|
||||||
|
### Prerequisite
|
||||||
|
|
||||||
|
Nodejs version >= 20
|
||||||
|
|
||||||
|
|
||||||
### Standalone Mode
|
### Standalone Mode
|
||||||
|
|
||||||
For development, serve with hot reload at https://localhost:8005
|
For development, serve with hot reload at https://localhost:8005
|
||||||
@@ -27,16 +53,15 @@ RANCHER_ENV=harvester VUE_APP_SERVER_VERSION=your-harvester-server-version API=h
|
|||||||
RANCHER_ENV=harvester VUE_APP_SERVER_VERSION=v1.4.0 API=https://192.168.1.123 yarn dev
|
RANCHER_ENV=harvester VUE_APP_SERVER_VERSION=v1.4.0 API=https://192.168.1.123 yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### Integration Mode
|
### Rancher Integration Mode
|
||||||
|
|
||||||
In this mode, you need to have Rancher and import Harvester, more detail in [Harvester Integration | Rancher](https://ranchermanager.docs.rancher.com/v2.6/integrations-in-rancher/harvester).
|
In this mode, you need to have Rancher and run Harvester UI Extension as Rancher extension, see more detail in [Rancher UI extension](https://extensions.rancher.io/extensions/next/extensions-getting-started#running-the-app).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# build harvester plugin
|
API=https://your-rancher-ip yarn dev
|
||||||
yarn build-pkg harvester && yarn serve-pkgs
|
|
||||||
|
|
||||||
# Open another terminal window
|
# Example
|
||||||
HARVESTER_PKG_URL=http://127.0.0.1:4500/harvester-${version}/harvester-${version}.umd.min.js API=https://your-rancher-ip RANCHER_ENV=harvester yarn mem-dev
|
API=https://192.168.1.123 yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
@@ -57,4 +82,4 @@ Unless required by applicable law or agreed to in writing, software
|
|||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rancher/shell": "3.0.1-rc.1",
|
"@rancher/shell": "3.0.1-rc.3",
|
||||||
"cache-loader": "^4.1.0",
|
"cache-loader": "^4.1.0",
|
||||||
"color": "4.2.3",
|
"color": "4.2.3",
|
||||||
"ip": "2.0.1",
|
"ip": "2.0.1",
|
||||||
@@ -46,4 +46,4 @@
|
|||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^7.1.0"
|
"eslint-plugin-promise": "^7.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,8 +81,10 @@ export default {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<v-dropdown
|
<v-dropdown
|
||||||
|
popper-class="filter-parent-sriov"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
|
:distance="20"
|
||||||
>
|
>
|
||||||
<slot name="header">
|
<slot name="header">
|
||||||
<button
|
<button
|
||||||
@@ -142,4 +144,11 @@ export default {
|
|||||||
.required {
|
.required {
|
||||||
color: var(--error);
|
color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.filter-parent-sriov .v-popper__arrow-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -724,7 +724,7 @@ export function init($plugin, store) {
|
|||||||
configureType(HCI.PCI_DEVICE, {
|
configureType(HCI.PCI_DEVICE, {
|
||||||
isCreatable: false,
|
isCreatable: false,
|
||||||
hiddenNamespaceGroupButton: true,
|
hiddenNamespaceGroupButton: true,
|
||||||
canYaml: false,
|
canYaml: true,
|
||||||
listGroups: [
|
listGroups: [
|
||||||
{
|
{
|
||||||
icon: 'icon-list-grouped',
|
icon: 'icon-list-grouped',
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
import { HCI, MANAGEMENT, CAPI } from '@shell/config/types';
|
|
||||||
import { HARVESTER, MULTI_CLUSTER } from '@shell/store/features';
|
|
||||||
import { allHash } from '@shell/utils/promise';
|
|
||||||
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
||||||
|
|
||||||
export const PRODUCT_NAME = 'harvester-manager';
|
|
||||||
|
|
||||||
export const NAME = 'harvesterManager';
|
|
||||||
|
|
||||||
const harvesterClustersLocation = {
|
|
||||||
name: 'c-cluster-product-resource',
|
|
||||||
params: {
|
|
||||||
cluster: BLANK_CLUSTER,
|
|
||||||
product: NAME,
|
|
||||||
resource: HCI.CLUSTER
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function init($plugin, store) {
|
|
||||||
const {
|
|
||||||
product,
|
|
||||||
basicType,
|
|
||||||
spoofedType,
|
|
||||||
configureType
|
|
||||||
} = $plugin.DSL(store, NAME);
|
|
||||||
|
|
||||||
product({
|
|
||||||
ifHaveType: CAPI.RANCHER_CLUSTER,
|
|
||||||
ifFeature: [MULTI_CLUSTER, HARVESTER],
|
|
||||||
inStore: 'management',
|
|
||||||
icon: 'harvester',
|
|
||||||
removable: false,
|
|
||||||
showClusterSwitcher: false,
|
|
||||||
weight: 100,
|
|
||||||
to: harvesterClustersLocation,
|
|
||||||
category: 'hci',
|
|
||||||
});
|
|
||||||
|
|
||||||
configureType(HCI.CLUSTER, { showListMasthead: false });
|
|
||||||
|
|
||||||
basicType([HCI.CLUSTER]);
|
|
||||||
spoofedType({
|
|
||||||
labelKey: 'harvesterManager.cluster.label',
|
|
||||||
name: HCI.CLUSTER,
|
|
||||||
type: HCI.CLUSTER,
|
|
||||||
namespaced: false,
|
|
||||||
weight: -1,
|
|
||||||
route: {
|
|
||||||
name: 'c-cluster-product-resource',
|
|
||||||
params: {
|
|
||||||
product: NAME,
|
|
||||||
resource: HCI.CLUSTER,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exact: false,
|
|
||||||
schemas: [
|
|
||||||
{
|
|
||||||
id: HCI.CLUSTER,
|
|
||||||
type: 'schema',
|
|
||||||
collectionMethods: [],
|
|
||||||
resourceFields: {},
|
|
||||||
attributes: { namespaced: true },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
group: 'Root',
|
|
||||||
getInstances: async() => {
|
|
||||||
const hash = {
|
|
||||||
rancherClusters: store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER }),
|
|
||||||
clusters: store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER }),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (store.getters['management/schemaFor'](MANAGEMENT.NODE)) {
|
|
||||||
hash.nodes = store.dispatch('management/findAll', { type: MANAGEMENT.NODE });
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await allHash(hash);
|
|
||||||
|
|
||||||
return res.rancherClusters.map((c) => {
|
|
||||||
return {
|
|
||||||
...c,
|
|
||||||
type: HCI.CLUSTER,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -93,7 +93,10 @@ export default {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
{{ t('harvester.usb.enablePassthroughWarning') }}
|
<t
|
||||||
|
k="harvester.usb.enablePassthroughWarning"
|
||||||
|
:raw="true"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #actions>
|
<template #actions>
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
<script>
|
|
||||||
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
||||||
import CruResource from '@shell/components/CruResource';
|
|
||||||
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
||||||
import Tab from '@shell/components/Tabbed/Tab';
|
|
||||||
import Tabbed from '@shell/components/Tabbed';
|
|
||||||
import { HCI, SCHEMA, CAPI, VIRTUAL_HARVESTER_PROVIDER } from '@shell/config/types';
|
|
||||||
import ClusterMembershipEditor from '@shell/components/form/Members/ClusterMembershipEditor';
|
|
||||||
import { Banner } from '@components/Banner';
|
|
||||||
import Labels from '@shell/edit/provisioning.cattle.io.cluster/Labels';
|
|
||||||
import AgentEnv from '@shell/edit/provisioning.cattle.io.cluster/AgentEnv';
|
|
||||||
import { set, get, clone } from '@shell/utils/object';
|
|
||||||
import { CAPI as CAPI_LABEL } from '@shell/config/labels-annotations';
|
|
||||||
|
|
||||||
import { createYaml } from '@shell/utils/create-yaml';
|
|
||||||
|
|
||||||
const REAL_TYPE = CAPI.RANCHER_CLUSTER;
|
|
||||||
|
|
||||||
export default {
|
|
||||||
emits: ['input'],
|
|
||||||
|
|
||||||
components: {
|
|
||||||
Banner,
|
|
||||||
ClusterMembershipEditor,
|
|
||||||
NameNsDescription,
|
|
||||||
CruResource,
|
|
||||||
Tab,
|
|
||||||
Tabbed,
|
|
||||||
Labels,
|
|
||||||
AgentEnv
|
|
||||||
},
|
|
||||||
|
|
||||||
mixins: [CreateEditView],
|
|
||||||
|
|
||||||
inheritAttrs: false,
|
|
||||||
|
|
||||||
props: {
|
|
||||||
mode: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
value: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return { membershipUpdate: {} };
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
generateYaml() {
|
|
||||||
return () => {
|
|
||||||
const resource = this.value;
|
|
||||||
|
|
||||||
const inStore = this.$store.getters['currentStore'](resource);
|
|
||||||
const schemas = this.$store.getters[`${ inStore }/all`](SCHEMA);
|
|
||||||
const clonedResource = clone(resource);
|
|
||||||
|
|
||||||
delete clonedResource.isSpoofed;
|
|
||||||
|
|
||||||
const out = createYaml(schemas, REAL_TYPE, clonedResource);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
done() {
|
|
||||||
return this.$router.replace({
|
|
||||||
name: 'c-cluster-product-resource-namespace-id',
|
|
||||||
params: {
|
|
||||||
resource: HCI.CLUSTER,
|
|
||||||
namespace: this.value.metadata.namespace,
|
|
||||||
id: this.value.metadata.name,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async saveOverride() {
|
|
||||||
set(this.value, 'metadata.labels', {
|
|
||||||
...(get(this.value, 'metadata.labels') || {}),
|
|
||||||
[CAPI_LABEL.PROVIDER]: VIRTUAL_HARVESTER_PROVIDER,
|
|
||||||
});
|
|
||||||
|
|
||||||
set(this.value, 'type', REAL_TYPE);
|
|
||||||
|
|
||||||
await this.save(...arguments);
|
|
||||||
|
|
||||||
this.value.waitForMgmt().then(() => {
|
|
||||||
if (this.membershipUpdate.save) {
|
|
||||||
this.membershipUpdate.save(this.value.mgmt.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onMembershipUpdate(update) {
|
|
||||||
this['membershipUpdate'] = update;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<CruResource
|
|
||||||
:mode="mode"
|
|
||||||
:resource="value"
|
|
||||||
:errors="errors"
|
|
||||||
:validation-passed="true"
|
|
||||||
:done-route="doneRoute"
|
|
||||||
:generate-yaml="generateYaml"
|
|
||||||
@finish="saveOverride"
|
|
||||||
@error="e=>errors = e"
|
|
||||||
>
|
|
||||||
<Banner
|
|
||||||
v-if="isCreate"
|
|
||||||
color="warning"
|
|
||||||
>
|
|
||||||
{{ t('harvesterManager.cluster.supportMessage') }}
|
|
||||||
</Banner>
|
|
||||||
<div class="mt-20">
|
|
||||||
<NameNsDescription
|
|
||||||
v-if="!isView"
|
|
||||||
:value="value"
|
|
||||||
:mode="mode"
|
|
||||||
:namespaced="false"
|
|
||||||
name-label="cluster.name.label"
|
|
||||||
name-placeholder="cluster.name.placeholder"
|
|
||||||
description-label="cluster.description.label"
|
|
||||||
description-placeholder="cluster.description.placeholder"
|
|
||||||
@update:value="$emit('input', $event)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Tabbed :side-tabs="true">
|
|
||||||
<Tab
|
|
||||||
name="memberRoles"
|
|
||||||
label-key="cluster.tabs.memberRoles"
|
|
||||||
:weight="3"
|
|
||||||
>
|
|
||||||
<Banner
|
|
||||||
v-if="isEdit"
|
|
||||||
color="info"
|
|
||||||
>
|
|
||||||
{{ t('cluster.memberRoles.removeMessage') }}
|
|
||||||
</Banner>
|
|
||||||
<ClusterMembershipEditor
|
|
||||||
:mode="mode"
|
|
||||||
:parent-id="value.mgmt ? value.mgmt.id : null"
|
|
||||||
@membership-update="onMembershipUpdate"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
<AgentEnv
|
|
||||||
:value="value"
|
|
||||||
:mode="mode"
|
|
||||||
@update:value="$emit('input', $event)"
|
|
||||||
/>
|
|
||||||
<Labels
|
|
||||||
:value="value"
|
|
||||||
:mode="mode"
|
|
||||||
@update:value="$emit('input', $event)"
|
|
||||||
/>
|
|
||||||
</Tabbed>
|
|
||||||
</CruResource>
|
|
||||||
</template>
|
|
||||||
@@ -20,10 +20,6 @@ export default function (plugin: IPlugin) {
|
|||||||
|
|
||||||
// Built-in icon
|
// Built-in icon
|
||||||
plugin.metadata.icon = require('./icon.svg');
|
plugin.metadata.icon = require('./icon.svg');
|
||||||
|
|
||||||
if (isDev && !isSingleVirtualCluster) {
|
|
||||||
plugin.addProduct(require('./config/harvester-manager'));
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.addProduct(require('./config/harvester-cluster'));
|
plugin.addProduct(require('./config/harvester-cluster'));
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,3 @@
|
|||||||
harvesterManager:
|
|
||||||
manage: Manage
|
|
||||||
tableHeaders:
|
|
||||||
kubernetesVersion: Kubernetes Version
|
|
||||||
harvesterVersion: Harvester Version
|
|
||||||
cluster:
|
|
||||||
label: Harvester Clusters
|
|
||||||
none: There are no Harvester Clusters
|
|
||||||
learnMore: Learn more about Harvester from the <a target="_blank" href="https://harvesterhci.io/" rel="noopener noreferrer nofollow">Harvester Web Site</a> or read the the <a target="_blank" href="https://docs.harvesterhci.io/" rel="noopener noreferrer nofollow">Harvester Docs</a>
|
|
||||||
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.
|
|
||||||
supportMessage: Harvester ui extension only supports Harvester cluster version greater or equal to 1.3.0
|
|
||||||
plugins:
|
|
||||||
loadError: Error loading harvester plugin
|
|
||||||
rke:
|
|
||||||
templateError: Incorrect template format
|
|
||||||
affinity:
|
|
||||||
thisPodNamespace: This VM's namespace
|
|
||||||
matchExpressions:
|
|
||||||
inNamespaces: "Workloads in these namespaces"
|
|
||||||
vmAffinityTitle: VM Scheduling
|
|
||||||
namespaces:
|
|
||||||
placeholder: e.g. default,system,base
|
|
||||||
label: Namespaces
|
|
||||||
addLabel: Add Workload Selector
|
|
||||||
topologyKey:
|
|
||||||
placeholder: 'topology.kubernetes.io/zone'
|
|
||||||
vGpu:
|
|
||||||
title: VGPUs
|
|
||||||
label: VGPU type
|
|
||||||
placeholder: 'Please select a VGPU'
|
|
||||||
|
|
||||||
generic:
|
generic:
|
||||||
tip: Tip
|
tip: Tip
|
||||||
resourceExternalLinkTips: 'External Link'
|
resourceExternalLinkTips: 'External Link'
|
||||||
@@ -39,6 +8,11 @@ generic:
|
|||||||
basic: Basic
|
basic: Basic
|
||||||
loading: Loading...
|
loading: Loading...
|
||||||
|
|
||||||
|
unsupported:
|
||||||
|
serverVersion: 'Current version: <code>{serverVersion}</code>'
|
||||||
|
description: Harvester ui only supports Harvester cluster version greater or equal to 1.3.0.
|
||||||
|
learnMore: Learn more about Harvester from the <a target="_blank" href="https://harvesterhci.io/" rel="noopener noreferrer nofollow">Harvester Web Site</a> or read the <a target="_blank" href="https://docs.harvesterhci.io/" rel="noopener noreferrer nofollow">Harvester Docs</a>
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
group:
|
group:
|
||||||
networks: Networks
|
networks: Networks
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import { HCI } from '../types';
|
|||||||
export default {
|
export default {
|
||||||
name: 'ListSriovGpuDevices',
|
name: 'ListSriovGpuDevices',
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Banner,
|
Banner,
|
||||||
Loading,
|
Loading,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import DeviceList from '../edit/kubevirt.io.virtualmachine/VirtualMachineUSBDevi
|
|||||||
export default {
|
export default {
|
||||||
name: 'ListUsbDevicePage',
|
name: 'ListUsbDevicePage',
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Banner,
|
Banner,
|
||||||
DeviceList,
|
DeviceList,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { ADD_ONS } from '../config/harvester-map';
|
|||||||
export default {
|
export default {
|
||||||
name: 'ListVGpuDevices',
|
name: 'ListVGpuDevices',
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Banner,
|
Banner,
|
||||||
Loading,
|
Loading,
|
||||||
|
|||||||
@@ -1,279 +0,0 @@
|
|||||||
<script>
|
|
||||||
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 { isHarvesterCluster } from '@shell/utils/cluster';
|
|
||||||
import { allHash } from '@shell/utils/promise';
|
|
||||||
import { STATE, NAME as NAME_COL, AGE, VERSION } from '@shell/config/table-headers';
|
|
||||||
import { MACHINE_POOLS } from '../config/table-headers';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
BrandImage,
|
|
||||||
ResourceTable,
|
|
||||||
Masthead,
|
|
||||||
TypeDescription,
|
|
||||||
Loading
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
schema: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
useQueryParamsForSimpleFiltering: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async fetch() {
|
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
||||||
const hash = await allHash({
|
|
||||||
hciClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.CLUSTER }),
|
|
||||||
mgmtClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: MANAGEMENT.CLUSTER })
|
|
||||||
});
|
|
||||||
|
|
||||||
this.hciClusters = hash.hciClusters;
|
|
||||||
this.mgmtClusters = hash.mgmtClusters;
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
const resource = CAPI.RANCHER_CLUSTER;
|
|
||||||
|
|
||||||
return {
|
|
||||||
navigating: false,
|
|
||||||
VIRTUAL,
|
|
||||||
hciDashboard: HCI.DASHBOARD,
|
|
||||||
resource,
|
|
||||||
hResource: HCI.CLUSTER,
|
|
||||||
realSchema: this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER),
|
|
||||||
hciClusters: [],
|
|
||||||
mgmtClusters: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
headers() {
|
|
||||||
return [
|
|
||||||
STATE,
|
|
||||||
NAME_COL,
|
|
||||||
{
|
|
||||||
name: 'harvesterVersion',
|
|
||||||
sort: 'harvesterVersion',
|
|
||||||
labelKey: 'harvesterManager.tableHeaders.harvesterVersion',
|
|
||||||
value: 'harvesterVersion',
|
|
||||||
getValue: (row) => row.harvesterVersion
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...VERSION,
|
|
||||||
labelKey: 'harvesterManager.tableHeaders.kubernetesVersion',
|
|
||||||
value: 'kubernetesVersion',
|
|
||||||
getValue: (row) => row.kubernetesVersion
|
|
||||||
},
|
|
||||||
MACHINE_POOLS,
|
|
||||||
AGE,
|
|
||||||
{
|
|
||||||
name: 'harvester',
|
|
||||||
label: ' ',
|
|
||||||
align: 'right',
|
|
||||||
width: 65,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
|
|
||||||
importLocation() {
|
|
||||||
return {
|
|
||||||
name: 'c-cluster-product-resource-create',
|
|
||||||
params: {
|
|
||||||
product: this.$store.getters['currentProduct'].name,
|
|
||||||
resource: this.schema.id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
canCreateCluster() {
|
|
||||||
const schema = this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
|
|
||||||
|
|
||||||
return !!schema?.collectionMethods.find((x) => x.toLowerCase() === 'post');
|
|
||||||
},
|
|
||||||
|
|
||||||
rows() {
|
|
||||||
return this.hciClusters
|
|
||||||
.filter((c) => {
|
|
||||||
const cluster = this.mgmtClusters.find((cluster) => cluster?.metadata?.name === c?.status?.clusterName);
|
|
||||||
|
|
||||||
return isHarvesterCluster(cluster);
|
|
||||||
})
|
|
||||||
.map((row) => {
|
|
||||||
if (row.isReady) {
|
|
||||||
row.setSupportedHarvesterVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
return row;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
typeDisplay() {
|
|
||||||
return this.t(`typeLabel."${ HCI.CLUSTER }"`, { count: this.rows?.length || 0 });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async goToCluster(row) {
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
// Don't show loading indicator for quickly fetched plugins
|
|
||||||
this.navigating = row.id;
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await row.goToCluster();
|
|
||||||
|
|
||||||
clearTimeout(timeout);
|
|
||||||
this.navigating = false;
|
|
||||||
} catch {
|
|
||||||
// The error handling is carried out within goToCluster, but just in case something happens before the promise chain can catch it...
|
|
||||||
clearTimeout(timeout);
|
|
||||||
this.navigating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<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
|
|
||||||
>
|
|
||||||
<router-link
|
|
||||||
:to="importLocation"
|
|
||||||
class="btn role-primary"
|
|
||||||
>
|
|
||||||
{{ t('cluster.importAction') }}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</Masthead>
|
|
||||||
|
|
||||||
<ResourceTable
|
|
||||||
v-if="rows && rows.length"
|
|
||||||
:schema="schema"
|
|
||||||
:headers="headers"
|
|
||||||
: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 && row.isSupportedHarvester"
|
|
||||||
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>
|
|
||||||
</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') }}
|
|
||||||
</div>
|
|
||||||
<hr class="info-section">
|
|
||||||
<div class="logo">
|
|
||||||
<BrandImage
|
|
||||||
file-name="harvester.png"
|
|
||||||
height="64"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="tagline">
|
|
||||||
<div>{{ t('harvesterManager.cluster.description') }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="tagline sub-tagline">
|
|
||||||
<div v-clean-html="t('harvesterManager.cluster.learnMore', {}, true)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cluster-link {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
// Use visibility to avoid the columns re-adjusting when the icon is shown
|
|
||||||
visibility: hidden;
|
|
||||||
|
|
||||||
&.navigating {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
.no-clusters {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
margin-top: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 60px 0 40px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tagline {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 22px;
|
|
||||||
max-width: 80%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
|
|
||||||
import { DEFAULT_WORKSPACE, HCI, MANAGEMENT } from '@shell/config/types';
|
|
||||||
import { HARVESTER_NAME, HARVESTER_NAME as VIRTUAL } from '@shell/config/features';
|
|
||||||
import { SETTING } from '@shell/config/settings';
|
|
||||||
import { colorForState, stateDisplay, STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
||||||
|
|
||||||
export default class HciCluster extends ProvCluster {
|
|
||||||
get isSupportedHarvester() {
|
|
||||||
return this._isSupportedHarvester === undefined ? true : this._isSupportedHarvester;
|
|
||||||
}
|
|
||||||
|
|
||||||
get harvesterVersion() {
|
|
||||||
return this._harvesterVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
get stateObj() {
|
|
||||||
if (!this.isSupportedHarvester) {
|
|
||||||
return { error: true, message: this.t('harvesterManager.cluster.supportMessage') };
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._stateObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyDefaults() {
|
|
||||||
if ( !this.spec ) {
|
|
||||||
this['spec'] = { agentEnvVars: [] };
|
|
||||||
this['metadata'] = { namespace: DEFAULT_WORKSPACE };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get isReady() {
|
|
||||||
// If the Connected condition exists, use that (2.6+)
|
|
||||||
if ( this.hasCondition('Connected') ) {
|
|
||||||
return this.isCondition('Connected');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise use Ready (older)
|
|
||||||
return this.isCondition('Ready');
|
|
||||||
}
|
|
||||||
|
|
||||||
get canEdit() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get stateColor() {
|
|
||||||
if (!this.isSupportedHarvester) {
|
|
||||||
return colorForState(STATES_ENUM.DENIED);
|
|
||||||
}
|
|
||||||
|
|
||||||
return colorForState(this.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
get stateDisplay() {
|
|
||||||
if (!this.isSupportedHarvester) {
|
|
||||||
return stateDisplay(STATES_ENUM.DENIED);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateDisplay(this.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch and cache the response for /ui-info
|
|
||||||
*
|
|
||||||
* Storing this in a cache means any changes to `ui-info` require a dashboard refresh... but it cuts out a http request every time we
|
|
||||||
* go to a cluster
|
|
||||||
*
|
|
||||||
* @param {string} clusterId
|
|
||||||
*/
|
|
||||||
async _getUiInfo(clusterId) {
|
|
||||||
if (!this._uiInfo) {
|
|
||||||
try {
|
|
||||||
const infoUrl = `/k8s/clusters/${ clusterId }/v1/harvester/ui-info`;
|
|
||||||
|
|
||||||
this._uiInfo = await this.$dispatch('request', { url: infoUrl });
|
|
||||||
} catch (e) {
|
|
||||||
console.info(`Failed to fetch harvester ui-info from ${ this.nameDisplay }, this may be an older cluster that cannot provide one`); // eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._uiInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the harvester plugin's package name and url for legacy clusters that don't provide the package (i.e. it's coming from
|
|
||||||
* outside the cluster)
|
|
||||||
*/
|
|
||||||
_legacyClusterPkgDetails() {
|
|
||||||
let uiOfflinePreferred = this.$rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_OFFLINE_PREFERRED)?.value;
|
|
||||||
// options: ['dynamic', 'true', 'false']
|
|
||||||
|
|
||||||
if (uiOfflinePreferred === 'dynamic') {
|
|
||||||
// We shouldn't need to worry about the version of the dashboard when embedded in harvester (aka in isSingleProduct)
|
|
||||||
const version = this.$rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.VERSION_RANCHER)?.value;
|
|
||||||
|
|
||||||
if (version.endsWith('-head')) {
|
|
||||||
uiOfflinePreferred = 'false';
|
|
||||||
} else {
|
|
||||||
uiOfflinePreferred = 'true';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the version that's embedded in the dashboard
|
|
||||||
const pkgName = `${ HARVESTER_NAME }-1.0.3`;
|
|
||||||
|
|
||||||
if (uiOfflinePreferred === 'true') {
|
|
||||||
// Embedded (aka give me the embedded plugin that was in the last rancher release)
|
|
||||||
const embeddedPath = `${ pkgName }/${ pkgName }.umd.min.js`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
pkgUrl: process.env.dev ? `${ process.env.api }/dashboard/${ embeddedPath }` : embeddedPath,
|
|
||||||
pkgName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uiOfflinePreferred === 'false') {
|
|
||||||
// Remote (aka give me the latest version of the embedded plugin that might not have been released yet)
|
|
||||||
const uiDashboardHarvesterRemotePlugin = this.$rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_DASHBOARD_HARVESTER_LEGACY_PLUGIN)?.value;
|
|
||||||
const parts = uiDashboardHarvesterRemotePlugin?.replace('.umd.min.js', '').split('/');
|
|
||||||
const pkgNameFromUrl = parts?.length > 1 ? parts[parts.length - 1] : null;
|
|
||||||
|
|
||||||
if (!pkgNameFromUrl) {
|
|
||||||
throw new Error(`Unable to determine harvester plugin name from '${ uiDashboardHarvesterRemotePlugin }'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
pkgUrl: uiDashboardHarvesterRemotePlugin,
|
|
||||||
pkgName: pkgNameFromUrl
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Unsupported value for ${ SETTING.UI_OFFLINE_PREFERRED }: 'uiOfflinePreferred'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the harvester plugin's package name and url for clusters that provide the plugin
|
|
||||||
*/
|
|
||||||
_supportedClusterPkgDetails(uiInfo, clusterId) {
|
|
||||||
let pkgName = `${ HARVESTER_NAME }-${ uiInfo['ui-plugin-bundled-version'] }`;
|
|
||||||
const fileName = `${ pkgName }.umd.min.js`;
|
|
||||||
let pkgUrl;
|
|
||||||
|
|
||||||
if (uiInfo['ui-source'] === 'bundled' ) { // offline bundled
|
|
||||||
pkgUrl = `/k8s/clusters/${ clusterId }/v1/harvester/plugin-assets/${ fileName }`;
|
|
||||||
} else if (uiInfo['ui-source'] === 'external') {
|
|
||||||
if (uiInfo['ui-plugin-index']) {
|
|
||||||
pkgUrl = uiInfo['ui-plugin-index'];
|
|
||||||
|
|
||||||
// When using an external address, the pkgName should also be get from the url
|
|
||||||
const names = pkgUrl.split('/');
|
|
||||||
const jsName = names[names.length - 1];
|
|
||||||
|
|
||||||
pkgName = jsName?.split('.umd.min.js')[0];
|
|
||||||
} else {
|
|
||||||
throw new Error('Harvester cluster requested the plugin at `ui-plugin-index` is used, however did not provide a value for it');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
pkgUrl,
|
|
||||||
pkgName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_overridePkgDetails() {
|
|
||||||
// Support loading the pkg from a locally, or other, address
|
|
||||||
// This helps testing of the harvester plugin when packaged up, instead of directly imported
|
|
||||||
const harvesterPkgUrl = process.env.harvesterPkgUrl;
|
|
||||||
|
|
||||||
if (!harvesterPkgUrl) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const parts = harvesterPkgUrl.replace('.umd.min.js', '').split('/');
|
|
||||||
const pkgNameFromUrl = parts.length > 1 ? parts[parts.length - 1] : null;
|
|
||||||
|
|
||||||
if (pkgNameFromUrl) {
|
|
||||||
return {
|
|
||||||
pkgUrl: harvesterPkgUrl,
|
|
||||||
pkgName: pkgNameFromUrl
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _pkgDetails() {
|
|
||||||
const overridePkgDetails = this._overridePkgDetails();
|
|
||||||
|
|
||||||
if (overridePkgDetails) {
|
|
||||||
return overridePkgDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
const clusterId = this.mgmt.id;
|
|
||||||
const uiInfo = await this._getUiInfo(clusterId);
|
|
||||||
|
|
||||||
return uiInfo ? this._supportedClusterPkgDetails(uiInfo, clusterId) : this._legacyClusterPkgDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
async goToCluster() {
|
|
||||||
this.currentRouter().push({
|
|
||||||
name: `${ VIRTUAL }-c-cluster-resource`,
|
|
||||||
params: {
|
|
||||||
cluster: this.status.clusterName,
|
|
||||||
product: VIRTUAL,
|
|
||||||
resource: HCI.DASHBOARD // Go directly to dashboard to avoid blip of components on screen
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async setSupportedHarvesterVersion() {
|
|
||||||
if (this._isSupportedHarvester !== undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `/k8s/clusters/${ this.status.clusterName }/v1`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const setting = await this.$dispatch('request', { url: `${ url }/${ HCI.SETTING }s/server-version` });
|
|
||||||
|
|
||||||
this._harvesterVersion = setting?.value;
|
|
||||||
this._isSupportedHarvester = this.$rootGetters['harvester-common/getFeatureEnabled']('supportHarvesterClusterVersion', setting?.value);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('unable to get harvester version from settings/server-version', error); // eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "harvester",
|
"name": "harvester",
|
||||||
"description": "Rancher UI Extension for Harvester",
|
"description": "Rancher UI Extension for Harvester",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"rancher": {
|
"rancher": {
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"catalog.cattle.io/display-name": "Harvester",
|
"catalog.cattle.io/display-name": "Harvester",
|
||||||
"catalog.cattle.io/kube-version": ">= 1.16.0-0",
|
"catalog.cattle.io/kube-version": ">= 1.16.0-0",
|
||||||
"catalog.cattle.io/rancher-version": ">= 2.10.0-0",
|
"catalog.cattle.io/rancher-version": ">= 2.10.1-0",
|
||||||
"catalog.cattle.io/ui-extensions-version": ">= 3.0.0-0"
|
"catalog.cattle.io/ui-extensions-version": ">= 3.0.0 < 4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"icon": "https://raw.githubusercontent.com/harvester/harvester-ui-extension/main/pkg/harvester/icon.svg",
|
"icon": "https://raw.githubusercontent.com/harvester/harvester-ui-extension/main/pkg/harvester/icon.svg",
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<script>
|
||||||
|
import BrandImage from '@shell/components/BrandImage';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { BrandImage },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
serverVersion() {
|
||||||
|
const version = this.$store.getters['harvester-common/getServerVersion']() || '';
|
||||||
|
|
||||||
|
return version.replace('v', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="dashboard-root">
|
||||||
|
<div class="dashboard-content">
|
||||||
|
<main class="main-layout">
|
||||||
|
<div class="logo">
|
||||||
|
<BrandImage
|
||||||
|
file-name="harvester.png"
|
||||||
|
height="64"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="tagline">
|
||||||
|
<div class="description">
|
||||||
|
{{ t('unsupported.description') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tagline">
|
||||||
|
<div v-clean-html="t('unsupported.serverVersion', { serverVersion }, true)" />
|
||||||
|
</div>
|
||||||
|
<div class="tagline">
|
||||||
|
<div v-clean-html="t('unsupported.learnMore', {}, true)" />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dashboard-root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-content {
|
||||||
|
display: grid;
|
||||||
|
flex-grow:1;
|
||||||
|
|
||||||
|
grid-template-areas:
|
||||||
|
"main";
|
||||||
|
|
||||||
|
grid-template-columns: auto;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 200px 0 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagline {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 22px;
|
||||||
|
max-width: 80%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-size: 24px !important;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -15,8 +15,25 @@ import HarvesterAirgapUpdgrade from '../pages/c/_cluster/airgapupgrade/index.vue
|
|||||||
import HarvesterMembers from '../pages/c/_cluster/members/index.vue';
|
import HarvesterMembers from '../pages/c/_cluster/members/index.vue';
|
||||||
import ProjectNamespaces from '../pages/c/_cluster/projectsnamespaces.vue';
|
import ProjectNamespaces from '../pages/c/_cluster/projectsnamespaces.vue';
|
||||||
import HarvesterAlertmanagerReceiver from '../pages/c/_cluster/alertmanagerconfig/_alertmanagerconfigid/receiver.vue';
|
import HarvesterAlertmanagerReceiver from '../pages/c/_cluster/alertmanagerconfig/_alertmanagerconfigid/receiver.vue';
|
||||||
|
import HarvesterUnsupported from '../pages/c/_cluster/unsupported/index.vue';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
|
{
|
||||||
|
route: {
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-unsupported-standalone`,
|
||||||
|
path: `/:product`,
|
||||||
|
component: HarvesterUnsupported
|
||||||
|
},
|
||||||
|
parent: 'blank'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
route: {
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-unsupported`,
|
||||||
|
path: `/:product`,
|
||||||
|
component: HarvesterUnsupported
|
||||||
|
},
|
||||||
|
parent: 'plain'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-support`,
|
name: `${ PRODUCT_NAME }-c-cluster-support`,
|
||||||
path: `/:product/c/:cluster/support`,
|
path: `/:product/c/:cluster/support`,
|
||||||
|
|||||||
@@ -101,6 +101,19 @@ export default {
|
|||||||
|
|
||||||
const res: any = await allHash(hash);
|
const res: any = await allHash(hash);
|
||||||
|
|
||||||
|
const isHarvesterVersionSupported = rootGetters['harvester-common/getFeatureEnabled']('supportHarvesterClusterVersion');
|
||||||
|
|
||||||
|
if (!isHarvesterVersionSupported) {
|
||||||
|
const product = rootGetters['productId'];
|
||||||
|
|
||||||
|
this.$router?.push({
|
||||||
|
name: `${ product }-c-cluster-unsupported${ id === 'local' ? '-standalone' : '' }`,
|
||||||
|
params: { product }
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await dispatch('cleanNamespaces', null, { root: true });
|
await dispatch('cleanNamespaces', null, { root: true });
|
||||||
|
|
||||||
commit('updateNamespaces', {
|
commit('updateNamespaces', {
|
||||||
|
|||||||
@@ -2460,7 +2460,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||||
|
|
||||||
"@babel/runtime@^7.12.13", "@babel/runtime@^7.15.4", "@babel/runtime@^7.8.4":
|
"@babel/runtime@^7.12.13", "@babel/runtime@^7.8.4":
|
||||||
version "7.25.6"
|
version "7.25.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.6.tgz#9afc3289f7184d8d7f98b099884c26317b9264d2"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.6.tgz#9afc3289f7184d8d7f98b099884c26317b9264d2"
|
||||||
integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==
|
integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==
|
||||||
@@ -2915,24 +2915,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@novnc/novnc/-/novnc-1.2.0.tgz#535ffd7d074022d35205deae2753100f1f2b29f3"
|
resolved "https://registry.yarnpkg.com/@novnc/novnc/-/novnc-1.2.0.tgz#535ffd7d074022d35205deae2753100f1f2b29f3"
|
||||||
integrity sha512-FaUckOedGhSbwQBXk/KGyxKt9ngskg4wPw6ghbHWXOUEmQscAZr3467lTU5DSfppwHJt5k+lQiHoeYUuY90l2Q==
|
integrity sha512-FaUckOedGhSbwQBXk/KGyxKt9ngskg4wPw6ghbHWXOUEmQscAZr3467lTU5DSfppwHJt5k+lQiHoeYUuY90l2Q==
|
||||||
|
|
||||||
"@nuxtjs/axios@5.13.6":
|
|
||||||
version "5.13.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.13.6.tgz#6f4bbd98a3a7799a5d2c0726c6ad2a98aa111881"
|
|
||||||
integrity sha512-XS+pOE0xsDODs1zAIbo95A0LKlilvJi8YW0NoXYuq3/jjxGgWDxizZ6Yx0AIIjZOoGsXJOPc0/BcnSEUQ2mFBA==
|
|
||||||
dependencies:
|
|
||||||
"@nuxtjs/proxy" "^2.1.0"
|
|
||||||
axios "^0.21.1"
|
|
||||||
axios-retry "^3.1.9"
|
|
||||||
consola "^2.15.3"
|
|
||||||
defu "^5.0.0"
|
|
||||||
|
|
||||||
"@nuxtjs/proxy@^2.1.0":
|
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@nuxtjs/proxy/-/proxy-2.1.0.tgz#fa7715a11d237fa1273503c4e9e137dd1bf5575b"
|
|
||||||
integrity sha512-/qtoeqXgZ4Mg6LRg/gDUZQrFpOlOdHrol/vQYMnKu3aN3bP90UfOUB3QSDghUUK7OISAJ0xp8Ld78aHyCTcKCQ==
|
|
||||||
dependencies:
|
|
||||||
http-proxy-middleware "^1.0.6"
|
|
||||||
|
|
||||||
"@one-ini/wasm@0.1.1":
|
"@one-ini/wasm@0.1.1":
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323"
|
resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323"
|
||||||
@@ -2958,10 +2940,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.29.tgz#6546d69768c706bebd66bfa73b36aef3d105987a"
|
resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.29.tgz#6546d69768c706bebd66bfa73b36aef3d105987a"
|
||||||
integrity sha512-qBBqfazS9y5VjV7fJDPNXmxd9AP/2uiE05mKFWP41kpbO+tEb62RnUBXCm14XLeScDZQcOuiAKVHMmvCFzF0BA==
|
integrity sha512-qBBqfazS9y5VjV7fJDPNXmxd9AP/2uiE05mKFWP41kpbO+tEb62RnUBXCm14XLeScDZQcOuiAKVHMmvCFzF0BA==
|
||||||
|
|
||||||
"@rancher/shell@3.0.1-rc.1":
|
"@rancher/shell@3.0.1-rc.3":
|
||||||
version "3.0.1-rc.1"
|
version "3.0.1-rc.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.1-rc.1.tgz#db3abc5e5160daffce4e42b113f7d38c025041b2"
|
resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.1-rc.3.tgz#0c6d68ec77c659f954dd1a302c3ef40369b56f37"
|
||||||
integrity sha512-1h245vyOaFXVHPdeSre4laCguXZijeIEQp31Yse0YV1KaHjNM7WZZRvLk3CqASrOtpVEGQ3W/vZO3qWLSQMJ9A==
|
integrity sha512-05jGdQxJHbNmP6VoukhQ1WqY326irGOgrv6oVSm3FoTPST2zHH3F8WbEuHtlG4rhU/siV4MfXPOHE9Zozgw7jQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@aws-sdk/client-ec2" "3.658.1"
|
"@aws-sdk/client-ec2" "3.658.1"
|
||||||
"@aws-sdk/client-eks" "3.1.0"
|
"@aws-sdk/client-eks" "3.1.0"
|
||||||
@@ -2972,7 +2954,6 @@
|
|||||||
"@babel/plugin-proposal-private-property-in-object" "7.14.5"
|
"@babel/plugin-proposal-private-property-in-object" "7.14.5"
|
||||||
"@babel/preset-typescript" "7.16.7"
|
"@babel/preset-typescript" "7.16.7"
|
||||||
"@novnc/novnc" "1.2.0"
|
"@novnc/novnc" "1.2.0"
|
||||||
"@nuxtjs/axios" "5.13.6"
|
|
||||||
"@popperjs/core" "2.4.4"
|
"@popperjs/core" "2.4.4"
|
||||||
"@rancher/icons" "2.0.29"
|
"@rancher/icons" "2.0.29"
|
||||||
"@types/is-url" "1.2.30"
|
"@types/is-url" "1.2.30"
|
||||||
@@ -2987,6 +2968,8 @@
|
|||||||
"@vue/vue3-jest" "^27.0.0-alpha.1"
|
"@vue/vue3-jest" "^27.0.0-alpha.1"
|
||||||
add "2.0.6"
|
add "2.0.6"
|
||||||
ansi_up "5.0.0"
|
ansi_up "5.0.0"
|
||||||
|
axios "0.21.4"
|
||||||
|
axios-retry "3.1.9"
|
||||||
babel-eslint "10.1.0"
|
babel-eslint "10.1.0"
|
||||||
babel-plugin-module-resolver "4.0.0"
|
babel-plugin-module-resolver "4.0.0"
|
||||||
babel-preset-vue "2.0.2"
|
babel-preset-vue "2.0.2"
|
||||||
@@ -3009,6 +2992,7 @@
|
|||||||
d3-selection "1.4.1"
|
d3-selection "1.4.1"
|
||||||
dagre-d3 "0.6.4"
|
dagre-d3 "0.6.4"
|
||||||
dayjs "1.8.29"
|
dayjs "1.8.29"
|
||||||
|
defu "5.0.1"
|
||||||
diff2html "3.4.24"
|
diff2html "3.4.24"
|
||||||
dompurify "2.5.4"
|
dompurify "2.5.4"
|
||||||
element-matches "^0.1.2"
|
element-matches "^0.1.2"
|
||||||
@@ -3698,7 +3682,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
|
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
|
||||||
integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
|
integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
|
||||||
|
|
||||||
"@types/http-proxy@^1.17.5", "@types/http-proxy@^1.17.8":
|
"@types/http-proxy@^1.17.8":
|
||||||
version "1.17.15"
|
version "1.17.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36"
|
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36"
|
||||||
integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==
|
integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==
|
||||||
@@ -5201,15 +5185,14 @@ aws4@^1.8.0:
|
|||||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef"
|
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef"
|
||||||
integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==
|
integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==
|
||||||
|
|
||||||
axios-retry@^3.1.9:
|
axios-retry@3.1.9:
|
||||||
version "3.9.1"
|
version "3.1.9"
|
||||||
resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.9.1.tgz#c8924a8781c8e0a2c5244abf773deb7566b3830d"
|
resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.1.9.tgz#6c30fc9aeb4519aebaec758b90ef56fa03fe72e8"
|
||||||
integrity sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w==
|
integrity sha512-NFCoNIHq8lYkJa6ku4m+V1837TP6lCa7n79Iuf8/AqATAHYB0ISaAS1eyIenDOfHOLtym34W65Sjke2xjg2fsA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.15.4"
|
is-retry-allowed "^1.1.0"
|
||||||
is-retry-allowed "^2.2.0"
|
|
||||||
|
|
||||||
axios@^0.21.1:
|
axios@0.21.4, axios@^0.21.1:
|
||||||
version "0.21.4"
|
version "0.21.4"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
|
||||||
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
|
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
|
||||||
@@ -6110,11 +6093,6 @@ connect-history-api-fallback@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8"
|
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8"
|
||||||
integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
|
integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
|
||||||
|
|
||||||
consola@^2.15.3:
|
|
||||||
version "2.15.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550"
|
|
||||||
integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==
|
|
||||||
|
|
||||||
console-browserify@^1.2.0:
|
console-browserify@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
|
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
|
||||||
@@ -7241,7 +7219,7 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
|
|||||||
has-property-descriptors "^1.0.0"
|
has-property-descriptors "^1.0.0"
|
||||||
object-keys "^1.1.1"
|
object-keys "^1.1.1"
|
||||||
|
|
||||||
defu@^5.0.0:
|
defu@5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/defu/-/defu-5.0.1.tgz#a034278f9b032bf0845d261aa75e9ad98da878ac"
|
resolved "https://registry.yarnpkg.com/defu/-/defu-5.0.1.tgz#a034278f9b032bf0845d261aa75e9ad98da878ac"
|
||||||
integrity sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==
|
integrity sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==
|
||||||
@@ -9039,17 +9017,6 @@ http-proxy-agent@^4.0.1:
|
|||||||
agent-base "6"
|
agent-base "6"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
http-proxy-middleware@^1.0.6:
|
|
||||||
version "1.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665"
|
|
||||||
integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==
|
|
||||||
dependencies:
|
|
||||||
"@types/http-proxy" "^1.17.5"
|
|
||||||
http-proxy "^1.18.1"
|
|
||||||
is-glob "^4.0.1"
|
|
||||||
is-plain-obj "^3.0.0"
|
|
||||||
micromatch "^4.0.2"
|
|
||||||
|
|
||||||
http-proxy-middleware@^2.0.3:
|
http-proxy-middleware@^2.0.3:
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f"
|
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f"
|
||||||
@@ -9474,10 +9441,10 @@ is-regex@^1.1.4:
|
|||||||
call-bind "^1.0.2"
|
call-bind "^1.0.2"
|
||||||
has-tostringtag "^1.0.0"
|
has-tostringtag "^1.0.0"
|
||||||
|
|
||||||
is-retry-allowed@^2.2.0:
|
is-retry-allowed@^1.1.0:
|
||||||
version "2.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d"
|
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
|
||||||
integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==
|
integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
|
||||||
|
|
||||||
is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3:
|
is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user