@rancher/shell 3.0.1-rc.1 → 3.0.1-rc.3
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/styles/app.scss +0 -1
- package/assets/translations/en-us.yaml +2 -5
- package/assets/translations/zh-hans.yaml +0 -3
- package/components/GlobalRoleBindings.vue +0 -7
- package/components/SideNav.vue +5 -3
- package/components/SortableTable/THead.vue +1 -1
- package/components/auth/RoleDetailEdit.vue +0 -16
- package/components/nav/TopLevelMenu.vue +5 -3
- package/edit/catalog.cattle.io.clusterrepo.vue +0 -9
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +5 -2
- package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +17 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -4
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +20 -6
- package/models/__tests__/management.cattle.io.cluster.test.ts +0 -42
- package/models/fleet.cattle.io.gitrepo.js +5 -0
- package/models/management.cattle.io.cluster.js +0 -24
- package/models/management.cattle.io.globalrole.js +1 -0
- package/models/provisioning.cattle.io.cluster.js +23 -1
- package/package.json +6 -4
- package/pages/auth/login.vue +7 -2
- package/pages/c/_cluster/auth/roles/index.vue +1 -11
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +71 -1
- package/pages/c/_cluster/explorer/index.vue +6 -2
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +1 -1
- package/scripts/extension/parse-tag-name +19 -12
- package/scripts/publish-shell.sh +10 -2
- package/scripts/test-plugins-build.sh +8 -9
- package/scripts/typegen.sh +2 -0
- package/types/shell/index.d.ts +116 -1
- package/utils/__tests__/object.test.ts +30 -1
- package/utils/object.js +28 -0
- package/utils/version.js +1 -1
- package/vue.config.js +4 -3
- package/machine-config/__tests__/vmwarevsphere-pool-config-merge.test.ts +0 -30
- package/machine-config/vmwarevsphere-pool-config-merge.ts +0 -25
package/assets/styles/app.scss
CHANGED
|
@@ -1065,7 +1065,7 @@ catalog:
|
|
|
1065
1065
|
target:
|
|
1066
1066
|
git: Git repository containing Helm chart or cluster template definitions
|
|
1067
1067
|
http: http(s) URL to an index generated by Helm
|
|
1068
|
-
oci: OCI Repository
|
|
1068
|
+
oci: OCI Repository
|
|
1069
1069
|
label: Target
|
|
1070
1070
|
url:
|
|
1071
1071
|
label: Index URL
|
|
@@ -1475,6 +1475,7 @@ cluster:
|
|
|
1475
1475
|
placeholder: A unique name for the cluster
|
|
1476
1476
|
directoryConfig:
|
|
1477
1477
|
title: Data directory configuration
|
|
1478
|
+
banner: Data directory configuration can not be changed once the cluster has been created
|
|
1478
1479
|
radioInput:
|
|
1479
1480
|
defaultLabel: Use default data directory configuration
|
|
1480
1481
|
commonLabel: Use a common base directory for data directory configuration (sub-directories will be used for the system-agent, provisioning and distro paths)
|
|
@@ -4877,10 +4878,6 @@ rbac:
|
|
|
4877
4878
|
admin:
|
|
4878
4879
|
label: Administrator
|
|
4879
4880
|
description: Administrators have full control over the entire installation and all resources in all clusters.
|
|
4880
|
-
restricted-admin:
|
|
4881
|
-
label: Restricted Administrator
|
|
4882
|
-
description: Restricted Admins have full control over all resources in all downstream clusters but no access to the local cluster.
|
|
4883
|
-
deprecation: 'Warning: The Restricted Administrator role has been deprecated as of Rancher 2.8.0 and will be removed in a future release - Check out the <a href="{releaseNotesUrl}" target="_blank" rel="noopener noreferrer nofollow">Release Notes</a>'
|
|
4884
4881
|
user:
|
|
4885
4882
|
label: Standard User
|
|
4886
4883
|
description: Standard Users can create new clusters and manage clusters and projects they have been granted access to.
|
|
@@ -4556,9 +4556,6 @@ rbac:
|
|
|
4556
4556
|
admin:
|
|
4557
4557
|
label: 管理员
|
|
4558
4558
|
description: 管理员可以完全控制整个安装以及所有集群中的所有资源。
|
|
4559
|
-
restricted-admin:
|
|
4560
|
-
label: 受限管理员
|
|
4561
|
-
description: 受限管理员可以完全控制所有下游集群的所有资源,但不能访问本地集群。
|
|
4562
4559
|
user:
|
|
4563
4560
|
label: 普通用户
|
|
4564
4561
|
description: 普通用户可以创建集群,并管理其授权访问的集群和项目。
|
|
@@ -108,7 +108,6 @@ export default {
|
|
|
108
108
|
// This not only identifies global roles but the order here is the order we want to display them in the UI
|
|
109
109
|
globalPermissions: [
|
|
110
110
|
'admin',
|
|
111
|
-
'restricted-admin',
|
|
112
111
|
'user',
|
|
113
112
|
'user-base',
|
|
114
113
|
],
|
|
@@ -121,7 +120,6 @@ export default {
|
|
|
121
120
|
};
|
|
122
121
|
},
|
|
123
122
|
computed: {
|
|
124
|
-
...mapGetters(['releaseNotesUrl']),
|
|
125
123
|
...mapGetters({ t: 'i18n/t' }),
|
|
126
124
|
|
|
127
125
|
isCreate() {
|
|
@@ -375,11 +373,6 @@ export default {
|
|
|
375
373
|
</div>
|
|
376
374
|
</template>
|
|
377
375
|
</Checkbox>
|
|
378
|
-
<p
|
|
379
|
-
v-if="role.id === 'restricted-admin'"
|
|
380
|
-
v-clean-html="t('rbac.globalRoles.role.restricted-admin.deprecation', { releaseNotesUrl }, true)"
|
|
381
|
-
class="deprecation-notice"
|
|
382
|
-
/>
|
|
383
376
|
</div>
|
|
384
377
|
</div>
|
|
385
378
|
</template>
|
package/components/SideNav.vue
CHANGED
|
@@ -111,8 +111,10 @@ export default {
|
|
|
111
111
|
|
|
112
112
|
computed: {
|
|
113
113
|
...mapState(['managementReady', 'clusterReady']),
|
|
114
|
-
...mapGetters(['productId', 'clusterId', 'currentProduct', 'rootProduct', 'isSingleProduct', 'namespaceMode', 'isExplorer', 'isVirtualCluster']),
|
|
115
|
-
...mapGetters({
|
|
114
|
+
...mapGetters(['isStandaloneHarvester', 'productId', 'clusterId', 'currentProduct', 'rootProduct', 'isSingleProduct', 'namespaceMode', 'isExplorer', 'isVirtualCluster']),
|
|
115
|
+
...mapGetters({
|
|
116
|
+
locale: 'i18n/selectedLocaleLabel', availableLocales: 'i18n/availableLocales', hasMultipleLocales: 'i18n/hasMultipleLocales'
|
|
117
|
+
}),
|
|
116
118
|
...mapGetters('type-map', ['activeProducts']),
|
|
117
119
|
|
|
118
120
|
favoriteTypes: mapPref(FAVORITE_TYPES),
|
|
@@ -437,7 +439,7 @@ export default {
|
|
|
437
439
|
</span>
|
|
438
440
|
|
|
439
441
|
<!-- locale selector -->
|
|
440
|
-
<span v-if="isSingleProduct">
|
|
442
|
+
<span v-if="isSingleProduct && hasMultipleLocales && !isStandaloneHarvester">
|
|
441
443
|
<v-dropdown
|
|
442
444
|
popperClass="localeSelector"
|
|
443
445
|
placement="top"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { mapGetters } from 'vuex';
|
|
3
2
|
import { MANAGEMENT, RBAC } from '@shell/config/types';
|
|
4
3
|
import CruResource from '@shell/components/CruResource';
|
|
5
4
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
@@ -15,7 +14,6 @@ import { ucFirst } from '@shell/utils/string';
|
|
|
15
14
|
import SortableTable from '@shell/components/SortableTable';
|
|
16
15
|
import { _CLONE, _DETAIL } from '@shell/config/query-params';
|
|
17
16
|
import { SCOPED_RESOURCES, SCOPED_RESOURCE_GROUPS } from '@shell/config/roles';
|
|
18
|
-
import { Banner } from '@components/Banner';
|
|
19
17
|
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
|
20
18
|
|
|
21
19
|
import { SUBTYPE_MAPPING, VERBS } from '@shell/models/management.cattle.io.roletemplate';
|
|
@@ -66,7 +64,6 @@ export default {
|
|
|
66
64
|
SortableTable,
|
|
67
65
|
Loading,
|
|
68
66
|
Error,
|
|
69
|
-
Banner,
|
|
70
67
|
LabeledInput
|
|
71
68
|
},
|
|
72
69
|
|
|
@@ -163,12 +160,6 @@ export default {
|
|
|
163
160
|
},
|
|
164
161
|
|
|
165
162
|
computed: {
|
|
166
|
-
...mapGetters(['releaseNotesUrl']),
|
|
167
|
-
|
|
168
|
-
showRestrictedAdminDeprecationBanner() {
|
|
169
|
-
return this.value.subtype === GLOBAL && this.value.id === 'restricted-admin';
|
|
170
|
-
},
|
|
171
|
-
|
|
172
163
|
label() {
|
|
173
164
|
return this.t(`rbac.roletemplate.subtypes.${ this.value.subtype }.label`);
|
|
174
165
|
},
|
|
@@ -559,13 +550,6 @@ export default {
|
|
|
559
550
|
@finish="save"
|
|
560
551
|
@cancel="cancel"
|
|
561
552
|
>
|
|
562
|
-
<Banner
|
|
563
|
-
v-if="showRestrictedAdminDeprecationBanner"
|
|
564
|
-
color="warning"
|
|
565
|
-
class="mb-20"
|
|
566
|
-
>
|
|
567
|
-
<span v-clean-html="t('rbac.globalRoles.role.restricted-admin.deprecation', { releaseNotesUrl }, true)" />
|
|
568
|
-
</Banner>
|
|
569
553
|
<template v-if="isDetail">
|
|
570
554
|
<SortableTable
|
|
571
555
|
key-field="index"
|
|
@@ -920,7 +920,7 @@ export default {
|
|
|
920
920
|
// needs !important so that we can
|
|
921
921
|
// offset the tooltip a bit so it doesn't
|
|
922
922
|
// overlap the pin icon and cause bad UX
|
|
923
|
-
left:
|
|
923
|
+
left: 48px !important;
|
|
924
924
|
}
|
|
925
925
|
|
|
926
926
|
.localeSelector, .footer-tooltip {
|
|
@@ -1069,7 +1069,7 @@ export default {
|
|
|
1069
1069
|
line-height: normal;
|
|
1070
1070
|
|
|
1071
1071
|
& > p {
|
|
1072
|
-
width:
|
|
1072
|
+
width: 182px;
|
|
1073
1073
|
white-space: nowrap;
|
|
1074
1074
|
overflow: hidden;
|
|
1075
1075
|
text-overflow: ellipsis;
|
|
@@ -1117,9 +1117,11 @@ export default {
|
|
|
1117
1117
|
|
|
1118
1118
|
> i, > img {
|
|
1119
1119
|
display: block;
|
|
1120
|
-
width: 42px;
|
|
1121
1120
|
font-size: $icon-size;
|
|
1122
1121
|
margin-right: 14px;
|
|
1122
|
+
&.group-icon {
|
|
1123
|
+
width: 42px;
|
|
1124
|
+
}
|
|
1123
1125
|
}
|
|
1124
1126
|
|
|
1125
1127
|
.rancher-provider-icon,
|
|
@@ -287,12 +287,3 @@ export default {
|
|
|
287
287
|
/>
|
|
288
288
|
</form>
|
|
289
289
|
</template>
|
|
290
|
-
|
|
291
|
-
<style lang="scss">
|
|
292
|
-
span.oci-experimental-badge {
|
|
293
|
-
background-color: var(--warning);
|
|
294
|
-
color: var(--darker-active-bg);
|
|
295
|
-
font-size: 12px;
|
|
296
|
-
padding: 2px 6px;
|
|
297
|
-
}
|
|
298
|
-
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import merge from 'lodash/merge';
|
|
3
3
|
import { ucFirst } from '@shell/utils/string';
|
|
4
|
-
import { isSimpleKeyValue } from '@shell/utils/object';
|
|
4
|
+
import { isSimpleKeyValue, set } from '@shell/utils/object';
|
|
5
5
|
import { _CREATE, _VIEW } from '@shell/config/query-params';
|
|
6
6
|
import { SCHEMA, NAMESPACE } from '@shell/config/types';
|
|
7
7
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
@@ -222,6 +222,9 @@ export default {
|
|
|
222
222
|
this.value.spec.match.namespaces = [];
|
|
223
223
|
this.value.spec.match.excludedNamespaces = [];
|
|
224
224
|
}
|
|
225
|
+
},
|
|
226
|
+
setParameters(e) {
|
|
227
|
+
return set(this.value.spec, 'parameters', e);
|
|
225
228
|
}
|
|
226
229
|
}
|
|
227
230
|
};
|
|
@@ -356,7 +359,7 @@ export default {
|
|
|
356
359
|
v-model:value="parametersYaml"
|
|
357
360
|
class="yaml-editor"
|
|
358
361
|
:editor-mode="editorMode"
|
|
359
|
-
@newObject="
|
|
362
|
+
@newObject="setParameters"
|
|
360
363
|
/>
|
|
361
364
|
</Tab>
|
|
362
365
|
</Tabbed>
|
|
@@ -113,7 +113,7 @@ describe('component: DirectoryConfig', () => {
|
|
|
113
113
|
expect(wrapper.vm.value.k8sDistro).toStrictEqual(k8sDistroValue);
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
it('should render the component with configuration being an empty object, without errors and radio be of value DATA_DIR_RADIO_OPTIONS.
|
|
116
|
+
it('should render the component with configuration being an empty object, without errors and radio be of value DATA_DIR_RADIO_OPTIONS.CUSTOM (edit scenario)', () => {
|
|
117
117
|
const newMountOptions = clone(mountOptions);
|
|
118
118
|
|
|
119
119
|
newMountOptions.propsData.value = {};
|
|
@@ -131,17 +131,21 @@ describe('component: DirectoryConfig', () => {
|
|
|
131
131
|
const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
|
|
132
132
|
|
|
133
133
|
expect(title.exists()).toBe(true);
|
|
134
|
-
expect(radioInput.
|
|
134
|
+
expect(radioInput.isVisible()).toBe(false);
|
|
135
135
|
|
|
136
|
-
expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.
|
|
136
|
+
expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.CUSTOM);
|
|
137
137
|
|
|
138
138
|
// since we have all of the vars empty, then the inputs should not be there
|
|
139
|
-
expect(systemAgentInput.exists()).toBe(
|
|
140
|
-
expect(provisioningInput.exists()).toBe(
|
|
141
|
-
expect(k8sDistroInput.exists()).toBe(
|
|
139
|
+
expect(systemAgentInput.exists()).toBe(true);
|
|
140
|
+
expect(provisioningInput.exists()).toBe(true);
|
|
141
|
+
expect(k8sDistroInput.exists()).toBe(true);
|
|
142
|
+
|
|
143
|
+
expect(systemAgentInput.attributes().disabled).toBeDefined();
|
|
144
|
+
expect(provisioningInput.attributes().disabled).toBeDefined();
|
|
145
|
+
expect(k8sDistroInput.attributes().disabled).toBeDefined();
|
|
142
146
|
});
|
|
143
147
|
|
|
144
|
-
it('radio input should be set to DATA_DIR_RADIO_OPTIONS.CUSTOM with all data dir values existing and different (edit scenario)',
|
|
148
|
+
it('radio input should be set to DATA_DIR_RADIO_OPTIONS.CUSTOM with all data dir values existing and different (edit scenario)', () => {
|
|
145
149
|
const newMountOptions = clone(mountOptions);
|
|
146
150
|
const inputPath = 'some-data-dir';
|
|
147
151
|
|
|
@@ -157,14 +161,20 @@ describe('component: DirectoryConfig', () => {
|
|
|
157
161
|
|
|
158
162
|
expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.CUSTOM);
|
|
159
163
|
|
|
164
|
+
const radioInput = wrapper.find('[data-testid="rke2-directory-config-radio-input"]');
|
|
160
165
|
const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
|
|
161
166
|
const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
|
|
162
167
|
const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
|
|
163
168
|
|
|
169
|
+
expect(radioInput.isVisible()).toBe(false);
|
|
164
170
|
expect(systemAgentInput.isVisible()).toBe(true);
|
|
165
171
|
expect(provisioningInput.isVisible()).toBe(true);
|
|
166
172
|
expect(k8sDistroInput.isVisible()).toBe(true);
|
|
167
173
|
|
|
174
|
+
expect(systemAgentInput.attributes().disabled).toBeDefined();
|
|
175
|
+
expect(provisioningInput.attributes().disabled).toBeDefined();
|
|
176
|
+
expect(k8sDistroInput.attributes().disabled).toBeDefined();
|
|
177
|
+
|
|
168
178
|
expect(wrapper.vm.value.systemAgent).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.AGENT }`);
|
|
169
179
|
expect(wrapper.vm.value.provisioning).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.PROVISIONING }`);
|
|
170
180
|
expect(wrapper.vm.value.k8sDistro).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.K8S_DISTRO_K3S }`);
|
|
@@ -22,11 +22,10 @@ import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
|
|
|
22
22
|
import { findBy, removeObject, clear } from '@shell/utils/array';
|
|
23
23
|
import { createYaml } from '@shell/utils/create-yaml';
|
|
24
24
|
import {
|
|
25
|
-
clone, diff, set, get, isEmpty
|
|
25
|
+
clone, diff, set, get, isEmpty, mergeWithReplaceArrays
|
|
26
26
|
} from '@shell/utils/object';
|
|
27
27
|
import { allHash } from '@shell/utils/promise';
|
|
28
28
|
import { sortBy } from '@shell/utils/sort';
|
|
29
|
-
import { vspherePoolConfigMerge } from '@shell/machine-config/vmwarevsphere-pool-config-merge';
|
|
30
29
|
|
|
31
30
|
import { compare, sortable } from '@shell/utils/version';
|
|
32
31
|
import { isHarvesterSatisfiesVersion, labelForAddon } from '@shell/utils/cluster';
|
|
@@ -1260,7 +1259,7 @@ export default {
|
|
|
1260
1259
|
delete clonedCurrentConfig.metadata;
|
|
1261
1260
|
|
|
1262
1261
|
if (this.provider === VMWARE_VSPHERE) {
|
|
1263
|
-
machinePool.config =
|
|
1262
|
+
machinePool.config = mergeWithReplaceArrays(clonedLatestConfig, clonedCurrentConfig);
|
|
1264
1263
|
} else {
|
|
1265
1264
|
machinePool.config = merge(clonedLatestConfig, clonedCurrentConfig);
|
|
1266
1265
|
}
|
|
@@ -1644,7 +1643,7 @@ export default {
|
|
|
1644
1643
|
const defaultChartValue = this.versionInfo[name];
|
|
1645
1644
|
const key = this.chartVersionKey(name);
|
|
1646
1645
|
|
|
1647
|
-
return
|
|
1646
|
+
return mergeWithReplaceArrays(defaultChartValue?.values, this.userChartValues[key]);
|
|
1648
1647
|
},
|
|
1649
1648
|
|
|
1650
1649
|
initServerAgentArgs() {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
<script>
|
|
3
3
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
4
|
-
import { _CREATE } from '@shell/config/query-params';
|
|
4
|
+
import { _CREATE, _EDIT } from '@shell/config/query-params';
|
|
5
5
|
import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
|
|
6
|
+
import { Banner } from '@components/Banner';
|
|
6
7
|
|
|
7
8
|
export const DATA_DIR_RADIO_OPTIONS = {
|
|
8
9
|
DEFAULT: 'defaultDataDir',
|
|
@@ -23,7 +24,8 @@ export default {
|
|
|
23
24
|
name: 'DirectoryConfig',
|
|
24
25
|
components: {
|
|
25
26
|
LabeledInput,
|
|
26
|
-
RadioGroup
|
|
27
|
+
RadioGroup,
|
|
28
|
+
Banner
|
|
27
29
|
},
|
|
28
30
|
props: {
|
|
29
31
|
mode: {
|
|
@@ -50,9 +52,7 @@ export default {
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
if (this.mode !== _CREATE) {
|
|
53
|
-
|
|
54
|
-
dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.CUSTOM;
|
|
55
|
-
}
|
|
55
|
+
dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.CUSTOM;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
return {
|
|
@@ -85,6 +85,9 @@ export default {
|
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
87
|
computed: {
|
|
88
|
+
isDisabled() {
|
|
89
|
+
return this.mode === _EDIT;
|
|
90
|
+
},
|
|
88
91
|
dataConfigRadioOptions() {
|
|
89
92
|
const defaultDataDirOption = {
|
|
90
93
|
value: DATA_DIR_RADIO_OPTIONS.DEFAULT,
|
|
@@ -148,10 +151,17 @@ export default {
|
|
|
148
151
|
<template>
|
|
149
152
|
<div class="row">
|
|
150
153
|
<div class="col span-8">
|
|
151
|
-
<h3
|
|
154
|
+
<h3>
|
|
152
155
|
{{ t('cluster.directoryConfig.title') }}
|
|
153
156
|
</h3>
|
|
157
|
+
<Banner
|
|
158
|
+
class="mb-20"
|
|
159
|
+
:closable="false"
|
|
160
|
+
color="info"
|
|
161
|
+
label-key="cluster.directoryConfig.banner"
|
|
162
|
+
/>
|
|
154
163
|
<RadioGroup
|
|
164
|
+
v-show="!isDisabled"
|
|
155
165
|
:value="dataConfigRadioValue"
|
|
156
166
|
class="mb-10"
|
|
157
167
|
:mode="mode"
|
|
@@ -167,6 +177,7 @@ export default {
|
|
|
167
177
|
:mode="mode"
|
|
168
178
|
:label="t('cluster.directoryConfig.common.label')"
|
|
169
179
|
:tooltip="t('cluster.directoryConfig.common.tooltip')"
|
|
180
|
+
:disabled="isDisabled"
|
|
170
181
|
data-testid="rke2-directory-config-common-data-dir"
|
|
171
182
|
/>
|
|
172
183
|
<div v-if="dataConfigRadioValue === DATA_DIR_RADIO_OPTIONS.CUSTOM">
|
|
@@ -176,6 +187,7 @@ export default {
|
|
|
176
187
|
:mode="mode"
|
|
177
188
|
:label="t('cluster.directoryConfig.systemAgent.label')"
|
|
178
189
|
:tooltip="t('cluster.directoryConfig.systemAgent.tooltip')"
|
|
190
|
+
:disabled="isDisabled"
|
|
179
191
|
data-testid="rke2-directory-config-systemAgent-data-dir"
|
|
180
192
|
/>
|
|
181
193
|
<LabeledInput
|
|
@@ -184,6 +196,7 @@ export default {
|
|
|
184
196
|
:mode="mode"
|
|
185
197
|
:label="t('cluster.directoryConfig.provisioning.label')"
|
|
186
198
|
:tooltip="t('cluster.directoryConfig.provisioning.tooltip')"
|
|
199
|
+
:disabled="isDisabled"
|
|
187
200
|
data-testid="rke2-directory-config-provisioning-data-dir"
|
|
188
201
|
/>
|
|
189
202
|
<LabeledInput
|
|
@@ -192,6 +205,7 @@ export default {
|
|
|
192
205
|
:mode="mode"
|
|
193
206
|
:label="t('cluster.directoryConfig.k8sDistro.label')"
|
|
194
207
|
:tooltip="t('cluster.directoryConfig.k8sDistro.tooltip')"
|
|
208
|
+
:disabled="isDisabled"
|
|
195
209
|
data-testid="rke2-directory-config-k8sDistro-data-dir"
|
|
196
210
|
/>
|
|
197
211
|
</div>
|
|
@@ -4,28 +4,6 @@ jest.mock('@shell/utils/clipboard', () => {
|
|
|
4
4
|
return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
const importedRKE2ClusterInfo = { status: { driver: 'rke2', provider: 'rke2' } };
|
|
8
|
-
|
|
9
|
-
const provisionedRKE2ClusterInfo = { status: { driver: 'rke2', provider: 'imported' } };
|
|
10
|
-
|
|
11
|
-
const importedK3sClusterInfo = { status: { driver: 'k3s', provider: 'k3s' } };
|
|
12
|
-
|
|
13
|
-
const provisionedK3sClusterInfo = { status: { driver: 'k3s', provider: 'imported' } };
|
|
14
|
-
|
|
15
|
-
const importedAksClusterInfo = { spec: { aksConfig: { imported: true } }, status: { provider: 'aks', driver: 'AKS' } };
|
|
16
|
-
|
|
17
|
-
const provisionedAksClusterInfo = { spec: { aksConfig: { imported: false } }, status: { provider: 'aks', driver: 'AKS' } };
|
|
18
|
-
|
|
19
|
-
const importedRKE1ClusterInfo = { status: { provider: 'rke', driver: 'imported' } };
|
|
20
|
-
|
|
21
|
-
const provisionedRKE1ClusterInfo = { status: { provider: 'rke', driver: 'rancherKubernetesEngine' } };
|
|
22
|
-
|
|
23
|
-
const localRKE1ClusterInfo = { status: { provider: 'rke', driver: 'imported' } };
|
|
24
|
-
|
|
25
|
-
const localRKE2ClusterInfo = { status: { provider: 'rke2', driver: 'rke2' } };
|
|
26
|
-
|
|
27
|
-
const localEKSClusterInfo = { status: { provider: 'eks', driver: 'imported' } };
|
|
28
|
-
|
|
29
7
|
describe('class MgmtCluster', () => {
|
|
30
8
|
describe('provisioner', () => {
|
|
31
9
|
const testCases = [
|
|
@@ -42,24 +20,4 @@ describe('class MgmtCluster', () => {
|
|
|
42
20
|
}
|
|
43
21
|
);
|
|
44
22
|
});
|
|
45
|
-
|
|
46
|
-
describe('isImported', () => {
|
|
47
|
-
it.each([
|
|
48
|
-
[importedRKE2ClusterInfo, true],
|
|
49
|
-
[provisionedRKE2ClusterInfo, false],
|
|
50
|
-
[importedK3sClusterInfo, true],
|
|
51
|
-
[provisionedK3sClusterInfo, false],
|
|
52
|
-
[importedAksClusterInfo, true],
|
|
53
|
-
[provisionedAksClusterInfo, false],
|
|
54
|
-
[importedRKE1ClusterInfo, true],
|
|
55
|
-
[provisionedRKE1ClusterInfo, false],
|
|
56
|
-
[localRKE1ClusterInfo, true],
|
|
57
|
-
[localRKE2ClusterInfo, true],
|
|
58
|
-
[localEKSClusterInfo, true]
|
|
59
|
-
])('should return isImported based on props data', (clusterData, expected) => {
|
|
60
|
-
const cluster = new MgmtCluster(clusterData);
|
|
61
|
-
|
|
62
|
-
expect(cluster.isImported).toBe(expected);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
23
|
});
|
|
@@ -338,6 +338,11 @@ export default class GitRepo extends SteveModel {
|
|
|
338
338
|
for (const bd of bundleDeployments) {
|
|
339
339
|
const clusterId = FleetUtils.clusterIdFromBundleDeploymentLabels(bd.metadata?.labels);
|
|
340
340
|
const c = clusters[clusterId];
|
|
341
|
+
|
|
342
|
+
if (!c) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
|
|
341
346
|
const resources = FleetUtils.resourcesFromBundleDeploymentStatus(bd.status);
|
|
342
347
|
|
|
343
348
|
resources.forEach((r) => {
|
|
@@ -89,30 +89,6 @@ export default class MgmtCluster extends SteveModel {
|
|
|
89
89
|
return pools.filter((x) => x.spec?.clusterName === this.id);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
get isImported() {
|
|
93
|
-
if (this.isLocal) {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
// imported rke2 and k3s have status.driver === rke2 and k3s respectively
|
|
97
|
-
// Provisioned rke2 and k3s have status.driver === imported
|
|
98
|
-
if (this.status?.provider === 'k3s' || this.status?.provider === 'rke2') {
|
|
99
|
-
return this.status?.driver === this.status?.provider;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// imported KEv2
|
|
103
|
-
const kontainerConfigs = ['aksConfig', 'eksConfig', 'gkeConfig'];
|
|
104
|
-
|
|
105
|
-
const isImportedKontainer = kontainerConfigs.filter((key) => {
|
|
106
|
-
return this.spec?.[key]?.imported === true;
|
|
107
|
-
}).length;
|
|
108
|
-
|
|
109
|
-
if (isImportedKontainer) {
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return this.provisioner === 'imported';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
92
|
get provisioner() {
|
|
117
93
|
// For imported K3s clusters, this.status.driver is 'k3s.'
|
|
118
94
|
return this.status?.driver ? this.status.driver : 'imported';
|
|
@@ -117,6 +117,7 @@ export default class GlobalRole extends SteveDescriptionModel {
|
|
|
117
117
|
norman.id = this.id;
|
|
118
118
|
norman.name = this.displayName;
|
|
119
119
|
norman.description = this.description;
|
|
120
|
+
norman.inheritedClusterRoles = this.inheritedClusterRoles;
|
|
120
121
|
|
|
121
122
|
return norman;
|
|
122
123
|
})();
|
|
@@ -282,7 +282,29 @@ export default class ProvCluster extends SteveModel {
|
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
get isImported() {
|
|
285
|
-
|
|
285
|
+
if (this.isLocal) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// imported rke2 and k3s have status.driver === rke2 and k3s respectively
|
|
290
|
+
// Provisioned rke2 and k3s have status.driver === imported
|
|
291
|
+
if (this.mgmt?.status?.provider === 'k3s' || this.mgmt?.status?.provider === 'rke2') {
|
|
292
|
+
return this.mgmt?.status?.driver === this.mgmt?.status?.provider;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// imported KEv2
|
|
296
|
+
// we can't rely on this.provisioner to determine imported-ness for these clusters, as it will return 'aks' 'eks' 'gke' for both provisioned and imported clusters
|
|
297
|
+
const kontainerConfigs = ['aksConfig', 'eksConfig', 'gkeConfig'];
|
|
298
|
+
|
|
299
|
+
const isImportedKontainer = kontainerConfigs.filter((key) => {
|
|
300
|
+
return this.mgmt?.spec?.[key]?.imported === true;
|
|
301
|
+
}).length;
|
|
302
|
+
|
|
303
|
+
if (isImportedKontainer) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return this.provisioner === 'imported';
|
|
286
308
|
}
|
|
287
309
|
|
|
288
310
|
get isCustom() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rancher/shell",
|
|
3
|
-
"version": "3.0.1-rc.
|
|
3
|
+
"version": "3.0.1-rc.3",
|
|
4
4
|
"description": "Rancher Dashboard Shell",
|
|
5
5
|
"repository": "https://github.com/rancherlabs/dashboard",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -36,7 +36,6 @@
|
|
|
36
36
|
"@babel/plugin-proposal-private-property-in-object": "7.14.5",
|
|
37
37
|
"@babel/preset-typescript": "7.16.7",
|
|
38
38
|
"@novnc/novnc": "1.2.0",
|
|
39
|
-
"@nuxtjs/axios": "5.13.6",
|
|
40
39
|
"@popperjs/core": "2.4.4",
|
|
41
40
|
"@rancher/icons": "2.0.29",
|
|
42
41
|
"@types/is-url": "1.2.30",
|
|
@@ -51,6 +50,8 @@
|
|
|
51
50
|
"@vue/vue3-jest": "^27.0.0-alpha.1",
|
|
52
51
|
"add": "2.0.6",
|
|
53
52
|
"ansi_up": "5.0.0",
|
|
53
|
+
"axios": "0.21.4",
|
|
54
|
+
"axios-retry": "3.1.9",
|
|
54
55
|
"babel-eslint": "10.1.0",
|
|
55
56
|
"babel-plugin-module-resolver": "4.0.0",
|
|
56
57
|
"babel-preset-vue": "2.0.2",
|
|
@@ -73,6 +74,7 @@
|
|
|
73
74
|
"d3-selection": "1.4.1",
|
|
74
75
|
"dagre-d3": "0.6.4",
|
|
75
76
|
"dayjs": "1.8.29",
|
|
77
|
+
"defu": "5.0.1",
|
|
76
78
|
"diff2html": "3.4.24",
|
|
77
79
|
"dompurify": "2.5.4",
|
|
78
80
|
"element-matches": "^0.1.2",
|
|
@@ -160,7 +162,7 @@
|
|
|
160
162
|
"semver": "7.5.4",
|
|
161
163
|
"@types/lodash": "4.17.5",
|
|
162
164
|
"@types/node": "~20.10.0",
|
|
163
|
-
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
|
|
165
|
+
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
|
|
164
166
|
},
|
|
165
167
|
"nyc": {
|
|
166
168
|
"extension": [
|
|
@@ -168,4 +170,4 @@
|
|
|
168
170
|
".vue"
|
|
169
171
|
]
|
|
170
172
|
}
|
|
171
|
-
}
|
|
173
|
+
}
|
package/pages/auth/login.vue
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import loadPlugins from '@shell/plugins/plugin';
|
|
32
32
|
import Loading from '@shell/components/Loading';
|
|
33
33
|
import { getGlobalBannerFontSizes } from '@shell/utils/banners';
|
|
34
|
+
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
34
35
|
|
|
35
36
|
export default {
|
|
36
37
|
name: 'Login',
|
|
@@ -64,7 +65,7 @@ export default {
|
|
|
64
65
|
},
|
|
65
66
|
|
|
66
67
|
computed: {
|
|
67
|
-
...mapGetters(['
|
|
68
|
+
...mapGetters(['isSingleProduct']),
|
|
68
69
|
...mapGetters({ t: 'i18n/t', hasMultipleLocales: 'i18n/hasMultipleLocales' }),
|
|
69
70
|
|
|
70
71
|
loggedOutSuccessMsg() {
|
|
@@ -77,6 +78,10 @@ export default {
|
|
|
77
78
|
return this.t('login.loggedOut');
|
|
78
79
|
},
|
|
79
80
|
|
|
81
|
+
isHarvester() {
|
|
82
|
+
return this.isSingleProduct?.productName === HARVESTER;
|
|
83
|
+
},
|
|
84
|
+
|
|
80
85
|
singleProvider() {
|
|
81
86
|
return this.providers.length === 1 ? this.providers[0] : undefined;
|
|
82
87
|
},
|
|
@@ -498,7 +503,7 @@ export default {
|
|
|
498
503
|
</div>
|
|
499
504
|
</template>
|
|
500
505
|
<div
|
|
501
|
-
v-if="showLocaleSelector && hasMultipleLocales && !
|
|
506
|
+
v-if="showLocaleSelector && hasMultipleLocales && !isHarvester"
|
|
502
507
|
class="locale-selector"
|
|
503
508
|
>
|
|
504
509
|
<LocaleSelector
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { mapGetters } from 'vuex';
|
|
3
2
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
4
3
|
import Tabbed from '@shell/components/Tabbed';
|
|
5
4
|
import { MANAGEMENT } from '@shell/config/types';
|
|
@@ -8,7 +7,6 @@ import Loading from '@shell/components/Loading';
|
|
|
8
7
|
import { SUBTYPE_MAPPING, CREATE_VERBS } from '@shell/models/management.cattle.io.roletemplate';
|
|
9
8
|
import { NAME } from '@shell/config/product/auth';
|
|
10
9
|
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
11
|
-
import { Banner } from '@components/Banner';
|
|
12
10
|
|
|
13
11
|
const GLOBAL = SUBTYPE_MAPPING.GLOBAL.key;
|
|
14
12
|
const CLUSTER = SUBTYPE_MAPPING.CLUSTER.key;
|
|
@@ -33,7 +31,7 @@ export default {
|
|
|
33
31
|
name: 'Roles',
|
|
34
32
|
|
|
35
33
|
components: {
|
|
36
|
-
Tab, Tabbed, ResourceTable, Loading
|
|
34
|
+
Tab, Tabbed, ResourceTable, Loading
|
|
37
35
|
},
|
|
38
36
|
|
|
39
37
|
async fetch() {
|
|
@@ -100,8 +98,6 @@ export default {
|
|
|
100
98
|
},
|
|
101
99
|
|
|
102
100
|
computed: {
|
|
103
|
-
...mapGetters(['releaseNotesUrl']),
|
|
104
|
-
|
|
105
101
|
globalResources() {
|
|
106
102
|
return this.globalRoles;
|
|
107
103
|
},
|
|
@@ -183,12 +179,6 @@ export default {
|
|
|
183
179
|
:weight="tabs[GLOBAL].weight"
|
|
184
180
|
:label-key="tabs[GLOBAL].labelKey"
|
|
185
181
|
>
|
|
186
|
-
<Banner
|
|
187
|
-
color="warning"
|
|
188
|
-
class="mb-20"
|
|
189
|
-
>
|
|
190
|
-
<span v-clean-html="t('rbac.globalRoles.role.restricted-admin.deprecation', { releaseNotesUrl }, true)" />
|
|
191
|
-
</Banner>
|
|
192
182
|
<ResourceTable
|
|
193
183
|
:schema="tabs[GLOBAL].schema"
|
|
194
184
|
:rows="globalResources"
|
|
@@ -3,6 +3,7 @@ import Dashboard from '@shell/pages/c/_cluster/explorer/index.vue';
|
|
|
3
3
|
import { shallowMount } from '@vue/test-utils';
|
|
4
4
|
import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
5
5
|
import { NODE_ARCHITECTURE } from '@shell/config/labels-annotations';
|
|
6
|
+
import { WORKLOAD_TYPES } from '@shell/config/types';
|
|
6
7
|
|
|
7
8
|
describe('page: cluster dashboard', () => {
|
|
8
9
|
const mountOptions = {
|
|
@@ -97,12 +98,81 @@ describe('page: cluster dashboard', () => {
|
|
|
97
98
|
[STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 1],
|
|
98
99
|
[STATES_ENUM.HEALTHY, 'icon-checkmark', true, false, false, [{ status: 'True' }], 1, 0],
|
|
99
100
|
]]
|
|
100
|
-
])('%p cluster - %p agent health box', (_, agentId, isLocal, agentResources, statuses) => {
|
|
101
|
+
])('%p cluster - %p agent health box :', (_, agentId, isLocal, agentResources, statuses) => {
|
|
102
|
+
it.each(statuses)('should NOT show %p status due to missing canList permissions', (status, iconClass, isLoaded, disconnected, error, conditions, readyReplicas, unavailableReplicas) => {
|
|
103
|
+
const options = clone(mountOptions);
|
|
104
|
+
|
|
105
|
+
options.global.mocks.$store.getters.currentCluster.isLocal = isLocal;
|
|
106
|
+
|
|
107
|
+
const resources = agentResources.reduce((acc, r) => {
|
|
108
|
+
const agent = {
|
|
109
|
+
metadata: { state: { error } },
|
|
110
|
+
spec: { replicas: 1 },
|
|
111
|
+
status: {
|
|
112
|
+
readyReplicas,
|
|
113
|
+
unavailableReplicas,
|
|
114
|
+
conditions
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return isLoaded ? {
|
|
119
|
+
...acc,
|
|
120
|
+
[r]: agent
|
|
121
|
+
} : 'loading';
|
|
122
|
+
}, {});
|
|
123
|
+
|
|
124
|
+
const wrapper = shallowMount(Dashboard, {
|
|
125
|
+
...options,
|
|
126
|
+
data: () => ({
|
|
127
|
+
...resources,
|
|
128
|
+
disconnected,
|
|
129
|
+
canViewAgents: true
|
|
130
|
+
})
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const box = wrapper.find(`[data-testid="k8s-service-${ agentId }"]`);
|
|
134
|
+
|
|
135
|
+
expect(box.exists()).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe.each([
|
|
140
|
+
['local', 'fleet', true, ['fleetDeployment', 'fleetStatefulSet'], [
|
|
141
|
+
[STATES_ENUM.IN_PROGRESS, 'icon-spinner', false, false, false, '', 0, 0],
|
|
142
|
+
[STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, false, [{ status: 'False' }], 0, 0],
|
|
143
|
+
[STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, true, [{ status: 'True' }], 0, 0],
|
|
144
|
+
[STATES_ENUM.WARNING, 'icon-warning', true, true, false, [{ status: 'True' }], 0, 0],
|
|
145
|
+
[STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 0],
|
|
146
|
+
[STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 1],
|
|
147
|
+
[STATES_ENUM.HEALTHY, 'icon-checkmark', true, false, false, [{ status: 'True' }], 1, 0],
|
|
148
|
+
]],
|
|
149
|
+
['downstream RKE2', 'fleet', false, ['fleetStatefulSet'], [
|
|
150
|
+
[STATES_ENUM.IN_PROGRESS, 'icon-spinner', false, false, false, '', 0, 0],
|
|
151
|
+
[STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, false, [{ status: 'False' }], 0, 0],
|
|
152
|
+
[STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, true, [{ status: 'True' }], 0, 0],
|
|
153
|
+
[STATES_ENUM.WARNING, 'icon-warning', true, true, false, [{ status: 'True' }], 0, 0],
|
|
154
|
+
[STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 0],
|
|
155
|
+
[STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 1],
|
|
156
|
+
[STATES_ENUM.HEALTHY, 'icon-checkmark', true, false, false, [{ status: 'True' }], 1, 0],
|
|
157
|
+
]],
|
|
158
|
+
['downstream RKE2', 'cattle', false, ['cattleDeployment'], [
|
|
159
|
+
[STATES_ENUM.IN_PROGRESS, 'icon-spinner', false, false, false, '', 0, 0],
|
|
160
|
+
[STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, false, [{ status: 'False' }], 0, 0],
|
|
161
|
+
[STATES_ENUM.UNHEALTHY, 'icon-warning', true, true, false, [{ status: 'True' }], 0, 0],
|
|
162
|
+
[STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, true, [{ status: 'True' }], 0, 0],
|
|
163
|
+
[STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 0],
|
|
164
|
+
[STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 1],
|
|
165
|
+
[STATES_ENUM.HEALTHY, 'icon-checkmark', true, false, false, [{ status: 'True' }], 1, 0],
|
|
166
|
+
]]
|
|
167
|
+
])('%p cluster - %p agent health box ::', (_, agentId, isLocal, agentResources, statuses) => {
|
|
101
168
|
it.each(statuses)('should show %p status', (status, iconClass, isLoaded, disconnected, error, conditions, readyReplicas, unavailableReplicas) => {
|
|
102
169
|
const options = clone(mountOptions);
|
|
103
170
|
|
|
104
171
|
options.global.mocks.$store.getters.currentCluster.isLocal = isLocal;
|
|
105
172
|
|
|
173
|
+
// let's pass the canList now
|
|
174
|
+
options.global.mocks.$store.getters['cluster/canList'] = (type: string) => !!(type === WORKLOAD_TYPES.DEPLOYMENT) || !!(type === WORKLOAD_TYPES.STATEFUL_SET);
|
|
175
|
+
|
|
106
176
|
const resources = agentResources.reduce((acc, r) => {
|
|
107
177
|
const agent = {
|
|
108
178
|
metadata: { state: { error } },
|
|
@@ -188,7 +188,11 @@ export default {
|
|
|
188
188
|
},
|
|
189
189
|
|
|
190
190
|
fleetAgentNamespace() {
|
|
191
|
-
|
|
191
|
+
if (this.currentCluster.isLocal) {
|
|
192
|
+
return this.$store.getters['cluster/canList'](WORKLOAD_TYPES.DEPLOYMENT) && this.$store.getters['cluster/canList'](WORKLOAD_TYPES.STATEFUL_SET) && this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-fleet-system');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return this.$store.getters['cluster/canList'](WORKLOAD_TYPES.STATEFUL_SET) && this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-fleet-system');
|
|
192
196
|
},
|
|
193
197
|
|
|
194
198
|
cattleAgentNamespace() {
|
|
@@ -196,7 +200,7 @@ export default {
|
|
|
196
200
|
return;
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
return this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-system');
|
|
203
|
+
return this.$store.getters['cluster/canList'](WORKLOAD_TYPES.DEPLOYMENT) && this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-system');
|
|
200
204
|
},
|
|
201
205
|
|
|
202
206
|
canViewAgents() {
|
|
@@ -4,27 +4,34 @@ GITHUB_RELEASE_TAG=$1
|
|
|
4
4
|
GITHUB_RUN_ID=$2
|
|
5
5
|
GITHUB_WORKFLOW_TYPE=$3
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
echo "Parse tag name - evaluating release tag $GITHUB_RELEASE_TAG"
|
|
8
|
+
|
|
9
|
+
# Ensure "catalog" workflow release tag name matches the root <pkg-name>
|
|
8
10
|
if [[ "${GITHUB_WORKFLOW_TYPE}" == "catalog" ]]; then
|
|
11
|
+
BASE_EXT=$(jq -r .name package.json)
|
|
12
|
+
EXT_VERSION=$(jq -r .version package.json)
|
|
13
|
+
|
|
14
|
+
if [[ "${GITHUB_RELEASE_TAG}" != "${BASE_EXT}-${EXT_VERSION}" ]]; then
|
|
15
|
+
echo -e "release tag doesn't match catalog tag: release tag -> ${GITHUB_RELEASE_TAG} ::: curr catalog tag -> ${BASE_EXT}-${EXT_VERSION}"
|
|
16
|
+
gh run cancel ${GITHUB_RUN_ID}
|
|
17
|
+
fi
|
|
18
|
+
# Ensure "chart" workflow release tag name matches some pkg/<pkg-name>
|
|
19
|
+
else
|
|
20
|
+
NO_MATCHES="true"
|
|
21
|
+
|
|
9
22
|
for d in pkg/*/ ; do
|
|
10
23
|
pkg=$(basename $d)
|
|
11
24
|
|
|
12
25
|
PKG_VERSION=$(jq -r .version pkg/${pkg}/package.json)
|
|
13
|
-
|
|
26
|
+
CURR_PKG_TAG="${pkg}-${PKG_VERSION}"
|
|
14
27
|
|
|
15
|
-
if [[ "${GITHUB_RELEASE_TAG}" == "${
|
|
16
|
-
|
|
17
|
-
else
|
|
18
|
-
continue
|
|
28
|
+
if [[ "${GITHUB_RELEASE_TAG}" == "${CURR_PKG_TAG}" ]]; then
|
|
29
|
+
NO_MATCHES="false"
|
|
19
30
|
fi
|
|
20
31
|
done
|
|
21
|
-
else
|
|
22
|
-
# Ensure "charts" workflow release tag name does not match the root <pkg-name>
|
|
23
|
-
BASE_EXT=$(jq -r .name package.json)
|
|
24
|
-
EXT_VERSION=$(jq -r .version package.json)
|
|
25
32
|
|
|
26
|
-
if [[ "${
|
|
27
|
-
echo -e "tag: ${GITHUB_RELEASE_TAG}"
|
|
33
|
+
if [[ "${NO_MATCHES}" == "true" ]]; then
|
|
34
|
+
echo -e "release tag doesn't match any chart tag: ${GITHUB_RELEASE_TAG}. Check your pkg/<!-YOUR-EXT-> folders and corresponding versions to complete the match"
|
|
28
35
|
gh run cancel ${GITHUB_RUN_ID}
|
|
29
36
|
fi
|
|
30
37
|
fi
|
package/scripts/publish-shell.sh
CHANGED
|
@@ -6,7 +6,6 @@ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
|
|
6
6
|
BASE_DIR="$(cd $SCRIPT_DIR && cd ../.. && pwd)"
|
|
7
7
|
SHELL_DIR=$BASE_DIR/shell/
|
|
8
8
|
CREATORS_DIR=$BASE_DIR/creators/extension
|
|
9
|
-
PUBLISH_ARGS="--no-git-tag-version --access public --registry $NPM_REGISTRY $NPM_TAG"
|
|
10
9
|
FORCE_PUBLISH_TO_NPM="false"
|
|
11
10
|
DEFAULT_NPM_REGISTRY="https://registry.npmjs.org"
|
|
12
11
|
|
|
@@ -31,6 +30,8 @@ if [ "$FORCE_PUBLISH_TO_NPM" == "true" ]; then
|
|
|
31
30
|
export NPM_REGISTRY=$DEFAULT_NPM_REGISTRY
|
|
32
31
|
fi
|
|
33
32
|
|
|
33
|
+
PUBLISH_ARGS="--no-git-tag-version --access public --registry $NPM_REGISTRY"
|
|
34
|
+
|
|
34
35
|
pushd ${SHELL_DIR} >/dev/null
|
|
35
36
|
|
|
36
37
|
function publish() {
|
|
@@ -47,9 +48,16 @@ function publish() {
|
|
|
47
48
|
|
|
48
49
|
# if the PKG_VERSION has a - it means it will be a pre-release
|
|
49
50
|
if [[ $PKG_VERSION == *"-"* ]]; then
|
|
50
|
-
PUBLISH_ARGS="
|
|
51
|
+
PUBLISH_ARGS="$PUBLISH_ARGS --tag pre-release"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# when testing the workflow, we don't want to actually do an npm publish but only a dry run
|
|
55
|
+
if [ ${DRY_RUN} == "true" ]; then
|
|
56
|
+
PUBLISH_ARGS="$PUBLISH_ARGS --dry-run"
|
|
51
57
|
fi
|
|
52
58
|
|
|
59
|
+
echo "Publish to NPM - arguments ::: ${PUBLISH_ARGS}"
|
|
60
|
+
|
|
53
61
|
echo "Publishing ${NAME} from ${FOLDER}"
|
|
54
62
|
pushd ${FOLDER} >/dev/null
|
|
55
63
|
|
|
@@ -169,8 +169,9 @@ fi
|
|
|
169
169
|
|
|
170
170
|
# function to clone repos and install dependencies (including the newly published shell version)
|
|
171
171
|
function clone_repo_test_extension_build() {
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
REPO_ORG=$1
|
|
173
|
+
REPO_NAME=$2
|
|
174
|
+
PKG_NAME=$3
|
|
174
175
|
|
|
175
176
|
echo -e "\nSetting up $REPO_NAME repository locally\n"
|
|
176
177
|
|
|
@@ -183,7 +184,7 @@ function clone_repo_test_extension_build() {
|
|
|
183
184
|
fi
|
|
184
185
|
|
|
185
186
|
# cloning repo
|
|
186
|
-
git clone https://github.com
|
|
187
|
+
git clone https://github.com/$REPO_ORG/$REPO_NAME.git
|
|
187
188
|
pushd ${BASE_DIR}/$REPO_NAME
|
|
188
189
|
|
|
189
190
|
echo -e "\nInstalling dependencies for $REPO_NAME\n"
|
|
@@ -196,9 +197,6 @@ function clone_repo_test_extension_build() {
|
|
|
196
197
|
sed -i.bak -e "s/\"\@rancher\/shell\": \"[0-9]*.[0-9]*.[0-9]*\",/\"\@rancher\/shell\": \"${SHELL_VERSION}\",/g" package.json
|
|
197
198
|
rm package.json.bak
|
|
198
199
|
|
|
199
|
-
# we need to remove yarn.lock, otherwise it would install a version that we don't want
|
|
200
|
-
rm yarn.lock
|
|
201
|
-
|
|
202
200
|
echo -e "\nInstalling newly built shell version\n"
|
|
203
201
|
|
|
204
202
|
# installing new version of shell
|
|
@@ -223,8 +221,9 @@ function clone_repo_test_extension_build() {
|
|
|
223
221
|
|
|
224
222
|
# Here we just add the extension that we want to include as a check (all our official extensions should be included here)
|
|
225
223
|
# Don't forget to add the unit tests exception to clone_repo_test_extension_build function if a new extension has those
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
224
|
+
clone_repo_test_extension_build "rancher" "kubewarden-ui" "kubewarden"
|
|
225
|
+
clone_repo_test_extension_build "rancher" "elemental-ui" "elemental"
|
|
226
|
+
clone_repo_test_extension_build "neuvector" "manager-ext" "neuvector-ui-ext"
|
|
227
|
+
# clone_repo_test_extension_build "rancher" "capi-ui-extension" "capi"
|
|
229
228
|
|
|
230
229
|
echo "All done"
|
package/scripts/typegen.sh
CHANGED
|
@@ -31,9 +31,11 @@ ${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/plugins/dashboard-store/normalize
|
|
|
31
31
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/plugins/dashboard-store/resource-class.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/plugins/dashboard-store/ > /dev/null
|
|
32
32
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/plugins/dashboard-store/classify.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/plugins/dashboard-store/ > /dev/null
|
|
33
33
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/plugins/dashboard-store/actions.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/plugins/dashboard-store/ > /dev/null
|
|
34
|
+
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/plugins/steve/steve-class.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/plugins/steve/ > /dev/null
|
|
34
35
|
|
|
35
36
|
# # mixins
|
|
36
37
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/mixins/create-edit-view/index.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/mixins/create-edit-view > /dev/null
|
|
38
|
+
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/mixins/resource-fetch.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/mixins > /dev/null
|
|
37
39
|
|
|
38
40
|
# # models
|
|
39
41
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/models/namespace.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/models/ > /dev/null
|
package/types/shell/index.d.ts
CHANGED
|
@@ -2352,6 +2352,67 @@ declare var _default: import("vue").DefineComponent<{
|
|
|
2352
2352
|
export default _default;
|
|
2353
2353
|
}
|
|
2354
2354
|
|
|
2355
|
+
// @shell/mixins/resource-fetch
|
|
2356
|
+
|
|
2357
|
+
declare module '@shell/mixins/resource-fetch' {
|
|
2358
|
+
declare namespace _default {
|
|
2359
|
+
const mixins: any[];
|
|
2360
|
+
const inheritAttrs: boolean;
|
|
2361
|
+
function data(): {
|
|
2362
|
+
perfConfig: {};
|
|
2363
|
+
init: boolean;
|
|
2364
|
+
multipleResources: any[];
|
|
2365
|
+
loadResources: any[];
|
|
2366
|
+
hasManualRefresh: boolean;
|
|
2367
|
+
watch: boolean;
|
|
2368
|
+
isTooManyItemsToAutoUpdate: boolean;
|
|
2369
|
+
force: boolean;
|
|
2370
|
+
incremental: boolean;
|
|
2371
|
+
fetchedResourceType: any[];
|
|
2372
|
+
paginating: any;
|
|
2373
|
+
};
|
|
2374
|
+
function data(): {
|
|
2375
|
+
perfConfig: {};
|
|
2376
|
+
init: boolean;
|
|
2377
|
+
multipleResources: any[];
|
|
2378
|
+
loadResources: any[];
|
|
2379
|
+
hasManualRefresh: boolean;
|
|
2380
|
+
watch: boolean;
|
|
2381
|
+
isTooManyItemsToAutoUpdate: boolean;
|
|
2382
|
+
force: boolean;
|
|
2383
|
+
incremental: boolean;
|
|
2384
|
+
fetchedResourceType: any[];
|
|
2385
|
+
paginating: any;
|
|
2386
|
+
};
|
|
2387
|
+
function beforeUnmount(): void;
|
|
2388
|
+
function beforeUnmount(): void;
|
|
2389
|
+
namespace computed {
|
|
2390
|
+
function rows(): any;
|
|
2391
|
+
function rows(): any;
|
|
2392
|
+
function loading(): any;
|
|
2393
|
+
function loading(): any;
|
|
2394
|
+
const refreshFlag: import("vuex").Computed;
|
|
2395
|
+
}
|
|
2396
|
+
namespace watch {
|
|
2397
|
+
function refreshFlag(neu: any): Promise<void>;
|
|
2398
|
+
function refreshFlag(neu: any): Promise<void>;
|
|
2399
|
+
}
|
|
2400
|
+
namespace methods {
|
|
2401
|
+
function $initializeFetchData(type: any, multipleResources: any[], storeType: any): void;
|
|
2402
|
+
function $initializeFetchData(type: any, multipleResources: any[], storeType: any): void;
|
|
2403
|
+
function $fetchType(type: any, multipleResources: any[], storeType: any): any;
|
|
2404
|
+
function $fetchType(type: any, multipleResources: any[], storeType: any): any;
|
|
2405
|
+
function __getCountForResources(resourceNames: any, namespace: any, storeType: any): any;
|
|
2406
|
+
function __getCountForResources(resourceNames: any, namespace: any, storeType: any): any;
|
|
2407
|
+
function __getCountForResource(resourceName: any, namespace: any, storeType: any): any;
|
|
2408
|
+
function __getCountForResource(resourceName: any, namespace: any, storeType: any): any;
|
|
2409
|
+
function __gatherResourceFetchData(resourceName: any, multipleResources: any, currStore: any): void;
|
|
2410
|
+
function __gatherResourceFetchData(resourceName: any, multipleResources: any, currStore: any): void;
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
export default _default;
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2355
2416
|
// @shell/models/namespace
|
|
2356
2417
|
|
|
2357
2418
|
declare module '@shell/models/namespace' {
|
|
@@ -3115,6 +3176,40 @@ export default class Resource {
|
|
|
3115
3176
|
}
|
|
3116
3177
|
}
|
|
3117
3178
|
|
|
3179
|
+
// @shell/plugins/steve/hybrid-class
|
|
3180
|
+
|
|
3181
|
+
declare module '@shell/plugins/steve/hybrid-class' {
|
|
3182
|
+
export function cleanHybridResources(data: any): any;
|
|
3183
|
+
export default class HybridModel {
|
|
3184
|
+
constructor(data: any, ctx: any, rehydrateNamespace?: any, setClone?: boolean);
|
|
3185
|
+
get labels(): any;
|
|
3186
|
+
setLabels(val: any): void;
|
|
3187
|
+
metadata: {};
|
|
3188
|
+
setLabel(key: any, val: any): void;
|
|
3189
|
+
get annotations(): any;
|
|
3190
|
+
setAnnotations(val: any): void;
|
|
3191
|
+
setAnnotation(key: any, val: any): void;
|
|
3192
|
+
get state(): any;
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
// @shell/plugins/steve/steve-class
|
|
3197
|
+
|
|
3198
|
+
declare module '@shell/plugins/steve/steve-class' {
|
|
3199
|
+
export default class SteveModel extends HybridModel {
|
|
3200
|
+
get name(): any;
|
|
3201
|
+
get namespace(): any;
|
|
3202
|
+
/**
|
|
3203
|
+
* Set description based on the type of model available with private fallback
|
|
3204
|
+
*/
|
|
3205
|
+
set description(arg: any);
|
|
3206
|
+
get description(): any;
|
|
3207
|
+
_description: any;
|
|
3208
|
+
cleanForSave(data: any, forNew: any): any;
|
|
3209
|
+
}
|
|
3210
|
+
import HybridModel from "./hybrid-class";
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3118
3213
|
// @shell/store/features
|
|
3119
3214
|
|
|
3120
3215
|
declare module '@shell/store/features' {
|
|
@@ -3940,6 +4035,26 @@ export function dropKeys(obj: any, keys: any): void;
|
|
|
3940
4035
|
* @returns
|
|
3941
4036
|
*/
|
|
3942
4037
|
export function deepToRaw(obj: any, cache?: any): any;
|
|
4038
|
+
/**
|
|
4039
|
+
* Helper function to alter Lodash merge function default behaviour on merging arrays while updating machine pool configuration.
|
|
4040
|
+
*
|
|
4041
|
+
* In rke2.vue, the syncMachineConfigWithLatest function updates machine pool configuration by
|
|
4042
|
+
* merging the latest configuration received from the backend with the current configuration updated by the user.
|
|
4043
|
+
* However, Lodash's merge function treats arrays like object so index values are merged and not appended to arrays
|
|
4044
|
+
* resulting in undesired outcomes for us, Example:
|
|
4045
|
+
*
|
|
4046
|
+
* const lastSavedConfigFromBE = { a: ["test"] };
|
|
4047
|
+
* const currentConfigByUser = { a: [] };
|
|
4048
|
+
* merge(lastSavedConfigFromBE, currentConfigByUser); // returns { a: ["test"] }; but we expect { a: [] };
|
|
4049
|
+
*
|
|
4050
|
+
* More info: https://github.com/lodash/lodash/issues/1313
|
|
4051
|
+
*
|
|
4052
|
+
* This helper function addresses the issue by always replacing the old array with the new array during the merge process.
|
|
4053
|
+
*
|
|
4054
|
+
* This helper is used for another case in rke2.vue to handle merging addon chart default values with the user's current values.
|
|
4055
|
+
* It fixed https://github.com/rancher/dashboard/issues/12418
|
|
4056
|
+
*/
|
|
4057
|
+
export function mergeWithReplaceArrays(obj1?: {}, obj2?: {}): any;
|
|
3943
4058
|
export { isEqualBasic as isEqual };
|
|
3944
4059
|
export function toDictionary(array: any, callback: any): any;
|
|
3945
4060
|
/**
|
|
@@ -4588,7 +4703,7 @@ declare module '@shell/utils/version' {
|
|
|
4588
4703
|
export function parse(str: any): any;
|
|
4589
4704
|
export function sortable(str: any): any;
|
|
4590
4705
|
export function compare(in1: any, in2: any): any;
|
|
4591
|
-
export function isPrerelease(version
|
|
4706
|
+
export function isPrerelease(version?: string): boolean;
|
|
4592
4707
|
export function isDevBuild(version: any): boolean;
|
|
4593
4708
|
export function getVersionInfo(store: any): {
|
|
4594
4709
|
displayVersion: any;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { reactive, isReactive } from 'vue';
|
|
2
2
|
import {
|
|
3
|
-
clone, get, getter, isEmpty, toDictionary, remove, diff, definedKeys, deepToRaw
|
|
3
|
+
clone, get, getter, isEmpty, toDictionary, remove, diff, definedKeys, deepToRaw, mergeWithReplaceArrays
|
|
4
4
|
} from '@shell/utils/object';
|
|
5
5
|
|
|
6
6
|
describe('fx: get', () => {
|
|
@@ -378,3 +378,32 @@ describe('fx: deepToRaw', () => {
|
|
|
378
378
|
});
|
|
379
379
|
});
|
|
380
380
|
});
|
|
381
|
+
|
|
382
|
+
describe('fx: mergeWithReplaceArrays', () => {
|
|
383
|
+
const testCases: Array<[object?, object?, object?]> = [
|
|
384
|
+
// Some array test cases, an array from the first object should be replaced with the array from the second object
|
|
385
|
+
[{ a: ['one'] }, { a: [] }, { a: [] }],
|
|
386
|
+
[{ a: ['one', 'two'] }, { a: ['one', 'two', 'three'] }, { a: ['one', 'two', 'three'] }],
|
|
387
|
+
[{ a: ['one', 'two'], b: ['three', 'four'] }, { a: ['one'], b: [] }, { a: ['one'], b: [] }],
|
|
388
|
+
[{
|
|
389
|
+
a: ['one', 'two'], b: ['three', 'four'], c: 'five'
|
|
390
|
+
}, { a: ['one'], b: [] }, {
|
|
391
|
+
a: ['one'], b: [], c: 'five'
|
|
392
|
+
}],
|
|
393
|
+
// Some other test cases
|
|
394
|
+
[{ a: 'one' }, { b: 'two' }, { a: 'one', b: 'two' }],
|
|
395
|
+
[{ a: 'one' }, { a: '', b: 'two' }, { a: '', b: 'two' }],
|
|
396
|
+
[{ a: 'one', b: 'two' }, { a: 1, c: { d: null } }, {
|
|
397
|
+
a: 1, b: 'two', c: { d: null }
|
|
398
|
+
}],
|
|
399
|
+
[undefined, undefined, {}],
|
|
400
|
+
[{}, undefined, {}],
|
|
401
|
+
[undefined, {}, {}],
|
|
402
|
+
];
|
|
403
|
+
|
|
404
|
+
it.each(testCases)('should merge properly', (obj1, obj2, expected) => {
|
|
405
|
+
const result = mergeWithReplaceArrays(obj1, obj2);
|
|
406
|
+
|
|
407
|
+
expect(result).toStrictEqual(expected);
|
|
408
|
+
});
|
|
409
|
+
});
|
package/utils/object.js
CHANGED
|
@@ -8,6 +8,7 @@ import isObject from 'lodash/isObject';
|
|
|
8
8
|
import isArray from 'lodash/isArray';
|
|
9
9
|
import isEqual from 'lodash/isEqual';
|
|
10
10
|
import difference from 'lodash/difference';
|
|
11
|
+
import mergeWith from 'lodash/mergeWith';
|
|
11
12
|
import { splitObjectPath, joinObjectPath } from '@shell/utils/string';
|
|
12
13
|
import { addObject } from '@shell/utils/array';
|
|
13
14
|
|
|
@@ -471,3 +472,30 @@ export function deepToRaw(obj, cache = new WeakSet()) {
|
|
|
471
472
|
return result;
|
|
472
473
|
}
|
|
473
474
|
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Helper function to alter Lodash merge function default behaviour on merging arrays while updating machine pool configuration.
|
|
478
|
+
*
|
|
479
|
+
* In rke2.vue, the syncMachineConfigWithLatest function updates machine pool configuration by
|
|
480
|
+
* merging the latest configuration received from the backend with the current configuration updated by the user.
|
|
481
|
+
* However, Lodash's merge function treats arrays like object so index values are merged and not appended to arrays
|
|
482
|
+
* resulting in undesired outcomes for us, Example:
|
|
483
|
+
*
|
|
484
|
+
* const lastSavedConfigFromBE = { a: ["test"] };
|
|
485
|
+
* const currentConfigByUser = { a: [] };
|
|
486
|
+
* merge(lastSavedConfigFromBE, currentConfigByUser); // returns { a: ["test"] }; but we expect { a: [] };
|
|
487
|
+
*
|
|
488
|
+
* More info: https://github.com/lodash/lodash/issues/1313
|
|
489
|
+
*
|
|
490
|
+
* This helper function addresses the issue by always replacing the old array with the new array during the merge process.
|
|
491
|
+
*
|
|
492
|
+
* This helper is used for another case in rke2.vue to handle merging addon chart default values with the user's current values.
|
|
493
|
+
* It fixed https://github.com/rancher/dashboard/issues/12418
|
|
494
|
+
*/
|
|
495
|
+
export function mergeWithReplaceArrays(obj1 = {}, obj2 = {}) {
|
|
496
|
+
return mergeWith(obj1, obj2, (obj1Value, obj2Value) => {
|
|
497
|
+
if (Array.isArray(obj1Value) && Array.isArray(obj2Value)) {
|
|
498
|
+
return obj2Value;
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
package/utils/version.js
CHANGED
package/vue.config.js
CHANGED
|
@@ -131,11 +131,11 @@ const instrumentCode = () => {
|
|
|
131
131
|
};
|
|
132
132
|
|
|
133
133
|
const getLoaders = (SHELL_ABS) => [
|
|
134
|
-
//
|
|
134
|
+
// no fallback for pre-2013 browsers https://caniuse.com/webworkers
|
|
135
135
|
{
|
|
136
136
|
test: /web-worker.[a-z-]+.js/i,
|
|
137
137
|
loader: 'worker-loader',
|
|
138
|
-
options: { inline: 'fallback' },
|
|
138
|
+
options: { inline: 'no-fallback' },
|
|
139
139
|
},
|
|
140
140
|
// Handler for csv files (e.g. ec2 instance data)
|
|
141
141
|
{
|
|
@@ -462,7 +462,7 @@ const getWatcherIgnored = (excludes) => {
|
|
|
462
462
|
/dist-pkg/,
|
|
463
463
|
/scripts\/standalone/,
|
|
464
464
|
];
|
|
465
|
-
const pathExcludedPkg = excludes.map((excluded) => new RegExp(`/pkg.${ excluded }
|
|
465
|
+
const pathExcludedPkg = excludes.map((excluded) => new RegExp(`/pkg.${ excluded }/`));
|
|
466
466
|
const pathsCombined = [...paths, ...pathExcludedPkg];
|
|
467
467
|
const regexCombined = new RegExp(pathsCombined.map(({ source }) => source).join('|'));
|
|
468
468
|
|
|
@@ -516,6 +516,7 @@ module.exports = function(dir, _appConfig) {
|
|
|
516
516
|
@import "~shell/assets/styles/base/_variables.scss";
|
|
517
517
|
@import "~shell/assets/styles/base/_functions.scss";
|
|
518
518
|
@import "~shell/assets/styles/base/_mixins.scss";
|
|
519
|
+
@import 'node_modules/xterm/css/xterm.css';
|
|
519
520
|
`
|
|
520
521
|
}
|
|
521
522
|
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { vspherePoolConfigMerge } from '@shell/machine-config/vmwarevsphere-pool-config-merge';
|
|
2
|
-
|
|
3
|
-
describe('vspherePoolConfigMerge', () => {
|
|
4
|
-
const testCases: Array<[object?, object?, object?]> = [
|
|
5
|
-
// Some array test cases, an array from the first object should be replaced with the array from the second object
|
|
6
|
-
[{ a: ['one'] }, { a: [] }, { a: [] }],
|
|
7
|
-
[{ a: ['one', 'two'] }, { a: ['one', 'two', 'three'] }, { a: ['one', 'two', 'three'] }],
|
|
8
|
-
[{ a: ['one', 'two'], b: ['three', 'four'] }, { a: ['one'], b: [] }, { a: ['one'], b: [] }],
|
|
9
|
-
[{
|
|
10
|
-
a: ['one', 'two'], b: ['three', 'four'], c: 'five'
|
|
11
|
-
}, { a: ['one'], b: [] }, {
|
|
12
|
-
a: ['one'], b: [], c: 'five'
|
|
13
|
-
}],
|
|
14
|
-
// Some other test cases
|
|
15
|
-
[{ a: 'one' }, { b: 'two' }, { a: 'one', b: 'two' }],
|
|
16
|
-
[{ a: 'one' }, { a: '', b: 'two' }, { a: '', b: 'two' }],
|
|
17
|
-
[{ a: 'one', b: 'two' }, { a: 1, c: { d: null } }, {
|
|
18
|
-
a: 1, b: 'two', c: { d: null }
|
|
19
|
-
}],
|
|
20
|
-
[undefined, undefined, {}],
|
|
21
|
-
[{}, undefined, {}],
|
|
22
|
-
[undefined, {}, {}],
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
it.each(testCases)('should merge properly', (obj1, obj2, expected) => {
|
|
26
|
-
const result = vspherePoolConfigMerge(obj1, obj2);
|
|
27
|
-
|
|
28
|
-
expect(result).toStrictEqual(expected);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import mergeWith from 'lodash/mergeWith';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Helper function to alter Lodash merge function default behaviour on merging arrays while updating machine pool configuration.
|
|
5
|
-
*
|
|
6
|
-
* In rke2.vue, the syncMachineConfigWithLatest function updates machine pool configuration by
|
|
7
|
-
* merging the latest configuration received from the backend with the current configuration updated by the user.
|
|
8
|
-
* However, Lodash's merge function treats arrays like object so index values are merged and not appended to arrays
|
|
9
|
-
* resulting in undesired outcomes for us, Example:
|
|
10
|
-
*
|
|
11
|
-
* const lastSavedConfigFromBE = { a: ["test"] };
|
|
12
|
-
* const currentConfigByUser = { a: [] };
|
|
13
|
-
* merge(lastSavedConfigFromBE, currentConfigByUser); // returns { a: ["test"] }; but we expect { a: [] };
|
|
14
|
-
*
|
|
15
|
-
* More info: https://github.com/lodash/lodash/issues/1313
|
|
16
|
-
*
|
|
17
|
-
* This helper function addresses the issue by always replacing the old array with the new array during the merge process.
|
|
18
|
-
*/
|
|
19
|
-
export function vspherePoolConfigMerge(obj1 = {}, obj2 = {}): Object {
|
|
20
|
-
return mergeWith(obj1, obj2, (obj1Value, obj2Value) => {
|
|
21
|
-
if (Array.isArray(obj1Value) && Array.isArray(obj2Value)) {
|
|
22
|
-
return obj2Value;
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|