@rancher/shell 3.0.6 → 3.0.8-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/images/pl/dark/rancher-logo.svg +131 -44
- package/assets/images/pl/rancher-logo.svg +120 -44
- package/assets/images/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_basic.scss +2 -2
- package/assets/styles/base/_color-classic.scss +51 -0
- package/assets/styles/base/_color.scss +3 -3
- package/assets/styles/base/_mixins.scss +1 -1
- package/assets/styles/base/_typography.scss +1 -1
- package/assets/styles/base/_variables-classic.scss +47 -0
- package/assets/styles/global/_button.scss +49 -17
- package/assets/styles/global/_form.scss +1 -1
- package/assets/styles/themes/_dark.scss +4 -0
- package/assets/styles/themes/_light.scss +3 -69
- package/assets/styles/themes/_modern.scss +194 -50
- package/assets/styles/vendor/vue-select.scss +1 -2
- package/assets/translations/en-us.yaml +124 -32
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/ClusterIconMenu.vue +1 -1
- package/components/ClusterProviderIcon.vue +1 -1
- package/components/CodeMirror.vue +1 -1
- package/components/IconOrSvg.vue +40 -29
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/ResourceDetail/index.vue +2 -1
- package/components/SortableTable/index.vue +17 -2
- package/components/SortableTable/sorting.js +3 -1
- package/components/Tabbed/index.vue +5 -5
- package/components/fleet/FleetConfigMapSelector.vue +117 -0
- package/components/fleet/FleetSecretSelector.vue +127 -0
- package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
- package/components/form/FileImageSelector.vue +13 -4
- package/components/form/FileSelector.vue +11 -2
- package/components/form/ResourceLabeledSelect.vue +1 -0
- package/components/form/ResourceTabs/index.vue +37 -18
- package/components/form/SecretSelector.vue +6 -2
- package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/nav/Group.vue +29 -9
- package/components/nav/Header.vue +7 -8
- package/components/nav/NamespaceFilter.vue +1 -1
- package/components/nav/TopLevelMenu.helper.ts +47 -20
- package/components/nav/TopLevelMenu.vue +44 -14
- package/components/nav/Type.vue +0 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
- package/config/pagination-table-headers.js +10 -2
- package/config/product/auth.js +1 -0
- package/config/product/explorer.js +4 -3
- package/config/query-params.js +1 -0
- package/config/settings.ts +8 -1
- package/config/table-headers.js +9 -0
- package/config/types.js +2 -0
- package/core/plugin.ts +18 -6
- package/core/types.ts +8 -0
- package/detail/provisioning.cattle.io.cluster.vue +1 -0
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/dialog/InstallExtensionDialog.vue +71 -45
- package/dialog/UninstallExtensionDialog.vue +2 -1
- package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +94 -65
- package/edit/auth/oidc.vue +86 -16
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/list/projectsecret.vue +1 -1
- package/machine-config/azure.vue +1 -1
- package/mixins/__tests__/chart.test.ts +1 -1
- package/mixins/chart.js +2 -2
- package/models/__tests__/chart.test.ts +17 -9
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +3 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/event.js +7 -0
- package/models/management.cattle.io.authconfig.js +1 -0
- package/models/provisioning.cattle.io.cluster.js +9 -0
- package/package.json +2 -2
- package/pages/auth/login.vue +5 -2
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/chart.vue +2 -2
- package/pages/c/_cluster/explorer/EventsTable.vue +92 -9
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/settings/performance.vue +13 -26
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
- package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
- package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
- package/pages/c/_cluster/uiplugins/index.vue +110 -94
- package/pages/home.vue +313 -12
- package/plugins/__tests__/subscribe.events.test.ts +194 -0
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-store/actions.js +4 -1
- package/plugins/dashboard-store/getters.js +1 -1
- package/plugins/dashboard-store/resource-class.js +20 -5
- package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
- package/plugins/steve/index.js +18 -10
- package/plugins/steve/mutations.js +2 -2
- package/plugins/steve/resourceWatcher.js +2 -2
- package/plugins/steve/steve-pagination-utils.ts +12 -9
- package/plugins/steve/subscribe.js +113 -85
- package/plugins/subscribe-events.ts +211 -0
- package/rancher-components/BadgeState/BadgeState.vue +8 -6
- package/rancher-components/Banner/Banner.vue +2 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
- package/rancher-components/Form/Radio/RadioButton.vue +3 -3
- package/scripts/extension/publish +1 -1
- package/store/auth.js +8 -3
- package/store/aws.js +8 -6
- package/store/features.js +1 -0
- package/store/index.js +21 -25
- package/store/prefs.js +6 -0
- package/types/extension-manager.ts +8 -1
- package/types/kube/kube-api.ts +2 -1
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +52 -23
- package/types/shell/index.d.ts +412 -336
- package/types/store/subscribe-events.types.ts +70 -0
- package/types/store/subscribe.types.ts +6 -22
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/cluster.js +157 -3
- package/utils/dynamic-content/__tests__/config.test.ts +187 -0
- package/utils/dynamic-content/__tests__/index.test.ts +390 -0
- package/utils/dynamic-content/__tests__/info.test.ts +263 -0
- package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
- package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
- package/utils/dynamic-content/__tests__/util.test.ts +235 -0
- package/utils/dynamic-content/config.ts +55 -0
- package/utils/dynamic-content/index.ts +273 -0
- package/utils/dynamic-content/info.ts +219 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +101 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/inactivity.ts +104 -0
- package/utils/pagination-utils.ts +105 -31
- package/utils/pagination-wrapper.ts +6 -8
- package/utils/release-notes.ts +1 -1
- package/utils/sort.js +5 -0
- package/utils/unit-tests/pagination-utils.spec.ts +283 -0
- package/utils/validators/formRules/__tests__/index.test.ts +7 -0
- package/utils/validators/formRules/index.ts +2 -2
package/pages/home.vue
CHANGED
|
@@ -8,9 +8,9 @@ import { BadgeState } from '@components/BadgeState';
|
|
|
8
8
|
import CommunityLinks from '@shell/components/CommunityLinks.vue';
|
|
9
9
|
import SingleClusterInfo from '@shell/components/SingleClusterInfo.vue';
|
|
10
10
|
import { mapGetters, mapState } from 'vuex';
|
|
11
|
-
import { MANAGEMENT, CAPI } from '@shell/config/types';
|
|
11
|
+
import { MANAGEMENT, CAPI, COUNT } from '@shell/config/types';
|
|
12
12
|
import { NAME as MANAGER } from '@shell/config/product/manager';
|
|
13
|
-
import { STATE } from '@shell/config/table-headers';
|
|
13
|
+
import { AGE, STATE } from '@shell/config/table-headers';
|
|
14
14
|
import { MODE, _IMPORT } from '@shell/config/query-params';
|
|
15
15
|
import { createMemoryFormat, formatSi, parseSi, createMemoryValues } from '@shell/utils/units';
|
|
16
16
|
import { markSeenReleaseNotes } from '@shell/utils/version';
|
|
@@ -28,8 +28,12 @@ import { PaginationParamFilter, FilterArgs, PaginationFilterField, PaginationArg
|
|
|
28
28
|
import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
|
|
29
29
|
import { sameContents } from '@shell/utils/array';
|
|
30
30
|
import { PagTableFetchPageSecondaryResourcesOpts, PagTableFetchSecondaryResourcesOpts, PagTableFetchSecondaryResourcesReturns } from '@shell/types/components/paginatedResourceTable';
|
|
31
|
-
import { CURRENT_RANCHER_VERSION } from '@shell/config/version';
|
|
31
|
+
import { CURRENT_RANCHER_VERSION, getVersionData } from '@shell/config/version';
|
|
32
32
|
import { CAPI as CAPI_LAB_AND_ANO } from '@shell/config/labels-annotations';
|
|
33
|
+
import paginationUtils from '@shell/utils/pagination-utils';
|
|
34
|
+
import ResourceTable from '@shell/components/ResourceTable.vue';
|
|
35
|
+
import Preset from '@shell/mixins/preset';
|
|
36
|
+
import { PaginationFeatureHomePageClusterConfig } from '@shell/types/resources/settings';
|
|
33
37
|
|
|
34
38
|
export default defineComponent({
|
|
35
39
|
name: 'Home',
|
|
@@ -42,9 +46,10 @@ export default defineComponent({
|
|
|
42
46
|
CommunityLinks,
|
|
43
47
|
SingleClusterInfo,
|
|
44
48
|
TabTitle,
|
|
49
|
+
ResourceTable,
|
|
45
50
|
},
|
|
46
51
|
|
|
47
|
-
mixins: [PageHeaderActions],
|
|
52
|
+
mixins: [PageHeaderActions, Preset],
|
|
48
53
|
|
|
49
54
|
data() {
|
|
50
55
|
const options = this.$store.getters[`type-map/optionsFor`](CAPI.RANCHER_CLUSTER)?.custom || {};
|
|
@@ -200,15 +205,41 @@ export default defineComponent({
|
|
|
200
205
|
|
|
201
206
|
clusterCount: 0,
|
|
202
207
|
|
|
203
|
-
CURRENT_RANCHER_VERSION
|
|
208
|
+
CURRENT_RANCHER_VERSION,
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* User has decided to disable the alt list
|
|
212
|
+
*/
|
|
213
|
+
altClusterListDisabled: false,
|
|
214
|
+
/**
|
|
215
|
+
* There are too many clusters to show in the home page list.
|
|
216
|
+
*
|
|
217
|
+
* If not disabled, show alt table
|
|
218
|
+
*/
|
|
219
|
+
tooManyClusters: undefined as boolean | undefined,
|
|
220
|
+
altClusterListRows: undefined as any[] | undefined,
|
|
221
|
+
altClusterListFeature: paginationUtils.getFeature<PaginationFeatureHomePageClusterConfig>({ rootGetters: this.$store.getters }, 'homePageCluster'),
|
|
222
|
+
|
|
223
|
+
presetVersion: getVersionData()?.Version,
|
|
204
224
|
};
|
|
205
225
|
},
|
|
206
226
|
|
|
227
|
+
mounted() {
|
|
228
|
+
this.preset('altClusterListDisabled', 'boolean');
|
|
229
|
+
},
|
|
230
|
+
|
|
207
231
|
computed: {
|
|
208
232
|
...mapState(['managementReady']),
|
|
209
233
|
...mapGetters(['currentCluster', 'defaultClusterId']),
|
|
210
234
|
mcm: mapFeature(MULTI_CLUSTER),
|
|
211
235
|
|
|
236
|
+
vaiOnSettingsHeaders() {
|
|
237
|
+
return [
|
|
238
|
+
...this.headers, // include age as we're sorting by it
|
|
239
|
+
AGE
|
|
240
|
+
];
|
|
241
|
+
},
|
|
242
|
+
|
|
212
243
|
canCreateCluster() {
|
|
213
244
|
return !!this.provClusterSchema?.collectionMethods.find((x: string) => x.toLowerCase() === 'post');
|
|
214
245
|
},
|
|
@@ -216,6 +247,21 @@ export default defineComponent({
|
|
|
216
247
|
afterLoginRoute: mapPref(AFTER_LOGIN_ROUTE),
|
|
217
248
|
homePageCards: mapPref(HIDE_HOME_PAGE_CARDS),
|
|
218
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Show the alt table
|
|
252
|
+
*/
|
|
253
|
+
altClusterList() {
|
|
254
|
+
return this.tooManyClusters && !this.altClusterListDisabled;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
watch: {
|
|
260
|
+
async altClusterList(neu) {
|
|
261
|
+
if (neu) {
|
|
262
|
+
await this.initAltClusters();
|
|
263
|
+
}
|
|
264
|
+
},
|
|
219
265
|
},
|
|
220
266
|
|
|
221
267
|
async created() {
|
|
@@ -227,6 +273,12 @@ export default defineComponent({
|
|
|
227
273
|
// If we do not, then if they set the landing page, that won't work unless the release notes are marked read
|
|
228
274
|
// otherwise we always take them to the home page to see the release notes
|
|
229
275
|
markSeenReleaseNotes(this.$store);
|
|
276
|
+
|
|
277
|
+
this.tooManyClusters = this.isTooManyClusters();
|
|
278
|
+
|
|
279
|
+
if (this.altClusterList) {
|
|
280
|
+
await this.initAltClusters();
|
|
281
|
+
}
|
|
230
282
|
},
|
|
231
283
|
|
|
232
284
|
// Forget the types when we leave the page
|
|
@@ -458,7 +510,83 @@ export default defineComponent({
|
|
|
458
510
|
}
|
|
459
511
|
|
|
460
512
|
return pagination;
|
|
461
|
-
}
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
async toggleAltClusterListDisabled(disabled: boolean) {
|
|
516
|
+
// Clear the cache so the table doesn't show the previous mode's results
|
|
517
|
+
await this.$store.dispatch('management/forgetType', CAPI.RANCHER_CLUSTER);
|
|
518
|
+
|
|
519
|
+
this.altClusterListDisabled = disabled;
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Determine if we should use an alternative cluster list which contains most recently created clusters
|
|
524
|
+
*
|
|
525
|
+
* Checks
|
|
526
|
+
* - can view clusters
|
|
527
|
+
* - if vai is on
|
|
528
|
+
* - if alt list feature is on
|
|
529
|
+
* - if cluster count exceeds threshold
|
|
530
|
+
*/
|
|
531
|
+
isTooManyClusters(): boolean {
|
|
532
|
+
if (!this.provClusterSchema || !this.canViewMgmtClusters) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const featureConfig = this.altClusterListFeature;
|
|
537
|
+
|
|
538
|
+
if (!featureConfig || !featureConfig.enabled) { // vai is off, or feature is explicitly disabled
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const threshold = featureConfig.configuration?.threshold;
|
|
543
|
+
|
|
544
|
+
if (threshold === undefined) { // invalid config
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const counts = this.$store.getters[`management/all`](COUNT)?.[0]?.counts || {};
|
|
549
|
+
|
|
550
|
+
this.clusterCount = counts[CAPI.RANCHER_CLUSTER]?.summary.count;
|
|
551
|
+
|
|
552
|
+
return this.clusterCount > threshold;
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Fetch clusters used to populate alt table
|
|
557
|
+
*/
|
|
558
|
+
async initAltClusters() {
|
|
559
|
+
const featureConfig = this.altClusterListFeature;
|
|
560
|
+
const results = featureConfig?.configuration?.results || 50;
|
|
561
|
+
|
|
562
|
+
// Fetch a limited number of provisioning clusters
|
|
563
|
+
const opt1: ActionFindPageArgs = {
|
|
564
|
+
pagination: {
|
|
565
|
+
projectsOrNamespaces: [],
|
|
566
|
+
filters: paginationFilterClusters(this.$store, false),
|
|
567
|
+
page: 1,
|
|
568
|
+
pageSize: results, // We're fetching the total results... then paging locally
|
|
569
|
+
sort: [{ field: 'metadata.creationTimestamp', asc: false }]
|
|
570
|
+
},
|
|
571
|
+
watch: false,
|
|
572
|
+
};
|
|
573
|
+
const provClusters = await this.$store.dispatch('management/findPage', { type: CAPI.RANCHER_CLUSTER, opt: opt1 });
|
|
574
|
+
|
|
575
|
+
// Also fetch the management clusters associated with the provisioning clusters
|
|
576
|
+
const opt2: ActionFindPageArgs = {
|
|
577
|
+
pagination: new FilterArgs({
|
|
578
|
+
filters: PaginationParamFilter.createMultipleFields(provClusters.map((r: any) => new PaginationFilterField({
|
|
579
|
+
field: 'id',
|
|
580
|
+
value: r.mgmtClusterId
|
|
581
|
+
}))),
|
|
582
|
+
}),
|
|
583
|
+
watch: false,
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
await this.$store.dispatch(`management/findPage`, { type: MANAGEMENT.CLUSTER, opt: opt2 });
|
|
587
|
+
|
|
588
|
+
this.altClusterListRows = provClusters;
|
|
589
|
+
},
|
|
462
590
|
}
|
|
463
591
|
});
|
|
464
592
|
|
|
@@ -485,9 +613,164 @@ export default defineComponent({
|
|
|
485
613
|
<IndentedPanel class="mt-20 mb-20">
|
|
486
614
|
<div class="row home-panels">
|
|
487
615
|
<div class="col main-panel">
|
|
488
|
-
<div
|
|
616
|
+
<div
|
|
617
|
+
v-if="altClusterList !== undefined"
|
|
618
|
+
class="row panel"
|
|
619
|
+
>
|
|
620
|
+
<div
|
|
621
|
+
v-if="mcm && altClusterList"
|
|
622
|
+
class="col span-12"
|
|
623
|
+
>
|
|
624
|
+
<ResourceTable
|
|
625
|
+
:schema="provClusterSchema"
|
|
626
|
+
:table-actions="false"
|
|
627
|
+
:row-actions="false"
|
|
628
|
+
key-field="id"
|
|
629
|
+
|
|
630
|
+
:headers="vaiOnSettingsHeaders"
|
|
631
|
+
defaultSortBy="age"
|
|
632
|
+
|
|
633
|
+
:loading="!altClusterListRows"
|
|
634
|
+
|
|
635
|
+
:rows="altClusterListRows || []"
|
|
636
|
+
:rowsPerPage="altClusterListFeature?.configuration.pagesPerRow || 10"
|
|
637
|
+
|
|
638
|
+
:namespaced="false"
|
|
639
|
+
:groupable="false"
|
|
640
|
+
>
|
|
641
|
+
<template #header-left>
|
|
642
|
+
<div class="row table-heading">
|
|
643
|
+
<h1 class="mb-0">
|
|
644
|
+
{{ t('landing.clusters.title') }}
|
|
645
|
+
</h1>
|
|
646
|
+
</div>
|
|
647
|
+
</template>
|
|
648
|
+
<template #sub-header-row>
|
|
649
|
+
<h2 class="too-many-clusters">
|
|
650
|
+
{{ t('landing.clusters.tooMany.showingSome', { rows: altClusterListRows?.length || '...', total: clusterCount}) }}
|
|
651
|
+
<a @click="toggleAltClusterListDisabled(true)">{{ t('landing.clusters.tooMany.showAll') }}</a>
|
|
652
|
+
</h2>
|
|
653
|
+
</template>
|
|
654
|
+
<!--
|
|
655
|
+
Below is a big copy & paste from PaginatedResourceTable, however should be temporary (altClusterList removed in 2.14 once full SSP support for clusters if available)
|
|
656
|
+
-->
|
|
657
|
+
<template
|
|
658
|
+
v-if="canCreateCluster || !!provClusterSchema"
|
|
659
|
+
#header-middle
|
|
660
|
+
>
|
|
661
|
+
<div class="table-heading">
|
|
662
|
+
<router-link
|
|
663
|
+
v-if="!!provClusterSchema"
|
|
664
|
+
:to="manageLocation"
|
|
665
|
+
class="btn btn-sm role-secondary"
|
|
666
|
+
data-testid="cluster-management-manage-button"
|
|
667
|
+
role="button"
|
|
668
|
+
:aria-label="t('cluster.manageAction')"
|
|
669
|
+
@keyup.space="$router.push(manageLocation)"
|
|
670
|
+
>
|
|
671
|
+
{{ t('cluster.manageAction') }}
|
|
672
|
+
</router-link>
|
|
673
|
+
<router-link
|
|
674
|
+
v-if="canCreateCluster"
|
|
675
|
+
:to="importLocation"
|
|
676
|
+
class="btn btn-sm role-primary"
|
|
677
|
+
data-testid="cluster-create-import-button"
|
|
678
|
+
role="button"
|
|
679
|
+
:aria-label="t('cluster.importAction')"
|
|
680
|
+
@keyup.space="$router.push(importLocation)"
|
|
681
|
+
>
|
|
682
|
+
{{ t('cluster.importAction') }}
|
|
683
|
+
</router-link>
|
|
684
|
+
<router-link
|
|
685
|
+
v-if="canCreateCluster"
|
|
686
|
+
:to="createLocation"
|
|
687
|
+
class="btn btn-sm role-primary"
|
|
688
|
+
data-testid="cluster-create-button"
|
|
689
|
+
role="button"
|
|
690
|
+
:aria-label="t('generic.create')"
|
|
691
|
+
@keyup.space="$router.push(createLocation)"
|
|
692
|
+
>
|
|
693
|
+
{{ t('generic.create') }}
|
|
694
|
+
</router-link>
|
|
695
|
+
</div>
|
|
696
|
+
</template>
|
|
697
|
+
<template #col:name="{row}">
|
|
698
|
+
<td class="col-name">
|
|
699
|
+
<div class="list-cluster-name">
|
|
700
|
+
<p
|
|
701
|
+
v-if="row.mgmt"
|
|
702
|
+
class="cluster-name"
|
|
703
|
+
>
|
|
704
|
+
<router-link
|
|
705
|
+
v-if="row.mgmt.isReady && !row.hasError"
|
|
706
|
+
:to="{ name: 'c-cluster-explorer', params: { cluster: row.mgmt.id }}"
|
|
707
|
+
role="link"
|
|
708
|
+
:aria-label="row.nameDisplay"
|
|
709
|
+
>
|
|
710
|
+
{{ row.nameDisplay }}
|
|
711
|
+
</router-link>
|
|
712
|
+
<span v-else>{{ row.nameDisplay }}</span>
|
|
713
|
+
<i
|
|
714
|
+
v-if="row.unavailableMachines"
|
|
715
|
+
v-clean-tooltip="row.unavailableMachines"
|
|
716
|
+
class="conditions-alert-icon icon-alert icon"
|
|
717
|
+
/>
|
|
718
|
+
<i
|
|
719
|
+
v-if="row.isRke1"
|
|
720
|
+
v-clean-tooltip="t('cluster.rke1Unsupported')"
|
|
721
|
+
class="rke1-unsupported-icon icon-warning icon"
|
|
722
|
+
/>
|
|
723
|
+
</p>
|
|
724
|
+
<p
|
|
725
|
+
v-if="row.description"
|
|
726
|
+
class="cluster-description"
|
|
727
|
+
>
|
|
728
|
+
{{ row.description }}
|
|
729
|
+
</p>
|
|
730
|
+
</div>
|
|
731
|
+
</td>
|
|
732
|
+
</template>
|
|
733
|
+
<template #col:kubernetesVersion="{row}">
|
|
734
|
+
<td class="col-name">
|
|
735
|
+
<span>
|
|
736
|
+
{{ row.kubernetesVersion }}
|
|
737
|
+
</span>
|
|
738
|
+
<div
|
|
739
|
+
v-clean-tooltip="{content: row.architecture.tooltip, placement: 'left'}"
|
|
740
|
+
class="text-muted"
|
|
741
|
+
>
|
|
742
|
+
{{ row.architecture.label }}
|
|
743
|
+
</div>
|
|
744
|
+
</td>
|
|
745
|
+
</template>
|
|
746
|
+
<template #col:cpu="{row}">
|
|
747
|
+
<td v-if="row.mgmt && cpuAllocatable(row.mgmt)">
|
|
748
|
+
{{ `${cpuAllocatable(row.mgmt)} ${t('landing.clusters.cores', {count:cpuAllocatable(row.mgmt) })}` }}
|
|
749
|
+
</td>
|
|
750
|
+
<td v-else>
|
|
751
|
+
—
|
|
752
|
+
</td>
|
|
753
|
+
</template>
|
|
754
|
+
<template #col:memory="{row}">
|
|
755
|
+
<td v-if="row.mgmt && memoryAllocatable(row.mgmt) && !memoryAllocatable(row.mgmt).match(/^0 [a-zA-z]/)">
|
|
756
|
+
{{ memoryAllocatable(row.mgmt) }}
|
|
757
|
+
</td>
|
|
758
|
+
<td v-else>
|
|
759
|
+
—
|
|
760
|
+
</td>
|
|
761
|
+
</template>
|
|
762
|
+
<!-- <template #cell:explorer="{row}">
|
|
763
|
+
<router-link v-if="row && row.isReady" class="btn btn-sm role-primary" :to="{name: 'c-cluster', params: {cluster: row.id}}">
|
|
764
|
+
{{ t('landing.clusters.explore') }}
|
|
765
|
+
</router-link>
|
|
766
|
+
<button v-else :disabled="true" class="btn btn-sm role-primary">
|
|
767
|
+
{{ t('landing.clusters.explore') }}
|
|
768
|
+
</button>
|
|
769
|
+
</template> -->
|
|
770
|
+
</ResourceTable>
|
|
771
|
+
</div>
|
|
489
772
|
<div
|
|
490
|
-
v-if="mcm"
|
|
773
|
+
v-else-if="mcm"
|
|
491
774
|
class="col span-12"
|
|
492
775
|
>
|
|
493
776
|
<PaginatedResourceTable
|
|
@@ -511,16 +794,25 @@ export default defineComponent({
|
|
|
511
794
|
>
|
|
512
795
|
<template #header-left>
|
|
513
796
|
<div class="row table-heading">
|
|
514
|
-
<
|
|
797
|
+
<h1 class="mb-0">
|
|
515
798
|
{{ t('landing.clusters.title') }}
|
|
516
|
-
</
|
|
799
|
+
</h1>
|
|
517
800
|
<BadgeState
|
|
518
|
-
v-if="clusterCount"
|
|
801
|
+
v-if="clusterCount && !tooManyClusters"
|
|
519
802
|
:label="clusterCount.toString()"
|
|
520
|
-
color="
|
|
803
|
+
color="bg-info ml-20 mr-20"
|
|
521
804
|
/>
|
|
522
805
|
</div>
|
|
523
806
|
</template>
|
|
807
|
+
<template
|
|
808
|
+
v-if="tooManyClusters"
|
|
809
|
+
#sub-header-row
|
|
810
|
+
>
|
|
811
|
+
<h2 class="too-many-clusters">
|
|
812
|
+
{{ t('landing.clusters.tooMany.showingAll', { rows: altClusterListRows?.length || '...', total: clusterCount}) }}
|
|
813
|
+
<a @click="toggleAltClusterListDisabled(false)">{{ t('landing.clusters.tooMany.showSome') }}</a>
|
|
814
|
+
</h2>
|
|
815
|
+
</template>
|
|
524
816
|
<template
|
|
525
817
|
v-if="canCreateCluster || !!provClusterSchema"
|
|
526
818
|
#header-middle
|
|
@@ -663,6 +955,14 @@ export default defineComponent({
|
|
|
663
955
|
}
|
|
664
956
|
.main-panel {
|
|
665
957
|
flex: auto;
|
|
958
|
+
|
|
959
|
+
.too-many-clusters {
|
|
960
|
+
margin-bottom: 5px;
|
|
961
|
+
|
|
962
|
+
a {
|
|
963
|
+
cursor: pointer;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
666
966
|
}
|
|
667
967
|
|
|
668
968
|
.side-panel {
|
|
@@ -744,6 +1044,7 @@ export default defineComponent({
|
|
|
744
1044
|
.search {
|
|
745
1045
|
align-items: center;
|
|
746
1046
|
display: flex;
|
|
1047
|
+
height: 39px;
|
|
747
1048
|
|
|
748
1049
|
> INPUT {
|
|
749
1050
|
background-color: transparent;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { SteveWatchEventListenerManager } from '@shell/plugins/subscribe-events';
|
|
2
|
+
import { STEVE_WATCH_EVENT_TYPES } from '@shell/types/store/subscribe.types';
|
|
3
|
+
|
|
4
|
+
// Mock function to create a consistent key for testing
|
|
5
|
+
const mockKeyForSubscribe = jest.fn(({
|
|
6
|
+
params: {
|
|
7
|
+
type, name, id, selector, mode
|
|
8
|
+
}
|
|
9
|
+
}) => {
|
|
10
|
+
return `${ type }-${ name }-${ id }-${ selector }-${ mode }`;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// Mock parameters and callbacks
|
|
14
|
+
const mockParams1 = {
|
|
15
|
+
type: 'pods', name: 'my-pod', id: 'abc-123', selector: 'app=test'
|
|
16
|
+
};
|
|
17
|
+
const mockCallback1 = jest.fn();
|
|
18
|
+
const mockCallback2 = jest.fn();
|
|
19
|
+
|
|
20
|
+
// The class under test
|
|
21
|
+
let manager: SteveWatchEventListenerManager;
|
|
22
|
+
|
|
23
|
+
describe('steveWatchEventListenerManager', () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
// Reset the manager and mocks before each test
|
|
26
|
+
manager = new SteveWatchEventListenerManager();
|
|
27
|
+
jest.clearAllMocks();
|
|
28
|
+
// Replace the internal keyForSubscribe with our mock
|
|
29
|
+
(manager as any).keyForSubscribe = mockKeyForSubscribe;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('initialization and Properties', () => {
|
|
33
|
+
it('should be created successfully', () => {
|
|
34
|
+
expect(manager).toBeInstanceOf(SteveWatchEventListenerManager);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should have a supportedEventTypes array with STEVE_WATCH_EVENT_TYPES.CHANGES', () => {
|
|
38
|
+
expect(manager.supportedEventTypes).toStrictEqual([STEVE_WATCH_EVENT_TYPES.CHANGES]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should correctly identify a supported event type', () => {
|
|
42
|
+
const isSupported = manager.isSupportedEventType(STEVE_WATCH_EVENT_TYPES.CHANGES);
|
|
43
|
+
|
|
44
|
+
expect(isSupported).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should correctly identify an unsupported event type', () => {
|
|
48
|
+
const isSupported = manager.isSupportedEventType('some.other.event' as STEVE_WATCH_EVENT_TYPES);
|
|
49
|
+
|
|
50
|
+
expect(isSupported).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('watch Management', () => {
|
|
55
|
+
it('should return undefined when getting a non-existent watch', () => {
|
|
56
|
+
const watch = manager.getWatch({ params: mockParams1 });
|
|
57
|
+
|
|
58
|
+
expect(watch).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should create a watch when setStandardWatch is called with standardWatch true and no watch exists', () => {
|
|
62
|
+
manager.setStandardWatch({ standardWatch: true, args: { params: mockParams1 } });
|
|
63
|
+
const watch = (manager as any).watches[mockKeyForSubscribe({ params: mockParams1 })];
|
|
64
|
+
|
|
65
|
+
expect(watch).toBeDefined();
|
|
66
|
+
expect(watch.hasStandardWatch).toBe(true);
|
|
67
|
+
expect(watch.listeners).toStrictEqual([]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should not create a watch when setStandardWatch is called with standardWatch false and no watch exists', () => {
|
|
71
|
+
manager.setStandardWatch({ standardWatch: false, args: { params: mockParams1 } });
|
|
72
|
+
const watch = (manager as any).watches[mockKeyForSubscribe({ params: mockParams1 })];
|
|
73
|
+
|
|
74
|
+
expect(watch).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should delete a watch when hasStandardWatch becomes false and there are no listeners', () => {
|
|
78
|
+
manager.setStandardWatch({ standardWatch: true, args: { params: mockParams1 } });
|
|
79
|
+
manager.setStandardWatch({ standardWatch: false, args: { params: mockParams1 } });
|
|
80
|
+
const watch = (manager as any).watches[mockKeyForSubscribe({ params: mockParams1 })];
|
|
81
|
+
|
|
82
|
+
expect(watch).toBeUndefined();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('listener and Callback Management', () => {
|
|
87
|
+
it('should add a new listener and a callback', () => {
|
|
88
|
+
const listener = manager.addEventListenerCallback({
|
|
89
|
+
callback: mockCallback1,
|
|
90
|
+
args: {
|
|
91
|
+
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
92
|
+
params: mockParams1,
|
|
93
|
+
id: 'cb-1'
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(listener).toBeDefined();
|
|
98
|
+
expect(listener.event).toBe(STEVE_WATCH_EVENT_TYPES.CHANGES);
|
|
99
|
+
expect(listener.callbacks['cb-1']).toBe(mockCallback1);
|
|
100
|
+
const watch = manager.getWatch({ params: mockParams1 });
|
|
101
|
+
|
|
102
|
+
expect(watch?.listeners.length).toBe(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should add a second callback to an existing listener', () => {
|
|
106
|
+
manager.addEventListenerCallback({
|
|
107
|
+
callback: mockCallback1,
|
|
108
|
+
args: {
|
|
109
|
+
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
110
|
+
params: mockParams1,
|
|
111
|
+
id: 'cb-1'
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
manager.addEventListenerCallback({
|
|
115
|
+
callback: mockCallback2,
|
|
116
|
+
args: {
|
|
117
|
+
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
118
|
+
params: mockParams1,
|
|
119
|
+
id: 'cb-2'
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const listener = manager.getEventListener({ args: { event: STEVE_WATCH_EVENT_TYPES.CHANGES, params: mockParams1 } });
|
|
124
|
+
|
|
125
|
+
expect(Object.keys(listener?.callbacks || {})).toHaveLength(2);
|
|
126
|
+
expect(listener?.callbacks['cb-2']).toBe(mockCallback2);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should trigger a specific event listener and its callbacks', () => {
|
|
130
|
+
manager.addEventListenerCallback({
|
|
131
|
+
callback: mockCallback1,
|
|
132
|
+
args: {
|
|
133
|
+
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
134
|
+
params: mockParams1,
|
|
135
|
+
id: 'cb-1'
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
manager.addEventListenerCallback({
|
|
139
|
+
callback: mockCallback2,
|
|
140
|
+
args: {
|
|
141
|
+
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
142
|
+
params: mockParams1,
|
|
143
|
+
id: 'cb-2'
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
manager.triggerEventListener({ event: STEVE_WATCH_EVENT_TYPES.CHANGES, params: mockParams1 });
|
|
148
|
+
expect(mockCallback1).toHaveBeenCalledTimes(1);
|
|
149
|
+
expect(mockCallback2).toHaveBeenCalledTimes(1);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should remove a specific callback from a listener', () => {
|
|
153
|
+
manager.addEventListenerCallback({
|
|
154
|
+
callback: mockCallback1,
|
|
155
|
+
args: {
|
|
156
|
+
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
157
|
+
params: mockParams1,
|
|
158
|
+
id: 'cb-1'
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
manager.removeEventListenerCallback({
|
|
162
|
+
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
163
|
+
params: mockParams1,
|
|
164
|
+
id: 'cb-1'
|
|
165
|
+
});
|
|
166
|
+
const listener = manager.getEventListener({ args: { event: STEVE_WATCH_EVENT_TYPES.CHANGES, params: mockParams1 } });
|
|
167
|
+
|
|
168
|
+
expect(listener?.callbacks['cb-1']).toBeUndefined();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should trigger all callbacks for a given watch', () => {
|
|
172
|
+
manager.addEventListenerCallback({
|
|
173
|
+
callback: mockCallback1,
|
|
174
|
+
args: {
|
|
175
|
+
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
176
|
+
params: mockParams1,
|
|
177
|
+
id: 'cb-1'
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
manager.addEventListenerCallback({
|
|
181
|
+
callback: mockCallback2,
|
|
182
|
+
args: {
|
|
183
|
+
event: 'another.event' as STEVE_WATCH_EVENT_TYPES,
|
|
184
|
+
params: mockParams1,
|
|
185
|
+
id: 'cb-2'
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
manager.triggerAllEventListeners({ params: mockParams1 });
|
|
190
|
+
expect(mockCallback1).toHaveBeenCalledTimes(1);
|
|
191
|
+
expect(mockCallback2).toHaveBeenCalledTimes(1);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
package/plugins/axios.js
CHANGED
|
@@ -11,7 +11,8 @@ export default function({
|
|
|
11
11
|
const options = { parseJSON: false };
|
|
12
12
|
const csrf = store.getters['cookies/get']({ key: CSRF, options });
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
// Request can ask not to send the CSRF header
|
|
15
|
+
if (csrf && !config.noApiCsrf) {
|
|
15
16
|
config.headers['x-api-csrf'] = csrf;
|
|
16
17
|
}
|
|
17
18
|
});
|
|
@@ -220,6 +220,9 @@ export default {
|
|
|
220
220
|
)
|
|
221
221
|
) {
|
|
222
222
|
if (opt.watch !== false ) {
|
|
223
|
+
// Note - Empty revision here seems broken
|
|
224
|
+
// - list page (watch all) --> detail page (stop watch all, watch one) --> list page (watch all - no revision)
|
|
225
|
+
// - the missing revision means watch start from now... instead of the point the clusters were last monitored (cache contains stale data)
|
|
223
226
|
const args = {
|
|
224
227
|
type,
|
|
225
228
|
revision: '',
|
|
@@ -695,7 +698,7 @@ export default {
|
|
|
695
698
|
|
|
696
699
|
const res = await dispatch('request', { opt, type });
|
|
697
700
|
|
|
698
|
-
await dispatch('load', { data: res });
|
|
701
|
+
await dispatch('load', { data: res, invalidatePageCache: opt.invalidatePageCache });
|
|
699
702
|
|
|
700
703
|
if ( opt.watch !== false ) {
|
|
701
704
|
dispatch('watch', createFindWatchArg({
|
|
@@ -526,7 +526,7 @@ export default {
|
|
|
526
526
|
const store = state.config.namespace;
|
|
527
527
|
const resource = id || context ? { id, context } : null;
|
|
528
528
|
|
|
529
|
-
return paginationUtils.isEnabled({ rootGetters }, { store, resource });
|
|
529
|
+
return paginationUtils.isEnabled({ rootGetters, $plugin: rootState.$plugin }, { store, resource });
|
|
530
530
|
},
|
|
531
531
|
|
|
532
532
|
/**
|