@rancher/shell 0.3.23 → 0.3.25
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/base/_variables.scss +1 -0
- package/assets/styles/themes/_dark.scss +1 -0
- package/assets/styles/themes/_light.scss +6 -5
- package/assets/translations/en-us.yaml +44 -17
- package/assets/translations/zh-hans.yaml +2 -2
- package/components/ClusterIconMenu.vue +143 -0
- package/components/CruResource.vue +7 -1
- package/components/ExplorerProjectsNamespaces.vue +11 -1
- package/components/FixedBanner.vue +17 -1
- package/components/Loading.vue +1 -1
- package/components/Markdown.vue +1 -1
- package/components/Questions/__tests__/Yaml.test.ts +3 -2
- package/components/SideNav.vue +1 -1
- package/components/SortableTable/index.vue +3 -2
- package/components/auth/RoleDetailEdit.vue +15 -2
- package/components/auth/login/saml.vue +12 -1
- package/components/form/LabeledSelect.vue +12 -5
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/Members/MembershipEditor.vue +6 -1
- package/components/form/SelectOrCreateAuthSecret.vue +7 -0
- package/components/form/__tests__/KeyValue.test.ts +6 -3
- package/components/form/__tests__/LabeledSelect.test.ts +18 -0
- package/components/formatter/PodsUsage.vue +11 -36
- package/components/formatter/PrincipalGroupBindings.vue +8 -5
- package/components/formatter/__tests__/PodsUsage.test.ts +36 -19
- package/components/nav/Group.vue +62 -34
- package/components/nav/Header.vue +13 -6
- package/components/nav/Pinned.vue +47 -0
- package/components/nav/TopLevelMenu.vue +673 -325
- package/components/nav/Type.vue +88 -8
- package/config/home-links.js +1 -1
- package/config/product/istio.js +15 -5
- package/config/router.js +3 -9
- package/config/table-headers.js +5 -6
- package/config/uiplugins.js +1 -0
- package/core/plugin-helpers.js +3 -0
- package/core/types.ts +6 -1
- package/creators/app/files/.vscode/settings.json +0 -1
- package/creators/pkg/init +2 -2
- package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +118 -0
- package/detail/autoscaling.horizontalpodautoscaler/index.vue +4 -4
- package/detail/provisioning.cattle.io.cluster.vue +7 -5
- package/edit/__tests__/management.cattle.io.clusterroletemplatebinding.test.ts +58 -0
- package/edit/__tests__/namespace.test.ts +5 -3
- package/edit/fleet.cattle.io.gitrepo.vue +43 -15
- package/edit/logging.banzaicloud.io.output/index.vue +7 -0
- package/edit/management.cattle.io.clusterroletemplatebinding.vue +3 -11
- package/edit/namespace.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/Basics.vue +662 -0
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +9 -8
- package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +13 -8
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -2
- package/edit/provisioning.cattle.io.cluster/MemberRoles.vue +40 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +237 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.tests.ts +71 -23
- package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +52 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -142
- package/edit/provisioning.cattle.io.cluster/rke2.vue +253 -582
- package/edit/workload/storage/ContainerMountPaths.vue +7 -5
- package/edit/workload/storage/__tests__/Storage.test.ts +2 -2
- package/edit/workload/storage/persistentVolumeClaim/__tests__/persistentvolumeclaim.test.ts +36 -0
- package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +15 -7
- package/initialize/App.js +2 -0
- package/initialize/client.js +63 -51
- package/initialize/index.js +7 -5
- package/layouts/default.vue +10 -2
- package/layouts/home.vue +6 -2
- package/layouts/plain.vue +9 -2
- package/list/fleet.cattle.io.cluster.vue +2 -2
- package/list/management.cattle.io.feature.vue +1 -1
- package/machine-config/amazonec2.vue +1 -0
- package/machine-config/vmwarevsphere.vue +48 -7
- package/mixins/brand.js +0 -8
- package/mixins/child-hook.js +2 -2
- package/mixins/create-edit-view/impl.js +3 -3
- package/mixins/fetch.client.js +3 -3
- package/models/__tests__/management.cattle.io.node.ts +96 -0
- package/models/__tests__/node.ts +74 -0
- package/models/cluster/node.js +6 -5
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -2
- package/models/management.cattle.io.cluster.js +22 -1
- package/models/management.cattle.io.clusterroletemplatebinding.js +3 -3
- package/models/management.cattle.io.globalrole.js +17 -2
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.projectroletemplatebinding.js +3 -3
- package/models/management.cattle.io.roletemplate.js +17 -2
- package/package.json +2 -6
- package/pages/__tests__/prefs.test.ts +1 -1
- package/pages/about.vue +2 -0
- package/pages/auth/setup.vue +5 -4
- package/pages/c/_cluster/explorer/ConfigBadge.vue +1 -0
- package/pages/c/_cluster/monitoring/index.vue +8 -3
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +9 -66
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +182 -0
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +15 -32
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +8 -46
- package/pages/c/_cluster/uiplugins/index.vue +64 -64
- package/pages/diagnostic.vue +0 -39
- package/pages/home.vue +1 -1
- package/pages/prefs.vue +3 -13
- package/plugins/dashboard-store/normalize.js +4 -4
- package/plugins/dashboard-store/resource-class.js +1 -1
- package/plugins/int-number.js +5 -2
- package/plugins/positive-int-number.js +19 -0
- package/plugins/steve/__tests__/getters.spec.ts +15 -0
- package/plugins/steve/getters.js +22 -10
- package/public/index.html +4 -2
- package/rancher-components/BadgeState/BadgeState.vue +5 -1
- package/rancher-components/Banner/Banner.test.ts +51 -1
- package/rancher-components/Banner/Banner.vue +134 -53
- package/rancher-components/Card/Card.test.ts +37 -0
- package/rancher-components/Card/Card.vue +24 -7
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +20 -29
- package/rancher-components/Form/Checkbox/Checkbox.vue +45 -20
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +2 -8
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +22 -10
- package/rancher-components/Form/Radio/RadioButton.test.ts +31 -0
- package/rancher-components/Form/Radio/RadioButton.vue +30 -13
- package/rancher-components/Form/Radio/RadioGroup.vue +26 -7
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +7 -6
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +25 -38
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +23 -11
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +19 -5
- package/rancher-components/StringList/StringList.test.ts +453 -49
- package/rancher-components/StringList/StringList.vue +92 -58
- package/scripts/extension/parse-tag-name +0 -0
- package/store/index.js +4 -0
- package/store/prefs.js +4 -4
- package/store/type-map.js +2 -16
- package/types/shell/index.d.ts +26 -14
- package/utils/__tests__/cluster.test.ts +55 -0
- package/utils/__tests__/object.test.ts +21 -2
- package/utils/__tests__/sort.test.ts +61 -0
- package/utils/cluster.js +47 -1
- package/utils/object.js +12 -5
- package/utils/string.js +12 -0
- package/utils/validators/formRules/__tests__/index.test.ts +13 -1
- package/utils/validators/formRules/index.ts +4 -0
- package/utils/validators/role-template.js +9 -1
- package/utils/version.js +1 -1
- package/vue.config.js +1 -4
- package/yarn-error.log +200 -0
- package/content/docs/en-us/getting-started.md +0 -224
- package/content/docs/en-us/whats-new.md +0 -29
- package/content/docs/zh-hans/getting-started.md +0 -224
- package/content/docs/zh-hans/whats-new.md +0 -28
- package/pages/docs/_doc.vue +0 -345
- package/pages/docs/toc.js +0 -27
- package/plugins/console.js +0 -34
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import
|
|
3
|
-
import Mount from '@shell/edit/workload/storage/Mount';
|
|
2
|
+
import { clone } from '@shell/utils/object';
|
|
4
3
|
import { _VIEW } from '@shell/config/query-params';
|
|
5
|
-
import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
|
|
6
4
|
import { randomStr } from '@shell/utils/string';
|
|
7
5
|
|
|
6
|
+
import Mount from '@shell/edit/workload/storage/Mount';
|
|
7
|
+
import ButtonDropdown from '@shell/components/ButtonDropdown';
|
|
8
|
+
import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
|
|
9
|
+
|
|
8
10
|
export default {
|
|
9
11
|
name: 'ContainerMountPaths',
|
|
10
12
|
components: {
|
|
@@ -109,7 +111,7 @@ export default {
|
|
|
109
111
|
const names = volumeMounts.map(({ name }) => name);
|
|
110
112
|
|
|
111
113
|
// Extract storage volumes to allow mutation, if matches mount map
|
|
112
|
-
return this.value.volumes.filter((volume) => names.includes(volume.name));
|
|
114
|
+
return clone(this.value.volumes.filter((volume) => names.includes(volume.name)));
|
|
113
115
|
},
|
|
114
116
|
|
|
115
117
|
getSelectedContainerVolumes() {
|
|
@@ -118,7 +120,7 @@ export default {
|
|
|
118
120
|
const names = volumeMounts.map(({ name }) => name);
|
|
119
121
|
|
|
120
122
|
// Extract storage volumes to allow mutation, if matches mount map
|
|
121
|
-
return this.value.volumes.filter((volume) => names.includes(volume.name));
|
|
123
|
+
return clone(this.value.volumes.filter((volume) => names.includes(volume.name)));
|
|
122
124
|
},
|
|
123
125
|
|
|
124
126
|
/**
|
|
@@ -33,7 +33,7 @@ describe('component: Storage', () => {
|
|
|
33
33
|
t: (text: string) => text, // Mock i18n global function used as alternative to the getter
|
|
34
34
|
$store: {
|
|
35
35
|
getters: {
|
|
36
|
-
'i18n/t': jest.fn(),
|
|
36
|
+
'i18n/t': jest.fn().mockImplementation((key: string) => key),
|
|
37
37
|
'i18n/exists': jest.fn()
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -63,7 +63,7 @@ describe('component: Storage', () => {
|
|
|
63
63
|
t: (text: string) => text, // Mock i18n global function used as alternative to the getter
|
|
64
64
|
$store: {
|
|
65
65
|
getters: {
|
|
66
|
-
'i18n/t': jest.fn(),
|
|
66
|
+
'i18n/t': jest.fn().mockImplementation((key: string) => key),
|
|
67
67
|
'i18n/exists': jest.fn()
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createLocalVue, mount } from '@vue/test-utils';
|
|
2
|
+
import PVC from '@shell/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: PVC', () => {
|
|
5
|
+
// TODO: Enable test after allowing to test async data with either #9711 or #9322
|
|
6
|
+
// eslint-disable-next-line jest/no-disabled-tests
|
|
7
|
+
it.skip('should initialize storage class on create mode', async() => {
|
|
8
|
+
const localVue = createLocalVue();
|
|
9
|
+
const name = 'test';
|
|
10
|
+
const wrapper = mount(PVC, {
|
|
11
|
+
localVue,
|
|
12
|
+
propsData: {
|
|
13
|
+
savePvcHookName: '',
|
|
14
|
+
value: { spec: { resources: { requests: {} } } }
|
|
15
|
+
},
|
|
16
|
+
mocks: {
|
|
17
|
+
$store: {
|
|
18
|
+
getters: {
|
|
19
|
+
'cluster/findAll': [{
|
|
20
|
+
metadata: {
|
|
21
|
+
name,
|
|
22
|
+
annotations: { 'storageclass.beta.kubernetes.io/is-default-class': true }
|
|
23
|
+
}
|
|
24
|
+
}],
|
|
25
|
+
'i18n/t': jest.fn()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
stubs: { LabeledSelect: { template: '<input />' } }
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const inputElement = wrapper.find('[data-testid="storage-class-name"]').element as HTMLInputElement;
|
|
33
|
+
|
|
34
|
+
expect(inputElement.value).toBe(name);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -48,6 +48,7 @@ export default {
|
|
|
48
48
|
|
|
49
49
|
this.storageClasses = hash.storageClasses;
|
|
50
50
|
this.persistentVolumes = hash.persistentVolumes;
|
|
51
|
+
this.$set(this.spec, 'storageClassName', (this.spec.storageClassName || this.defaultStorageClassName));
|
|
51
52
|
},
|
|
52
53
|
|
|
53
54
|
data() {
|
|
@@ -62,7 +63,7 @@ export default {
|
|
|
62
63
|
return {
|
|
63
64
|
storageClasses: [],
|
|
64
65
|
persistentVolumes: [],
|
|
65
|
-
|
|
66
|
+
isCreatePV: true,
|
|
66
67
|
spec,
|
|
67
68
|
uniqueId: new Date().getTime() // Allows form state to be individually deleted
|
|
68
69
|
};
|
|
@@ -73,6 +74,13 @@ export default {
|
|
|
73
74
|
return this.storageClasses.map((sc) => sc.metadata.name);
|
|
74
75
|
},
|
|
75
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Required to initialize with default SC on creation
|
|
79
|
+
*/
|
|
80
|
+
defaultStorageClassName() {
|
|
81
|
+
return this.storageClasses.find((sc) => sc.metadata?.annotations?.['storageclass.beta.kubernetes.io/is-default-class'] || sc.metadata?.annotations?.['storageclass.kubernetes.io/is-default-class'])?.metadata.name;
|
|
82
|
+
},
|
|
83
|
+
|
|
76
84
|
availablePVs() {
|
|
77
85
|
return this.persistentVolumes.reduce((total, each) => {
|
|
78
86
|
if (each?.status?.phase === 'Available') {
|
|
@@ -91,12 +99,11 @@ export default {
|
|
|
91
99
|
},
|
|
92
100
|
|
|
93
101
|
watch: {
|
|
94
|
-
|
|
102
|
+
isCreatePV(neu) {
|
|
95
103
|
if (neu) {
|
|
96
104
|
delete this.spec.volumeName;
|
|
97
105
|
this.spec.resources.requests.storage = null;
|
|
98
106
|
} else {
|
|
99
|
-
this.spec.storageClassName = '';
|
|
100
107
|
this.spec.resources.requests.storage = null;
|
|
101
108
|
}
|
|
102
109
|
},
|
|
@@ -161,8 +168,8 @@ export default {
|
|
|
161
168
|
<div class="row mb-10">
|
|
162
169
|
<div class="col span-6">
|
|
163
170
|
<RadioGroup
|
|
164
|
-
v-model="
|
|
165
|
-
name="
|
|
171
|
+
v-model="isCreatePV"
|
|
172
|
+
name="isCreatePV"
|
|
166
173
|
:options="[true, false]"
|
|
167
174
|
:labels="[t('persistentVolumeClaim.source.options.new'), t('persistentVolumeClaim.source.options.existing')]"
|
|
168
175
|
:mode="mode"
|
|
@@ -170,8 +177,9 @@ export default {
|
|
|
170
177
|
</div>
|
|
171
178
|
<div class="col span-6">
|
|
172
179
|
<LabeledSelect
|
|
173
|
-
v-if="
|
|
180
|
+
v-if="isCreatePV"
|
|
174
181
|
v-model="spec.storageClassName"
|
|
182
|
+
data-testid="storage-class-name"
|
|
175
183
|
:mode="mode"
|
|
176
184
|
:required="true"
|
|
177
185
|
:label="t('persistentVolumeClaim.storageClass')"
|
|
@@ -221,7 +229,7 @@ export default {
|
|
|
221
229
|
</div>
|
|
222
230
|
</div>
|
|
223
231
|
<div
|
|
224
|
-
v-if="
|
|
232
|
+
v-if="isCreatePV"
|
|
225
233
|
class="col span-6"
|
|
226
234
|
>
|
|
227
235
|
<UnitInput
|
package/initialize/App.js
CHANGED
package/initialize/client.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Taken from @nuxt/vue-app/template/client.js
|
|
2
|
+
|
|
1
3
|
import Vue from 'vue';
|
|
2
4
|
import fetch from 'unfetch';
|
|
3
5
|
import middleware from '../config/middleware.js';
|
|
@@ -21,6 +23,10 @@ import { createApp, NuxtError } from './index.js';
|
|
|
21
23
|
import fetchMixin from '../mixins/fetch.client';
|
|
22
24
|
import NuxtLink from '../components/nuxt/nuxt-link.client.js'; // should be included after ./index.js
|
|
23
25
|
|
|
26
|
+
// Mimic old @nuxt/vue-app/template/client.js
|
|
27
|
+
const isDev = process.env.dev;
|
|
28
|
+
const debug = isDev;
|
|
29
|
+
|
|
24
30
|
// Fetch mixin
|
|
25
31
|
if (!Vue.__nuxt__fetch__mixin__) {
|
|
26
32
|
Vue.mixin(fetchMixin);
|
|
@@ -51,69 +57,71 @@ if ($config._app) {
|
|
|
51
57
|
|
|
52
58
|
Object.assign(Vue.config, { silent: false, performance: true });
|
|
53
59
|
|
|
54
|
-
|
|
60
|
+
if (debug) {
|
|
61
|
+
const logs = NUXT.logs || [];
|
|
55
62
|
|
|
56
|
-
if (logs.length > 0) {
|
|
57
|
-
|
|
63
|
+
if (logs.length > 0) {
|
|
64
|
+
const ssrLogStyle = 'background: #2E495E;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;';
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
66
|
+
console.group && console.group('%cNuxt SSR', ssrLogStyle); // eslint-disable-line no-console
|
|
67
|
+
logs.forEach((logObj) => (console[logObj.type] || console.log)(...logObj.args)); // eslint-disable-line no-console
|
|
68
|
+
delete NUXT.logs;
|
|
69
|
+
console.groupEnd && console.groupEnd(); // eslint-disable-line no-console
|
|
70
|
+
}
|
|
64
71
|
|
|
65
|
-
// Setup global Vue error handler
|
|
66
|
-
if (!Vue.config.$nuxt) {
|
|
67
|
-
|
|
72
|
+
// Setup global Vue error handler
|
|
73
|
+
if (!Vue.config.$nuxt) {
|
|
74
|
+
const defaultErrorHandler = Vue.config.errorHandler;
|
|
68
75
|
|
|
69
|
-
|
|
76
|
+
Vue.config.errorHandler = async(err, vm, info, ...rest) => {
|
|
70
77
|
// Call other handler if exist
|
|
71
|
-
|
|
78
|
+
let handled = null;
|
|
72
79
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
if (typeof defaultErrorHandler === 'function') {
|
|
81
|
+
handled = defaultErrorHandler(err, vm, info, ...rest);
|
|
82
|
+
}
|
|
83
|
+
if (handled === true) {
|
|
84
|
+
return handled;
|
|
85
|
+
}
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
if (vm && vm.$root) {
|
|
88
|
+
const nuxtApp = Object.keys(Vue.config.$nuxt)
|
|
89
|
+
.find((nuxtInstance) => vm.$root[nuxtInstance]);
|
|
83
90
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
// Show Nuxt Error Page
|
|
92
|
+
if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
|
|
93
|
+
const currentApp = vm.$root[nuxtApp];
|
|
87
94
|
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
// Load error layout
|
|
96
|
+
let layout = (NuxtError.options || NuxtError).layout;
|
|
90
97
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
if (typeof layout === 'function') {
|
|
99
|
+
layout = layout(currentApp.context);
|
|
100
|
+
}
|
|
101
|
+
if (layout) {
|
|
102
|
+
await currentApp.loadLayout(layout).catch(() => {});
|
|
103
|
+
}
|
|
104
|
+
currentApp.setLayout(layout);
|
|
98
105
|
|
|
99
|
-
|
|
106
|
+
currentApp.error(err);
|
|
107
|
+
}
|
|
100
108
|
}
|
|
101
|
-
}
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
if (typeof defaultErrorHandler === 'function') {
|
|
111
|
+
return handled;
|
|
112
|
+
}
|
|
106
113
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
// Log to console
|
|
115
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
116
|
+
console.error(err); // eslint-disable-line no-console
|
|
117
|
+
} else {
|
|
118
|
+
console.error(err.message || err); // eslint-disable-line no-console
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
Vue.config.$nuxt = {};
|
|
122
|
+
}
|
|
123
|
+
Vue.config.$nuxt.$nuxt = true;
|
|
115
124
|
}
|
|
116
|
-
Vue.config.$nuxt.$nuxt = true;
|
|
117
125
|
|
|
118
126
|
const errorHandler = Vue.config.errorHandler || console.error; // eslint-disable-line no-console
|
|
119
127
|
|
|
@@ -623,7 +631,9 @@ function fixPrepatch(to, ___) {
|
|
|
623
631
|
checkForErrors(this);
|
|
624
632
|
|
|
625
633
|
// Hot reloading
|
|
626
|
-
|
|
634
|
+
if (isDev) {
|
|
635
|
+
setTimeout(() => hotReloadAPI(this), 100);
|
|
636
|
+
}
|
|
627
637
|
});
|
|
628
638
|
}
|
|
629
639
|
|
|
@@ -794,8 +804,10 @@ async function mountApp(__app) {
|
|
|
794
804
|
// Call window.{{globals.readyCallback}} callbacks
|
|
795
805
|
nuxtReady(_app);
|
|
796
806
|
|
|
797
|
-
|
|
798
|
-
|
|
807
|
+
if (isDev) {
|
|
808
|
+
// Enable hot reloading
|
|
809
|
+
hotReloadAPI(_app);
|
|
810
|
+
}
|
|
799
811
|
});
|
|
800
812
|
};
|
|
801
813
|
|
package/initialize/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Taken from @nuxt/vue-app/template/index.js
|
|
2
|
+
|
|
1
3
|
import Vue from 'vue';
|
|
2
4
|
import Meta from 'vue-meta';
|
|
3
5
|
import ClientOnly from 'vue-client-only';
|
|
@@ -34,8 +36,8 @@ import '../plugins/global-formatters';
|
|
|
34
36
|
import '../plugins/trim-whitespace';
|
|
35
37
|
import '../plugins/extend-router';
|
|
36
38
|
|
|
37
|
-
import consolePlugin from '../plugins/console';
|
|
38
39
|
import intNumber from '../plugins/int-number';
|
|
40
|
+
import positiveIntNumber from '../plugins/positive-int-number.js';
|
|
39
41
|
import nuxtClientInit from '../plugins/nuxt-client-init';
|
|
40
42
|
import replaceAll from '../plugins/replaceall';
|
|
41
43
|
import backButton from '../plugins/back-button';
|
|
@@ -272,14 +274,14 @@ async function createApp(ssrContext, config = {}) {
|
|
|
272
274
|
await axiosShell(app.context, inject);
|
|
273
275
|
}
|
|
274
276
|
|
|
275
|
-
if (process.client && typeof consolePlugin === 'function') {
|
|
276
|
-
await consolePlugin(app.context, inject);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
277
|
if (process.client && typeof intNumber === 'function') {
|
|
280
278
|
await intNumber(app.context, inject);
|
|
281
279
|
}
|
|
282
280
|
|
|
281
|
+
if (process.client && typeof positiveIntNumber === 'function') {
|
|
282
|
+
await positiveIntNumber(app.context, inject);
|
|
283
|
+
}
|
|
284
|
+
|
|
283
285
|
if (process.client && typeof nuxtClientInit === 'function') {
|
|
284
286
|
await nuxtClientInit(app.context, inject);
|
|
285
287
|
}
|
package/layouts/default.vue
CHANGED
|
@@ -70,7 +70,7 @@ export default {
|
|
|
70
70
|
|
|
71
71
|
computed: {
|
|
72
72
|
...mapState(['managementReady', 'clusterReady']),
|
|
73
|
-
...mapGetters(['clusterId', 'currentProduct', 'isRancherInHarvester']),
|
|
73
|
+
...mapGetters(['clusterId', 'currentProduct', 'isRancherInHarvester', 'showTopLevelMenu']),
|
|
74
74
|
|
|
75
75
|
afterLoginRoute: mapPref(AFTER_LOGIN_ROUTE),
|
|
76
76
|
|
|
@@ -237,7 +237,7 @@ export default {
|
|
|
237
237
|
<div
|
|
238
238
|
v-if="managementReady"
|
|
239
239
|
class="dashboard-content"
|
|
240
|
-
:class="{[pinClass]: true}"
|
|
240
|
+
:class="{[pinClass]: true, 'dashboard-padding-left': showTopLevelMenu}"
|
|
241
241
|
>
|
|
242
242
|
<Header />
|
|
243
243
|
<SideNav
|
|
@@ -318,6 +318,14 @@ export default {
|
|
|
318
318
|
overflow-y: auto;
|
|
319
319
|
min-height: 0px;
|
|
320
320
|
|
|
321
|
+
&.dashboard-padding-left {
|
|
322
|
+
padding-left: $app-bar-collapsed-width;
|
|
323
|
+
|
|
324
|
+
.overlay-content-mode {
|
|
325
|
+
left: calc(var(--nav-width) + $app-bar-collapsed-width);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
321
329
|
&.pin-right {
|
|
322
330
|
grid-template-areas:
|
|
323
331
|
"header header header"
|
package/layouts/home.vue
CHANGED
|
@@ -8,7 +8,7 @@ import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
|
|
|
8
8
|
import AzureWarning from '@shell/components/auth/AzureWarning';
|
|
9
9
|
import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
10
10
|
import Inactivity from '@shell/components/Inactivity';
|
|
11
|
-
import { mapState } from 'vuex';
|
|
11
|
+
import { mapState, mapGetters } from 'vuex';
|
|
12
12
|
|
|
13
13
|
export default {
|
|
14
14
|
|
|
@@ -36,6 +36,7 @@ export default {
|
|
|
36
36
|
computed: {
|
|
37
37
|
themeShortcut: mapPref(THEME_SHORTCUT),
|
|
38
38
|
...mapState(['managementReady']),
|
|
39
|
+
...mapGetters(['showTopLevelMenu']),
|
|
39
40
|
},
|
|
40
41
|
|
|
41
42
|
methods: {
|
|
@@ -57,7 +58,10 @@ export default {
|
|
|
57
58
|
<AwsComplianceBanner />
|
|
58
59
|
<AzureWarning />
|
|
59
60
|
|
|
60
|
-
<div
|
|
61
|
+
<div
|
|
62
|
+
class="dashboard-content"
|
|
63
|
+
:class="{'dashboard-padding-left': showTopLevelMenu}"
|
|
64
|
+
>
|
|
61
65
|
<Header
|
|
62
66
|
v-if="managementReady"
|
|
63
67
|
:simple="true"
|
package/layouts/plain.vue
CHANGED
|
@@ -12,6 +12,7 @@ import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
|
|
|
12
12
|
import AzureWarning from '@shell/components/auth/AzureWarning';
|
|
13
13
|
import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
14
14
|
import Inactivity from '@shell/components/Inactivity';
|
|
15
|
+
import { mapGetters } from 'vuex';
|
|
15
16
|
|
|
16
17
|
export default {
|
|
17
18
|
|
|
@@ -40,7 +41,10 @@ export default {
|
|
|
40
41
|
};
|
|
41
42
|
},
|
|
42
43
|
|
|
43
|
-
computed: {
|
|
44
|
+
computed: {
|
|
45
|
+
themeShortcut: mapPref(THEME_SHORTCUT),
|
|
46
|
+
...mapGetters(['showTopLevelMenu']),
|
|
47
|
+
},
|
|
44
48
|
|
|
45
49
|
methods: {
|
|
46
50
|
toggleTheme() {
|
|
@@ -59,7 +63,10 @@ export default {
|
|
|
59
63
|
<AwsComplianceBanner />
|
|
60
64
|
<AzureWarning />
|
|
61
65
|
|
|
62
|
-
<div
|
|
66
|
+
<div
|
|
67
|
+
class="dashboard-content"
|
|
68
|
+
:class="{'dashboard-padding-left': showTopLevelMenu}"
|
|
69
|
+
>
|
|
63
70
|
<Header :simple="true" />
|
|
64
71
|
<main class="main-layout">
|
|
65
72
|
<IndentedPanel class="pt-20">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import FleetClusters from '@shell/components/fleet/FleetClusters';
|
|
3
3
|
import { FLEET, MANAGEMENT } from '@shell/config/types';
|
|
4
|
-
import {
|
|
4
|
+
import { filterOnlyKubernetesClusters } from '@shell/utils/cluster';
|
|
5
5
|
import { Banner } from '@components/Banner';
|
|
6
6
|
import ResourceFetch from '@shell/mixins/resource-fetch';
|
|
7
7
|
|
|
@@ -56,7 +56,7 @@ export default {
|
|
|
56
56
|
},
|
|
57
57
|
|
|
58
58
|
filteredRows() {
|
|
59
|
-
return this.fleetClusters
|
|
59
|
+
return filterOnlyKubernetesClusters(this.fleetClusters, this.$store);
|
|
60
60
|
},
|
|
61
61
|
|
|
62
62
|
fleetClusters() {
|
|
@@ -82,7 +82,7 @@ export default {
|
|
|
82
82
|
},
|
|
83
83
|
|
|
84
84
|
enableRowActions() {
|
|
85
|
-
const schema = this.$store.getters[`management/schemaFor`](MANAGEMENT.
|
|
85
|
+
const schema = this.$store.getters[`management/schemaFor`](MANAGEMENT.FEATURE);
|
|
86
86
|
|
|
87
87
|
return schema?.resourceMethods?.includes('PUT');
|
|
88
88
|
},
|
|
@@ -14,7 +14,7 @@ import ArrayListSelect from '@shell/components/form/ArrayListSelect';
|
|
|
14
14
|
import YamlEditor from '@shell/components/YamlEditor';
|
|
15
15
|
import { get, set } from '@shell/utils/object';
|
|
16
16
|
import { integerString, keyValueStrings } from '@shell/utils/computed';
|
|
17
|
-
import { _CREATE } from '@shell/config/query-params';
|
|
17
|
+
import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
|
|
18
18
|
|
|
19
19
|
const SENTINEL = '__SENTINEL__';
|
|
20
20
|
const VAPP_MODE = {
|
|
@@ -136,6 +136,11 @@ function createOptionHelpers(name) {
|
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
const errorActions = Object.freeze({
|
|
140
|
+
CREATE: 'create',
|
|
141
|
+
DELETE: 'delete',
|
|
142
|
+
});
|
|
143
|
+
|
|
139
144
|
export default {
|
|
140
145
|
components: {
|
|
141
146
|
ArrayListSelect, Card, KeyValue, Loading, LabeledInput, LabeledSelect, Banner, UnitInput, RadioGroup, YamlEditor
|
|
@@ -144,6 +149,10 @@ export default {
|
|
|
144
149
|
mixins: [CreateEditView],
|
|
145
150
|
|
|
146
151
|
props: {
|
|
152
|
+
poolId: {
|
|
153
|
+
type: String,
|
|
154
|
+
default: '',
|
|
155
|
+
},
|
|
147
156
|
credentialId: {
|
|
148
157
|
type: String,
|
|
149
158
|
required: true,
|
|
@@ -246,6 +255,7 @@ export default {
|
|
|
246
255
|
vAppOptions,
|
|
247
256
|
vappMode: getInitialVappMode(this.value),
|
|
248
257
|
osOptions: OS_OPTIONS,
|
|
258
|
+
validationErrors: {},
|
|
249
259
|
};
|
|
250
260
|
},
|
|
251
261
|
|
|
@@ -347,6 +357,9 @@ export default {
|
|
|
347
357
|
}
|
|
348
358
|
|
|
349
359
|
this.updateVappOptions(INITIAL_VAPP_OPTIONS);
|
|
360
|
+
},
|
|
361
|
+
validationErrors(value) {
|
|
362
|
+
this.$emit('error', value);
|
|
350
363
|
}
|
|
351
364
|
},
|
|
352
365
|
|
|
@@ -384,9 +397,17 @@ export default {
|
|
|
384
397
|
const valueInContent = content.find((c) => c.value === this.value.datacenter );
|
|
385
398
|
|
|
386
399
|
if (!valueInContent) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
400
|
+
if (this.mode === _CREATE) {
|
|
401
|
+
set(this.value, 'datacenter', options[0]);
|
|
402
|
+
set(this.value, 'cloneFrom', undefined);
|
|
403
|
+
set(this.value, 'useDataStoreCluster', false);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if ([_EDIT, _VIEW].includes(this.mode)) {
|
|
407
|
+
this.manageErrors(errorActions.CREATE, 'datacenter');
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
this.manageErrors(errorActions.DELETE, 'datacenter');
|
|
390
411
|
}
|
|
391
412
|
|
|
392
413
|
set(this, 'dataCentersResults', content);
|
|
@@ -582,11 +603,19 @@ export default {
|
|
|
582
603
|
};
|
|
583
604
|
|
|
584
605
|
if (!isValueInContent()) {
|
|
585
|
-
|
|
606
|
+
if (this.mode === _CREATE) {
|
|
607
|
+
const value = isArray ? [] : content[0]?.value;
|
|
608
|
+
|
|
609
|
+
if (value !== SENTINEL) {
|
|
610
|
+
set(this.value, key, value);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
586
613
|
|
|
587
|
-
if (
|
|
588
|
-
|
|
614
|
+
if ([_EDIT, _VIEW].includes(this.mode)) {
|
|
615
|
+
this.manageErrors(errorActions.CREATE, key);
|
|
589
616
|
}
|
|
617
|
+
} else {
|
|
618
|
+
this.manageErrors(errorActions.DELETE, key);
|
|
590
619
|
}
|
|
591
620
|
},
|
|
592
621
|
|
|
@@ -654,6 +683,18 @@ export default {
|
|
|
654
683
|
set(this.value, 'vappProperty', opts.vappProperty);
|
|
655
684
|
this.initKeyValueParams('value.vappProperty', 'initVappArray');
|
|
656
685
|
},
|
|
686
|
+
|
|
687
|
+
manageErrors(action = errorActions.CREATE, key) {
|
|
688
|
+
if (action === errorActions.CREATE) {
|
|
689
|
+
const keys = [key, ...(this.validationErrors[this.poolId] || [])];
|
|
690
|
+
|
|
691
|
+
this.validationErrors = Object.assign({}, this.validationErrors, { [this.poolId]: keys });
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (action === errorActions.DELETE && this.validationErrors[this.poolId]) {
|
|
695
|
+
this.validationErrors = Object.assign({}, this.validationErrors, { [this.poolId]: this.validationErrors[this.poolId].filter((x) => x === key) }) ;
|
|
696
|
+
}
|
|
697
|
+
},
|
|
657
698
|
}
|
|
658
699
|
};
|
|
659
700
|
</script>
|
package/mixins/brand.js
CHANGED
|
@@ -141,14 +141,6 @@ export default {
|
|
|
141
141
|
}).then((setting) => setting.save());
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
|
-
} else if (!neu) {
|
|
145
|
-
const brandSetting = findBy(this.globalSettings, 'id', SETTING.BRAND);
|
|
146
|
-
|
|
147
|
-
if (brandSetting && brandSetting.value !== '') {
|
|
148
|
-
// 2) There should not be a brand... but there is a brand setting
|
|
149
|
-
brandSetting.value = '';
|
|
150
|
-
brandSetting.save();
|
|
151
|
-
}
|
|
152
144
|
}
|
|
153
145
|
}
|
|
154
146
|
},
|
package/mixins/child-hook.js
CHANGED
|
@@ -25,14 +25,14 @@ export default {
|
|
|
25
25
|
},
|
|
26
26
|
|
|
27
27
|
async applyHooks(key, ...args) {
|
|
28
|
-
if (
|
|
28
|
+
if (!key) {
|
|
29
29
|
throw new Error('Must specify key');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const hooks = sortBy(this[key] || [], ['priority', 'name']);
|
|
33
33
|
const out = {};
|
|
34
34
|
|
|
35
|
-
for (
|
|
35
|
+
for (const x of hooks) {
|
|
36
36
|
console.debug('Applying hook', x.name); // eslint-disable-line no-console
|
|
37
37
|
out[x.name] = await x.fn.apply(x.fnContext || this, args);
|
|
38
38
|
}
|
|
@@ -114,8 +114,8 @@ export default {
|
|
|
114
114
|
// Detect and resolve conflicts from a 409 response.
|
|
115
115
|
// If they are resolved, return a false-y value
|
|
116
116
|
// Else they can't be resolved, return an array of errors to show to the user.
|
|
117
|
-
conflict() {
|
|
118
|
-
return handleConflict(this.initialValue.toJSON(), this.value, this.liveValue, this.$store.getters, this.$store);
|
|
117
|
+
async conflict() {
|
|
118
|
+
return await handleConflict(this.initialValue.toJSON(), this.value, this.liveValue, this.$store.getters, this.$store, this.storeOverride || this.$store.getters['currentStore'](this.value.type));
|
|
119
119
|
},
|
|
120
120
|
|
|
121
121
|
async save(buttonDone, url, depth = 0) {
|
|
@@ -159,7 +159,7 @@ export default {
|
|
|
159
159
|
} catch (err) {
|
|
160
160
|
// Conflict, the resource being edited has changed since starting editing
|
|
161
161
|
if ( err.status === 409 && depth === 0 && this.isEdit) {
|
|
162
|
-
const errors = this.conflict();
|
|
162
|
+
const errors = await this.conflict();
|
|
163
163
|
|
|
164
164
|
if ( errors === false ) {
|
|
165
165
|
// It was automatically figured out, save again
|