@rancher/shell 3.0.8-rc.7 → 3.0.8-rc.8
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/components/Drawer/Chrome.vue +2 -6
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +3 -9
- package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
- package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
- package/components/Drawer/ResourceDetailDrawer/index.vue +3 -9
- package/components/Drawer/ResourceDetailDrawer/types.ts +16 -0
- package/components/Drawer/types.ts +3 -0
- package/components/PaginatedResourceTable.vue +2 -6
- package/components/Resource/Detail/Metadata/composables.ts +9 -9
- package/components/Resource/Detail/TitleBar/composables.ts +2 -1
- package/components/Resource/Detail/composables.ts +12 -0
- package/components/nav/Header.vue +1 -2
- package/components/nav/TopLevelMenu.helper.ts +16 -6
- package/machine-config/components/EC2Networking.vue +5 -2
- package/machine-config/components/__tests__/EC2Networking.test.ts +24 -0
- package/mixins/__tests__/chart.test.ts +21 -0
- package/mixins/chart.js +7 -1
- package/package.json +1 -1
- package/pages/home.vue +5 -2
- package/pkg/dynamic-importer.lib.js +4 -0
- package/plugins/dashboard-store/resource-class.js +1 -2
- package/plugins/steve/subscribe.js +17 -9
- package/plugins/subscribe-events.ts +4 -2
- package/store/index.js +32 -13
- package/store/type-map.js +3 -3
- package/types/shell/index.d.ts +1 -0
- package/types/store/subscribe-events.types.ts +8 -1
- package/types/store/subscribe.types.ts +1 -0
- package/utils/__tests__/version.test.ts +19 -1
- package/utils/back-off.ts +3 -3
- package/utils/dynamic-content/__tests__/info.test.ts +15 -9
- package/utils/dynamic-content/info.ts +1 -2
- package/utils/pagination-wrapper.ts +12 -8
- package/utils/version.js +15 -0
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script setup lang="ts">
|
|
2
2
|
import { useI18n } from '@shell/composables/useI18n';
|
|
3
3
|
import { useStore } from 'vuex';
|
|
4
4
|
import { computed } from 'vue';
|
|
5
|
-
|
|
6
|
-
ariaTarget: string;
|
|
7
|
-
}
|
|
8
|
-
</script>
|
|
5
|
+
import { Props } from './types';
|
|
9
6
|
|
|
10
|
-
<script setup lang="ts">
|
|
11
7
|
const props = defineProps<Props>();
|
|
12
8
|
const emit = defineEmits(['close']);
|
|
13
9
|
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script setup lang="ts">
|
|
2
2
|
import { useI18n } from '@shell/composables/useI18n';
|
|
3
3
|
import { _VIEW } from '@shell/config/query-params';
|
|
4
4
|
import { useStore } from 'vuex';
|
|
5
5
|
import Tab from '@shell/components/Tabbed/Tab.vue';
|
|
6
|
+
import { ConfigProps } from '@shell/components/Drawer/ResourceDetailDrawer/types';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
resource: any;
|
|
9
|
-
component: any;
|
|
10
|
-
resourceType: string;
|
|
11
|
-
}
|
|
12
|
-
</script>
|
|
13
|
-
<script setup lang="ts">
|
|
14
|
-
const props = defineProps<Props>();
|
|
8
|
+
const props = defineProps<ConfigProps>();
|
|
15
9
|
const store = useStore();
|
|
16
10
|
const i18n = useI18n(store);
|
|
17
11
|
</script>
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script setup lang="ts">
|
|
2
2
|
import { useI18n } from '@shell/composables/useI18n';
|
|
3
3
|
import { _VIEW } from '@shell/config/query-params';
|
|
4
4
|
import { useStore } from 'vuex';
|
|
5
5
|
import Tab from '@shell/components/Tabbed/Tab.vue';
|
|
6
6
|
import { useTemplateRef } from 'vue';
|
|
7
7
|
import ResourceYaml from '@shell/components/ResourceYaml.vue';
|
|
8
|
+
import { YamlProps } from '@shell/components/Drawer/ResourceDetailDrawer/types';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
resource: any;
|
|
11
|
-
yaml: string;
|
|
12
|
-
}
|
|
13
|
-
</script>
|
|
14
|
-
<script setup lang="ts">
|
|
15
|
-
const props = defineProps<Props>();
|
|
10
|
+
const props = defineProps<YamlProps>();
|
|
16
11
|
const store = useStore();
|
|
17
12
|
const i18n = useI18n(store);
|
|
18
13
|
const yamlComponent: any = useTemplateRef('yaml');
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { Props as YamlTabProps } from '@shell/components/Drawer/ResourceDetailDrawer/YamlTab.vue';
|
|
2
|
-
import { Props as ConfigTabProps } from '@shell/components/Drawer/ResourceDetailDrawer/ConfigTab.vue';
|
|
3
1
|
import { useStore } from 'vuex';
|
|
4
2
|
import { getYaml } from '@shell/components/Drawer/ResourceDetailDrawer/helpers';
|
|
3
|
+
import { ConfigProps, YamlProps } from '@shell/components/Drawer/ResourceDetailDrawer/types';
|
|
5
4
|
|
|
6
|
-
export async function useDefaultYamlTabProps(resource: any): Promise<
|
|
5
|
+
export async function useDefaultYamlTabProps(resource: any): Promise<YamlProps> {
|
|
7
6
|
const yaml = await getYaml(resource);
|
|
8
7
|
|
|
9
8
|
return {
|
|
@@ -12,7 +11,7 @@ export async function useDefaultYamlTabProps(resource: any): Promise<YamlTabProp
|
|
|
12
11
|
};
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
export function useDefaultConfigTabProps(resource: any):
|
|
14
|
+
export function useDefaultConfigTabProps(resource: any): ConfigProps | undefined {
|
|
16
15
|
const store = useStore();
|
|
17
16
|
|
|
18
17
|
// You don't want to show the Config tab if there isn't a an edit page to show and you don't want to show it if there isn't
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script setup lang="ts">
|
|
2
2
|
import Drawer from '@shell/components/Drawer/Chrome.vue';
|
|
3
3
|
import { useI18n } from '@shell/composables/useI18n';
|
|
4
4
|
import { useStore } from 'vuex';
|
|
@@ -9,17 +9,11 @@ import ConfigTab from '@shell/components/Drawer/ResourceDetailDrawer/ConfigTab.v
|
|
|
9
9
|
import { computed, ref } from 'vue';
|
|
10
10
|
import RcButton from '@components/RcButton/RcButton.vue';
|
|
11
11
|
import StateDot from '@shell/components/StateDot/index.vue';
|
|
12
|
+
import { ResourceDetailDrawerProps } from '@shell/components/Drawer/ResourceDetailDrawer/types';
|
|
12
13
|
|
|
13
|
-
export interface Props {
|
|
14
|
-
resource: any;
|
|
15
|
-
|
|
16
|
-
onClose?: () => void;
|
|
17
|
-
}
|
|
18
|
-
</script>
|
|
19
|
-
<script setup lang="ts">
|
|
20
14
|
const editBttnDataTestId = 'save-configuration-bttn';
|
|
21
15
|
const componentTestid = 'configuration-drawer-tabbed';
|
|
22
|
-
const props = defineProps<
|
|
16
|
+
const props = defineProps<ResourceDetailDrawerProps>();
|
|
23
17
|
const emit = defineEmits(['close']);
|
|
24
18
|
const store = useStore();
|
|
25
19
|
const i18n = useI18n(store);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface YamlProps {
|
|
2
|
+
resource: any;
|
|
3
|
+
yaml: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ConfigProps {
|
|
7
|
+
resource: any;
|
|
8
|
+
component: any;
|
|
9
|
+
resourceType: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ResourceDetailDrawerProps {
|
|
13
|
+
resource: any;
|
|
14
|
+
|
|
15
|
+
onClose?: () => void;
|
|
16
|
+
}
|
|
@@ -116,15 +116,11 @@ export default defineComponent({
|
|
|
116
116
|
},
|
|
117
117
|
|
|
118
118
|
async fetch() {
|
|
119
|
-
const promises = [
|
|
120
|
-
this.$fetchType(this.resource, [], this.overrideInStore || this.inStore),
|
|
121
|
-
];
|
|
122
|
-
|
|
123
119
|
if (this.fetchSecondaryResources) {
|
|
124
|
-
|
|
120
|
+
await this.fetchSecondaryResources({ canPaginate: this.canPaginate });
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
await
|
|
123
|
+
await this.$fetchType(this.resource, [], this.overrideInStore || this.inStore);
|
|
128
124
|
},
|
|
129
125
|
|
|
130
126
|
computed: {
|
|
@@ -6,18 +6,19 @@ import { computed, toValue, Ref } from 'vue';
|
|
|
6
6
|
import {
|
|
7
7
|
useLiveDate, useNamespace, useProject, useResourceDetails, useWorkspace
|
|
8
8
|
} from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields';
|
|
9
|
+
import { useOnShowConfiguration } from '@shell/components/Resource/Detail/composables';
|
|
9
10
|
|
|
10
11
|
export const useBasicMetadata = (resource: any) => {
|
|
11
12
|
const labels = useDefaultLabels(resource);
|
|
12
13
|
const annotations = useDefaultAnnotations(resource);
|
|
13
|
-
const
|
|
14
|
+
const onShowConfiguration = useOnShowConfiguration(resource);
|
|
14
15
|
|
|
15
16
|
return computed(() => {
|
|
16
17
|
return {
|
|
17
|
-
resource:
|
|
18
|
-
labels:
|
|
19
|
-
annotations:
|
|
20
|
-
onShowConfiguration
|
|
18
|
+
resource: toValue(resource),
|
|
19
|
+
labels: labels.value,
|
|
20
|
+
annotations: annotations.value,
|
|
21
|
+
onShowConfiguration
|
|
21
22
|
};
|
|
22
23
|
});
|
|
23
24
|
};
|
|
@@ -28,7 +29,7 @@ export const useDefaultMetadataProps = (resource: any, additionalIdentifyingInfo
|
|
|
28
29
|
|
|
29
30
|
const identifyingInformation = computed(() => [...defaultIdentifyingInformation.value, ...(additionalIdentifyingInformationValue || [])]);
|
|
30
31
|
const basicMetaData = useBasicMetadata(resource);
|
|
31
|
-
const
|
|
32
|
+
const onShowConfiguration = useOnShowConfiguration(resource);
|
|
32
33
|
|
|
33
34
|
return computed(() => {
|
|
34
35
|
return {
|
|
@@ -36,7 +37,7 @@ export const useDefaultMetadataProps = (resource: any, additionalIdentifyingInfo
|
|
|
36
37
|
identifyingInformation: identifyingInformation.value,
|
|
37
38
|
labels: basicMetaData.value.labels,
|
|
38
39
|
annotations: basicMetaData.value.annotations,
|
|
39
|
-
onShowConfiguration
|
|
40
|
+
onShowConfiguration
|
|
40
41
|
};
|
|
41
42
|
});
|
|
42
43
|
};
|
|
@@ -47,7 +48,6 @@ export const useDefaultMetadataForLegacyPagesProps = (resource: any) => {
|
|
|
47
48
|
const workspace = useWorkspace(resource);
|
|
48
49
|
const namespace = useNamespace(resource);
|
|
49
50
|
const liveDate = useLiveDate(resource);
|
|
50
|
-
const resourceValue = toValue(resource);
|
|
51
51
|
|
|
52
52
|
const identifyingInformation = computed((): IdentifyingInformationRow[] => {
|
|
53
53
|
const defaultInfo = [
|
|
@@ -71,7 +71,7 @@ export const useDefaultMetadataForLegacyPagesProps = (resource: any) => {
|
|
|
71
71
|
identifyingInformation: identifyingInformation.value,
|
|
72
72
|
labels: basicMetaData.value.labels,
|
|
73
73
|
annotations: basicMetaData.value.annotations,
|
|
74
|
-
onShowConfiguration:
|
|
74
|
+
onShowConfiguration: basicMetaData.value.onShowConfiguration
|
|
75
75
|
};
|
|
76
76
|
});
|
|
77
77
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useOnShowConfiguration } from '@shell/components/Resource/Detail/composables';
|
|
1
2
|
import { TitleBarProps } from '@shell/components/Resource/Detail/TitleBar/index.vue';
|
|
2
3
|
import { computed, Ref, toValue } from 'vue';
|
|
3
4
|
import { useRoute } from 'vue-router';
|
|
@@ -23,7 +24,7 @@ export const useDefaultTitleBarProps = (resource: any, resourceSubtype?: Ref<str
|
|
|
23
24
|
resource: resourceValue.type
|
|
24
25
|
}
|
|
25
26
|
};
|
|
26
|
-
const onShowConfiguration = resourceValue.disableResourceDetailDrawer ? undefined : (
|
|
27
|
+
const onShowConfiguration = resourceValue.disableResourceDetailDrawer ? undefined : useOnShowConfiguration(resource);
|
|
27
28
|
|
|
28
29
|
return {
|
|
29
30
|
resource: resourceValue,
|
|
@@ -2,6 +2,7 @@ import { computed, Ref, toValue } from 'vue';
|
|
|
2
2
|
import { useStore } from 'vuex';
|
|
3
3
|
import { Props as BannerProps } from '@components/Banner/Banner.vue';
|
|
4
4
|
import { useI18n } from '@shell/composables/useI18n';
|
|
5
|
+
import ResourceClass from '@shell/plugins/dashboard-store/resource-class';
|
|
5
6
|
|
|
6
7
|
export const useResourceDetailBannerProps = (resource: any): Ref<BannerProps | undefined> => {
|
|
7
8
|
const store = useStore();
|
|
@@ -43,3 +44,14 @@ export const useResourceDetailBannerProps = (resource: any): Ref<BannerProps | u
|
|
|
43
44
|
return undefined;
|
|
44
45
|
});
|
|
45
46
|
};
|
|
47
|
+
|
|
48
|
+
export const useOnShowConfiguration = (resource: any) => {
|
|
49
|
+
return (returnFocusSelector?: string) => {
|
|
50
|
+
const resourceValue = toValue(resource);
|
|
51
|
+
// Because extensions can make a copy of the resource-class it's possible that an extension will have a resource-class which predates the inclusion of showConfiguration
|
|
52
|
+
// to still the rest of shell to consume
|
|
53
|
+
const showConfiguration = resourceValue.showConfiguration ? resourceValue.showConfiguration.bind(resourceValue) : ResourceClass.prototype.showConfiguration.bind(resourceValue);
|
|
54
|
+
|
|
55
|
+
showConfiguration(returnFocusSelector);
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -97,7 +97,6 @@ export default {
|
|
|
97
97
|
'isSingleProduct',
|
|
98
98
|
'isRancherInHarvester',
|
|
99
99
|
'showTopLevelMenu',
|
|
100
|
-
'isMultiCluster',
|
|
101
100
|
'showWorkspaceSwitcher'
|
|
102
101
|
]),
|
|
103
102
|
|
|
@@ -424,7 +423,7 @@ export default {
|
|
|
424
423
|
data-testid="header"
|
|
425
424
|
>
|
|
426
425
|
<div>
|
|
427
|
-
<TopLevelMenu v-if="
|
|
426
|
+
<TopLevelMenu v-if="showTopLevelMenu" />
|
|
428
427
|
</div>
|
|
429
428
|
|
|
430
429
|
<div
|
|
@@ -28,6 +28,7 @@ interface UpdateArgs {
|
|
|
28
28
|
searchTerm: string,
|
|
29
29
|
pinnedIds: string[],
|
|
30
30
|
unPinnedMax?: number,
|
|
31
|
+
forceWatch?: boolean
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
type MgmtCluster = {
|
|
@@ -192,9 +193,12 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
192
193
|
this.clustersOthersWrapper = new PaginationWrapper({
|
|
193
194
|
$store,
|
|
194
195
|
id: 'tlm-unpinned-clusters',
|
|
195
|
-
onChange: async() => {
|
|
196
|
+
onChange: async({ forceWatch }) => {
|
|
196
197
|
if (this.args) {
|
|
197
|
-
await this.update(
|
|
198
|
+
await this.update({
|
|
199
|
+
...this.args,
|
|
200
|
+
forceWatch
|
|
201
|
+
});
|
|
198
202
|
}
|
|
199
203
|
},
|
|
200
204
|
enabledFor: {
|
|
@@ -210,9 +214,12 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
210
214
|
this.provClusterWrapper = new PaginationWrapper({
|
|
211
215
|
$store,
|
|
212
216
|
id: 'tlm-prov-clusters',
|
|
213
|
-
onChange: async() => {
|
|
217
|
+
onChange: async({ forceWatch }) => {
|
|
214
218
|
if (this.args) {
|
|
215
|
-
await this.update(
|
|
219
|
+
await this.update({
|
|
220
|
+
...this.args,
|
|
221
|
+
forceWatch
|
|
222
|
+
});
|
|
216
223
|
}
|
|
217
224
|
},
|
|
218
225
|
enabledFor: {
|
|
@@ -244,7 +251,7 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
244
251
|
pinned: MgmtCluster[],
|
|
245
252
|
notPinned: MgmtCluster[]
|
|
246
253
|
} = await allHash(promises) as any;
|
|
247
|
-
const provClusters = await this.updateProvCluster(res.notPinned, res.pinned);
|
|
254
|
+
const provClusters = await this.updateProvCluster(res.notPinned, res.pinned, args.forceWatch || false);
|
|
248
255
|
const provClustersByMgmtId = provClusters.reduce((res: { [mgmtId: string]: ProvCluster}, provCluster: ProvCluster) => {
|
|
249
256
|
if (provCluster.mgmtClusterId) {
|
|
250
257
|
res[provCluster.mgmtClusterId] = provCluster;
|
|
@@ -340,6 +347,7 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
340
347
|
}
|
|
341
348
|
|
|
342
349
|
return this.clustersPinnedWrapper.request({
|
|
350
|
+
forceWatch: args.forceWatch,
|
|
343
351
|
pagination: {
|
|
344
352
|
filters: this.constructParams({
|
|
345
353
|
pinnedIds: args.pinnedIds,
|
|
@@ -357,6 +365,7 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
357
365
|
*/
|
|
358
366
|
private async updateOthers(args: UpdateArgs): Promise<MgmtCluster[]> {
|
|
359
367
|
return this.clustersOthersWrapper.request({
|
|
368
|
+
forceWatch: args.forceWatch,
|
|
360
369
|
pagination: {
|
|
361
370
|
filters: this.constructParams({
|
|
362
371
|
searchTerm: args.searchTerm,
|
|
@@ -375,8 +384,9 @@ export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper impleme
|
|
|
375
384
|
/**
|
|
376
385
|
* Find all provisioning clusters associated with the displayed mgmt clusters
|
|
377
386
|
*/
|
|
378
|
-
private async updateProvCluster(notPinned: MgmtCluster[], pinned: MgmtCluster[]): Promise<ProvCluster[]> {
|
|
387
|
+
private async updateProvCluster(notPinned: MgmtCluster[], pinned: MgmtCluster[], forceWatch: boolean): Promise<ProvCluster[]> {
|
|
379
388
|
return this.provClusterWrapper.request({
|
|
389
|
+
forceWatch,
|
|
380
390
|
pagination: {
|
|
381
391
|
filters: [
|
|
382
392
|
PaginationParamFilter.createMultipleFields(
|
|
@@ -157,8 +157,11 @@ export default {
|
|
|
157
157
|
this.$emit('update:hasIpv6', neu);
|
|
158
158
|
},
|
|
159
159
|
|
|
160
|
-
allValid
|
|
161
|
-
|
|
160
|
+
allValid: {
|
|
161
|
+
handler(neu) {
|
|
162
|
+
this.$emit('validationChanged', neu);
|
|
163
|
+
},
|
|
164
|
+
immediate: true
|
|
162
165
|
}
|
|
163
166
|
},
|
|
164
167
|
|
|
@@ -121,4 +121,28 @@ describe('component: EC2Networking', () => {
|
|
|
121
121
|
expect(wrapper.vm.enableIpv6).toBe(false);
|
|
122
122
|
expect(ipv6AddressCountInput.exists()).toBe(false);
|
|
123
123
|
});
|
|
124
|
+
|
|
125
|
+
it('should emit a validationChanged: false event when created with ipv6 enabled while some other pools have ipv6 disabled', async() => {
|
|
126
|
+
const wrapper = shallowMount(EC2Networking, {
|
|
127
|
+
...defaultCreateSetup,
|
|
128
|
+
propsData: {
|
|
129
|
+
...defaultCreateSetup.propsData,
|
|
130
|
+
machinePools: [{ hasIpv6: true }, { hasIpv6: false }],
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
expect(wrapper.emitted('validationChanged')?.[0][0]).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should emit a validationChanged: true event when created with ipv6 enabled while all other pools also have ipv6 enabled', async() => {
|
|
138
|
+
const wrapper = shallowMount(EC2Networking, {
|
|
139
|
+
...defaultCreateSetup,
|
|
140
|
+
propsData: {
|
|
141
|
+
...defaultCreateSetup.propsData,
|
|
142
|
+
machinePools: [{ hasIpv6: true }, { hasIpv6: true }],
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(wrapper.emitted('validationChanged')?.[0][0]).toBe(true);
|
|
147
|
+
});
|
|
124
148
|
});
|
|
@@ -238,5 +238,26 @@ describe('chartMixin', () => {
|
|
|
238
238
|
icon: 'icon-downgrade-alt',
|
|
239
239
|
});
|
|
240
240
|
});
|
|
241
|
+
|
|
242
|
+
it('should return "upgrade" action when upgrading from a pre-release to a stable version', () => {
|
|
243
|
+
const wrapper = mount(DummyComponent, {
|
|
244
|
+
data: () => ({
|
|
245
|
+
existing: { spec: { chart: { metadata: { version: '1.0.0-rc1' } } } },
|
|
246
|
+
version: { version: '1.0.0' }
|
|
247
|
+
}),
|
|
248
|
+
global: {
|
|
249
|
+
mocks: {
|
|
250
|
+
$store: mockStore,
|
|
251
|
+
$route: { query: {} }
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
expect(wrapper.vm.action).toStrictEqual({
|
|
257
|
+
name: 'upgrade',
|
|
258
|
+
tKey: 'upgrade',
|
|
259
|
+
icon: 'icon-upgrade-alt',
|
|
260
|
+
});
|
|
261
|
+
});
|
|
241
262
|
});
|
|
242
263
|
});
|
package/mixins/chart.js
CHANGED
|
@@ -10,7 +10,7 @@ import { NAME as MANAGER } from '@shell/config/product/manager';
|
|
|
10
10
|
import { OPA_GATE_KEEPER_ID } from '@shell/pages/c/_cluster/gatekeeper/index.vue';
|
|
11
11
|
import { formatSi, parseSi } from '@shell/utils/units';
|
|
12
12
|
import { CAPI, CATALOG } from '@shell/config/types';
|
|
13
|
-
import { isPrerelease, compare } from '@shell/utils/version';
|
|
13
|
+
import { isPrerelease, compare, isUpgradeFromPreToStable } from '@shell/utils/version';
|
|
14
14
|
import difference from 'lodash/difference';
|
|
15
15
|
import { LINUX, APP_UPGRADE_STATUS } from '@shell/store/catalog';
|
|
16
16
|
import { clone } from '@shell/utils/object';
|
|
@@ -240,6 +240,12 @@ export default {
|
|
|
240
240
|
};
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
if (isUpgradeFromPreToStable(this.currentVersion, this.targetVersion)) {
|
|
244
|
+
return {
|
|
245
|
+
name: 'upgrade', tKey: 'upgrade', icon: 'icon-upgrade-alt'
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
243
249
|
if (compare(this.currentVersion, this.targetVersion) < 0) {
|
|
244
250
|
return {
|
|
245
251
|
name: 'upgrade', tKey: 'upgrade', icon: 'icon-upgrade-alt'
|
package/package.json
CHANGED
package/pages/home.vue
CHANGED
|
@@ -302,8 +302,11 @@ export default defineComponent({
|
|
|
302
302
|
return Promise.resolve({});
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
const promises = [];
|
|
306
|
+
|
|
305
307
|
if ( this.canViewMgmtClusters ) {
|
|
306
|
-
|
|
308
|
+
// This is the only one we need to block on (needed for the initial sort on mgmt name)
|
|
309
|
+
promises.push(this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER }));
|
|
307
310
|
}
|
|
308
311
|
|
|
309
312
|
if ( this.canViewMachine ) {
|
|
@@ -323,7 +326,7 @@ export default defineComponent({
|
|
|
323
326
|
this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_TEMPLATE });
|
|
324
327
|
}
|
|
325
328
|
|
|
326
|
-
return Promise.
|
|
329
|
+
return Promise.all(promises);
|
|
327
330
|
},
|
|
328
331
|
|
|
329
332
|
async fetchPageSecondaryResources({
|
|
@@ -39,7 +39,6 @@ import { handleConflict } from '@shell/plugins/dashboard-store/normalize';
|
|
|
39
39
|
import { ExtensionPoint, ActionLocation } from '@shell/core/types';
|
|
40
40
|
import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
|
|
41
41
|
import { parse } from '@shell/utils/selector';
|
|
42
|
-
import { importDrawer } from '@shell/utils/dynamic-importer';
|
|
43
42
|
|
|
44
43
|
export const DNS_LIKE_TYPES = ['dnsLabel', 'dnsLabelRestricted', 'hostname'];
|
|
45
44
|
|
|
@@ -910,7 +909,7 @@ export default class Resource {
|
|
|
910
909
|
const onClose = () => this.$ctx.commit('slideInPanel/close', undefined, { root: true });
|
|
911
910
|
|
|
912
911
|
this.$ctx.commit('slideInPanel/open', {
|
|
913
|
-
component:
|
|
912
|
+
component: require(`@shell/components/Drawer/ResourceDetailDrawer/index.vue`).default,
|
|
914
913
|
componentProps: {
|
|
915
914
|
resource: this,
|
|
916
915
|
onClose,
|
|
@@ -888,16 +888,20 @@ const defaultActions = {
|
|
|
888
888
|
}
|
|
889
889
|
});
|
|
890
890
|
}
|
|
891
|
-
|
|
892
891
|
// Should any listeners be notified of this request for them to kick off their own event handling?
|
|
893
|
-
getters.listenerManager.triggerEventListener({
|
|
892
|
+
getters.listenerManager.triggerEventListener({
|
|
893
|
+
event: STEVE_WATCH_MODE.RESOURCE_CHANGES,
|
|
894
|
+
params: {
|
|
895
|
+
...params,
|
|
896
|
+
forceWatch: opt.forceWatch
|
|
897
|
+
}
|
|
898
|
+
});
|
|
894
899
|
} else {
|
|
895
900
|
have = getters['all'](resourceType).slice();
|
|
896
901
|
|
|
897
902
|
if ( namespace ) {
|
|
898
903
|
have = have.filter((x) => x.metadata?.namespace === namespace);
|
|
899
904
|
}
|
|
900
|
-
|
|
901
905
|
want = await dispatch('findAll', {
|
|
902
906
|
type: resourceType,
|
|
903
907
|
watchNamespace: namespace,
|
|
@@ -1181,12 +1185,16 @@ const defaultActions = {
|
|
|
1181
1185
|
});
|
|
1182
1186
|
|
|
1183
1187
|
if (hasEventListeners) {
|
|
1184
|
-
//
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1188
|
+
const inError = getters.inError(obj); // We don't want to force listeners to resync if the socket is in error (handled by resource.error mechanism)
|
|
1189
|
+
|
|
1190
|
+
if (!inError) {
|
|
1191
|
+
// If there's event listeners kick them off
|
|
1192
|
+
// - The re-watch associated with normal watches will watch from a revision from it's own cache
|
|
1193
|
+
// - The revision in that cache might be ahead of the state the listeners have, so the watch won't ping something for the listeners to trigger on
|
|
1194
|
+
// - so to work around this whenever we start the watches again trigger off the changes for it
|
|
1195
|
+
// Improvement - we only do one event here (currently the only one supported), could expand to others
|
|
1196
|
+
getters.listenerManager.triggerEventListener({ event: STEVE_WATCH_EVENT_TYPES.CHANGES, params: obj });
|
|
1197
|
+
}
|
|
1190
1198
|
}
|
|
1191
1199
|
}
|
|
1192
1200
|
},
|
|
@@ -167,7 +167,7 @@ export class SteveWatchEventListenerManager {
|
|
|
167
167
|
|
|
168
168
|
if (eventWatcher) {
|
|
169
169
|
Object.values(eventWatcher.callbacks).forEach((cb) => {
|
|
170
|
-
cb();
|
|
170
|
+
cb({ forceWatch: params.forceWatch }); // eslint-disable-line node/no-callback-literal
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
173
|
}
|
|
@@ -176,7 +176,9 @@ export class SteveWatchEventListenerManager {
|
|
|
176
176
|
const watch = this.getWatch({ params });
|
|
177
177
|
|
|
178
178
|
watch.listeners.forEach((l) => {
|
|
179
|
-
Object.values(l.callbacks || {}).forEach((cb) =>
|
|
179
|
+
Object.values(l.callbacks || {}).forEach((cb) => {
|
|
180
|
+
cb({ forceWatch: params.forceWatch });// eslint-disable-line node/no-callback-literal
|
|
181
|
+
});
|
|
180
182
|
});
|
|
181
183
|
}
|
|
182
184
|
|
package/store/index.js
CHANGED
|
@@ -263,7 +263,7 @@ export const state = () => {
|
|
|
263
263
|
$route: markRaw({}),
|
|
264
264
|
$plugin: markRaw({}),
|
|
265
265
|
showWorkspaceSwitcher: true,
|
|
266
|
-
|
|
266
|
+
localCluster: null,
|
|
267
267
|
};
|
|
268
268
|
};
|
|
269
269
|
|
|
@@ -272,10 +272,20 @@ export const getters = {
|
|
|
272
272
|
return state.clusterReady === true;
|
|
273
273
|
},
|
|
274
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Cache of the mgmt cluster fetched at start up
|
|
277
|
+
*
|
|
278
|
+
* We cannot rely on the store to cache this as the store may contain a page without the local cluster
|
|
279
|
+
*/
|
|
280
|
+
localCluster(state) {
|
|
281
|
+
return state.localCluster;
|
|
282
|
+
},
|
|
283
|
+
|
|
275
284
|
isMultiCluster(state, getters) {
|
|
276
|
-
const
|
|
285
|
+
const clusterCount = getters['management/all'](COUNT)?.[0]?.counts?.[MANAGEMENT.CLUSTER]?.summary?.count || 0;
|
|
286
|
+
const localCluster = getters['localCluster'];
|
|
277
287
|
|
|
278
|
-
if (
|
|
288
|
+
if (clusterCount === 1 && !!localCluster) {
|
|
279
289
|
return false;
|
|
280
290
|
} else {
|
|
281
291
|
return true;
|
|
@@ -592,10 +602,9 @@ export const getters = {
|
|
|
592
602
|
},
|
|
593
603
|
|
|
594
604
|
isStandaloneHarvester(state, getters) {
|
|
595
|
-
const
|
|
596
|
-
const cluster = clusters.find((c) => c.id === 'local') || {};
|
|
605
|
+
const localCluster = getters['localCluster'];
|
|
597
606
|
|
|
598
|
-
return getters['isSingleProduct'] &&
|
|
607
|
+
return getters['isSingleProduct'] && localCluster?.isHarvester && !getters['isRancherInHarvester'];
|
|
599
608
|
},
|
|
600
609
|
|
|
601
610
|
showTopLevelMenu(getters) {
|
|
@@ -640,9 +649,10 @@ export const mutations = {
|
|
|
640
649
|
clearPageActionHandler(state) {
|
|
641
650
|
state.pageActionHandler = null;
|
|
642
651
|
},
|
|
643
|
-
managementChanged(state, { ready, isRancher }) {
|
|
652
|
+
managementChanged(state, { ready, isRancher, localCluster }) {
|
|
644
653
|
state.managementReady = ready;
|
|
645
654
|
state.isRancher = isRancher;
|
|
655
|
+
state.localCluster = localCluster;
|
|
646
656
|
},
|
|
647
657
|
clusterReady(state, ready) {
|
|
648
658
|
state.clusterReady = ready;
|
|
@@ -846,11 +856,21 @@ export const actions = {
|
|
|
846
856
|
|
|
847
857
|
res = await allHash(promises);
|
|
848
858
|
|
|
859
|
+
let localCluster = null;
|
|
860
|
+
|
|
849
861
|
if (!res[MANAGEMENT.SETTING] || !paginateClusters({ rootGetters, state })) {
|
|
850
862
|
// This introduces a synchronous request, however we need settings to determine if SSP is enabled
|
|
851
|
-
|
|
852
|
-
res[MANAGEMENT.CLUSTER] = await dispatch('management/findAll', { type: MANAGEMENT.CLUSTER, opt: { watch: false } });
|
|
863
|
+
await dispatch('management/findAll', { type: MANAGEMENT.CLUSTER, opt: { watch: false } });
|
|
853
864
|
toWatch.push(MANAGEMENT.CLUSTER);
|
|
865
|
+
|
|
866
|
+
localCluster = getters['management/byId'](MANAGEMENT.CLUSTER, 'local');
|
|
867
|
+
} else {
|
|
868
|
+
try {
|
|
869
|
+
localCluster = await dispatch('management/find', {
|
|
870
|
+
type: MANAGEMENT.CLUSTER, id: 'local', opt: { watch: false }
|
|
871
|
+
});
|
|
872
|
+
} catch (e) { // we don't care about errors, specifically 404s
|
|
873
|
+
}
|
|
854
874
|
}
|
|
855
875
|
|
|
856
876
|
// See comment above. Now that we have feature flags we can watch resources
|
|
@@ -858,11 +878,7 @@ export const actions = {
|
|
|
858
878
|
dispatch('management/watch', { type });
|
|
859
879
|
});
|
|
860
880
|
|
|
861
|
-
const isMultiCluster = getters['isMultiCluster'];
|
|
862
|
-
|
|
863
881
|
// If the local cluster is a Harvester cluster and 'rancher-manager-support' is true, it means that the embedded Rancher is being used.
|
|
864
|
-
const localCluster = res[MANAGEMENT.CLUSTER]?.find((c) => c.id === 'local');
|
|
865
|
-
|
|
866
882
|
if (localCluster?.isHarvester) {
|
|
867
883
|
const harvesterSetting = await dispatch('cluster/findAll', { type: HCI.SETTING, opt: { url: `/v1/harvester/${ HCI.SETTING }s` } });
|
|
868
884
|
const rancherManagerSupport = harvesterSetting.find((setting) => setting.id === 'rancher-manager-support');
|
|
@@ -904,6 +920,7 @@ export const actions = {
|
|
|
904
920
|
commit('managementChanged', {
|
|
905
921
|
ready: true,
|
|
906
922
|
isRancher,
|
|
923
|
+
localCluster
|
|
907
924
|
});
|
|
908
925
|
|
|
909
926
|
if ( res[FLEET.WORKSPACE] ) {
|
|
@@ -914,6 +931,8 @@ export const actions = {
|
|
|
914
931
|
});
|
|
915
932
|
}
|
|
916
933
|
|
|
934
|
+
const isMultiCluster = getters['isMultiCluster'];
|
|
935
|
+
|
|
917
936
|
console.log(`Done loading management; isRancher=${ isRancher }; isMultiCluster=${ isMultiCluster }`); // eslint-disable-line no-console
|
|
918
937
|
},
|
|
919
938
|
|
package/store/type-map.js
CHANGED
|
@@ -1895,7 +1895,7 @@ function ifHave(getters, option) {
|
|
|
1895
1895
|
case IF_HAVE.NOT_V1_ISTIO: {
|
|
1896
1896
|
return !isV1Istio(getters);
|
|
1897
1897
|
}
|
|
1898
|
-
case IF_HAVE.MULTI_CLUSTER: {
|
|
1898
|
+
case IF_HAVE.MULTI_CLUSTER: { // Used by harvester extension
|
|
1899
1899
|
return getters.isMultiCluster;
|
|
1900
1900
|
}
|
|
1901
1901
|
case IF_HAVE.NEUVECTOR_NAMESPACE: {
|
|
@@ -1904,10 +1904,10 @@ function ifHave(getters, option) {
|
|
|
1904
1904
|
case IF_HAVE.ADMIN: {
|
|
1905
1905
|
return isAdminUser(getters);
|
|
1906
1906
|
}
|
|
1907
|
-
case IF_HAVE.MCM_DISABLED: {
|
|
1907
|
+
case IF_HAVE.MCM_DISABLED: { // There's a general MCM ff, this is conflating it with a harvester concept
|
|
1908
1908
|
return !getters['isRancherInHarvester'];
|
|
1909
1909
|
}
|
|
1910
|
-
case IF_HAVE.NOT_STANDALONE_HARVESTER: {
|
|
1910
|
+
case IF_HAVE.NOT_STANDALONE_HARVESTER: { // Not used by harvester extension...
|
|
1911
1911
|
return !getters['isStandaloneHarvester'];
|
|
1912
1912
|
}
|
|
1913
1913
|
default:
|
package/types/shell/index.d.ts
CHANGED
|
@@ -5257,6 +5257,7 @@ export function parse(str: any): any;
|
|
|
5257
5257
|
export function sortable(str: any): any;
|
|
5258
5258
|
export function compare(in1: any, in2: any): any;
|
|
5259
5259
|
export function isPrerelease(version?: string): boolean;
|
|
5260
|
+
export function isUpgradeFromPreToStable(currentVersion: any, targetVersion: any): any;
|
|
5260
5261
|
export function isDevBuild(version: any): boolean;
|
|
5261
5262
|
export function getVersionInfo(store: any): {
|
|
5262
5263
|
displayVersion: any;
|
|
@@ -12,10 +12,17 @@ export interface STEVE_WATCH_EVENT_PARAMS_COMMON {
|
|
|
12
12
|
params: STEVE_WATCH_PARAMS,
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Args for @STEVE_WATCH_EVENT_LISTENER_CALLBACK
|
|
17
|
+
*/
|
|
18
|
+
export type STEVE_WATCH_EVENT_LISTENER_CALLBACK_PARAMS = {
|
|
19
|
+
forceWatch?: boolean,
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
/**
|
|
16
23
|
* Executes when a watch event has a listener and it's triggered
|
|
17
24
|
*/
|
|
18
|
-
export type STEVE_WATCH_EVENT_LISTENER_CALLBACK = () => void
|
|
25
|
+
export type STEVE_WATCH_EVENT_LISTENER_CALLBACK = (params: STEVE_WATCH_EVENT_LISTENER_CALLBACK_PARAMS) => void
|
|
19
26
|
|
|
20
27
|
/**
|
|
21
28
|
* Common params used when a watcher adds a listener to a watch
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isDevBuild } from '@shell/utils/version';
|
|
1
|
+
import { isDevBuild, isUpgradeFromPreToStable } from '@shell/utils/version';
|
|
2
2
|
|
|
3
3
|
describe('fx: isDevBuild', () => {
|
|
4
4
|
it.each([
|
|
@@ -16,3 +16,21 @@ describe('fx: isDevBuild', () => {
|
|
|
16
16
|
}
|
|
17
17
|
);
|
|
18
18
|
});
|
|
19
|
+
|
|
20
|
+
describe('fx: isUpgradeFromPreToStable', () => {
|
|
21
|
+
it('should be true when going from pre-release to stable of same version', () => {
|
|
22
|
+
expect(isUpgradeFromPreToStable('1.0.0-rc1', '1.0.0')).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should be false when going from stable to pre-release', () => {
|
|
26
|
+
expect(isUpgradeFromPreToStable('1.0.0', '1.0.0-rc1')).toBe(false );
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should be false for stable to stable', () => {
|
|
30
|
+
expect(isUpgradeFromPreToStable('1.0.0', '1.1.0')).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should be false for pre-release to pre-release', () => {
|
|
34
|
+
expect(isUpgradeFromPreToStable('1.0.0-rc1', '1.0.0-rc2')).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
package/utils/back-off.ts
CHANGED
|
@@ -137,9 +137,9 @@ class BackOff {
|
|
|
137
137
|
|
|
138
138
|
// First step is immediate (0.001s)
|
|
139
139
|
// Second and others are exponential
|
|
140
|
-
// 1,
|
|
141
|
-
// 1,
|
|
142
|
-
// 0.25s, 1s,
|
|
140
|
+
// Try: 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
141
|
+
// Multiple: 1, 4, 9, 16, 25, 36, 49, 64, 81
|
|
142
|
+
// Actual Time: 0.25s, 1s, 2.25s, 4s, 6.25s, 9s, 12.25s, 16s, 20.25s
|
|
143
143
|
const delay = backOffTry === 0 ? 1 : Math.pow(backOffTry, 2) * 250;
|
|
144
144
|
|
|
145
145
|
this.log('info', id, `Delaying call (attempt ${ backOffTry + 1 }, delayed by ${ delay }ms)`, description);
|
|
@@ -96,6 +96,7 @@ describe('systemInfoProvider', () => {
|
|
|
96
96
|
return undefined;
|
|
97
97
|
}),
|
|
98
98
|
'management/schemaFor': jest.fn(),
|
|
99
|
+
localCluster: mockClusters.find((c) => c.id === 'local') || null,
|
|
99
100
|
};
|
|
100
101
|
|
|
101
102
|
(version.getVersionData as jest.Mock).mockReturnValue({
|
|
@@ -159,6 +160,7 @@ describe('systemInfoProvider', () => {
|
|
|
159
160
|
|
|
160
161
|
mockGetters['uiplugins/plugins'] = null; // No plugins
|
|
161
162
|
mockGetters['auth/principalId'] = null; // No user
|
|
163
|
+
mockGetters['localCluster'] = null; // No clusters
|
|
162
164
|
|
|
163
165
|
const infoProvider = new SystemInfoProvider(mockGetters, {});
|
|
164
166
|
const qs = infoProvider.buildQueryString();
|
|
@@ -191,6 +193,7 @@ describe('systemInfoProvider', () => {
|
|
|
191
193
|
|
|
192
194
|
mockGetters['auth/principalId'] = 'user-456';
|
|
193
195
|
mockGetters['uiplugins/plugins'] = []; // No plugins
|
|
196
|
+
mockGetters['localCluster'] = null; // No clusters
|
|
194
197
|
|
|
195
198
|
const infoProvider = new SystemInfoProvider(mockGetters, {});
|
|
196
199
|
const qs = infoProvider.buildQueryString();
|
|
@@ -199,7 +202,6 @@ describe('systemInfoProvider', () => {
|
|
|
199
202
|
expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'install-uuid');
|
|
200
203
|
expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'server-version-type');
|
|
201
204
|
expect(mockGetters['management/typeRegistered']).toHaveBeenCalledWith(COUNT);
|
|
202
|
-
expect(mockGetters['management/typeRegistered']).toHaveBeenCalledWith(MANAGEMENT.CLUSTER);
|
|
203
205
|
expect(mockGetters['management/all']).not.toHaveBeenCalled();
|
|
204
206
|
|
|
205
207
|
// Verify the query string is built with fallback or empty values
|
|
@@ -225,6 +227,16 @@ describe('systemInfoProvider', () => {
|
|
|
225
227
|
return { id, value: '' }; // Empty values for all settings
|
|
226
228
|
}
|
|
227
229
|
});
|
|
230
|
+
|
|
231
|
+
// local cluster with missing properties
|
|
232
|
+
const localCluster = {
|
|
233
|
+
id: 'local',
|
|
234
|
+
isLocal: true,
|
|
235
|
+
status: { nodeCount: 1 },
|
|
236
|
+
// kubernetesVersionBase is missing
|
|
237
|
+
// provisioner is missing
|
|
238
|
+
};
|
|
239
|
+
|
|
228
240
|
mockGetters['management/all'].mockImplementation((type: string) => {
|
|
229
241
|
if (type === MANAGEMENT.SETTING) {
|
|
230
242
|
// Return settings, but with empty values
|
|
@@ -237,20 +249,14 @@ describe('systemInfoProvider', () => {
|
|
|
237
249
|
return [{ counts: { [MANAGEMENT.CLUSTER]: { summary: { count: 1 } } } }];
|
|
238
250
|
}
|
|
239
251
|
if (type === MANAGEMENT.CLUSTER) {
|
|
240
|
-
|
|
241
|
-
return [{
|
|
242
|
-
id: 'local',
|
|
243
|
-
isLocal: true,
|
|
244
|
-
status: { nodeCount: 1 },
|
|
245
|
-
// kubernetesVersionBase is missing
|
|
246
|
-
// provisioner is missing
|
|
247
|
-
}];
|
|
252
|
+
return [localCluster];
|
|
248
253
|
}
|
|
249
254
|
|
|
250
255
|
return [];
|
|
251
256
|
});
|
|
252
257
|
|
|
253
258
|
mockGetters['auth/principalId'] = null; // No user
|
|
259
|
+
mockGetters['localCluster'] = localCluster;
|
|
254
260
|
|
|
255
261
|
const infoProvider = new SystemInfoProvider(mockGetters, {});
|
|
256
262
|
const qs = infoProvider.buildQueryString();
|
|
@@ -107,8 +107,7 @@ export class SystemInfoProvider {
|
|
|
107
107
|
// High-level information from clusters
|
|
108
108
|
const counts = this.getAll(getters, COUNT)?.[0]?.counts || {};
|
|
109
109
|
const clusterCount = counts[MANAGEMENT.CLUSTER] || {};
|
|
110
|
-
const
|
|
111
|
-
const localCluster = all ? all.find((c: any) => c.isLocal) : undefined;
|
|
110
|
+
const localCluster = getters['localCluster'];
|
|
112
111
|
|
|
113
112
|
// Stats for installed extensions
|
|
114
113
|
const uiExtensionList = getters['uiplugins/plugins'];
|
|
@@ -19,7 +19,7 @@ interface Args {
|
|
|
19
19
|
/**
|
|
20
20
|
* Callback called when the resource is changed (notified by socket)
|
|
21
21
|
*/
|
|
22
|
-
onChange?:
|
|
22
|
+
onChange?: STEVE_WATCH_EVENT_LISTENER_CALLBACK,
|
|
23
23
|
|
|
24
24
|
formatResponse?: {
|
|
25
25
|
/**
|
|
@@ -72,13 +72,13 @@ class PaginationWrapper<T extends object> {
|
|
|
72
72
|
this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters, $plugin: this.$store.$plugin }, enabledFor);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
async request(
|
|
76
|
-
|
|
75
|
+
async request({ pagination, forceWatch }: {
|
|
76
|
+
forceWatch?: boolean,
|
|
77
|
+
pagination: PaginationArgs,
|
|
77
78
|
}): Promise<Result<T>> {
|
|
78
79
|
if (!this.isEnabled) {
|
|
79
80
|
throw new Error(`Wrapper for type '${ this.enabledFor.store }/${ this.enabledFor.resource?.id }' in context '${ this.enabledFor.resource?.context }' not supported`);
|
|
80
81
|
}
|
|
81
|
-
const { pagination } = args;
|
|
82
82
|
const opt: ActionFindPageArgs = {
|
|
83
83
|
watch: false,
|
|
84
84
|
pagination,
|
|
@@ -89,14 +89,18 @@ class PaginationWrapper<T extends object> {
|
|
|
89
89
|
const out: ActionFindPageTransientResult<T> = await this.$store.dispatch(`${ this.enabledFor.store }/findPage`, { opt, type: this.enabledFor.resource?.id });
|
|
90
90
|
|
|
91
91
|
// Watch
|
|
92
|
-
|
|
92
|
+
const firstTime = !this.steveWatchParams;
|
|
93
|
+
|
|
94
|
+
if (this.onChange && (firstTime || forceWatch) ) { // && !this.steveWatchParams
|
|
93
95
|
this.steveWatchParams = {
|
|
94
96
|
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
95
97
|
id: this.id,
|
|
96
98
|
params: {
|
|
97
|
-
type:
|
|
98
|
-
mode:
|
|
99
|
-
|
|
99
|
+
type: this.enabledFor.resource?.id as string,
|
|
100
|
+
mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
|
|
101
|
+
force: forceWatch,
|
|
102
|
+
},
|
|
103
|
+
|
|
100
104
|
};
|
|
101
105
|
|
|
102
106
|
this.watch();
|
package/utils/version.js
CHANGED
|
@@ -74,6 +74,21 @@ export function isPrerelease(version = '') {
|
|
|
74
74
|
return !!semver.prerelease(version);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
export function isUpgradeFromPreToStable(currentVersion, targetVersion) {
|
|
78
|
+
if (!isPrerelease(currentVersion) || isPrerelease(targetVersion)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const cVersion = semver.clean(currentVersion, { loose: true });
|
|
83
|
+
const tVersion = semver.clean(targetVersion, { loose: true });
|
|
84
|
+
|
|
85
|
+
if (cVersion && tVersion && semver.valid(cVersion) && semver.valid(tVersion)) {
|
|
86
|
+
return semver.lt(cVersion, tVersion);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
77
92
|
export function isDevBuild(version) {
|
|
78
93
|
if ( ['dev', 'master', 'head'].includes(version) || version.endsWith('-head') || version.match(/-rc\d+$/) || version.match(/-alpha\d+$/) ) {
|
|
79
94
|
return true;
|