@rancher/shell 3.0.9-rc.3 → 3.0.9-rc.5
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/brand/suse/metadata.json +2 -1
- package/assets/translations/en-us.yaml +105 -5
- package/components/ActionMenuShell.vue +1 -1
- package/components/Inactivity.vue +2 -2
- package/components/Resource/Detail/Card/ExtrasCard.vue +49 -15
- package/components/Resource/Detail/Card/__tests__/ExtrasCard.test.ts +111 -0
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +0 -17
- package/components/Resource/Detail/Masthead/index.vue +11 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +3 -1
- package/components/Resource/Detail/Metadata/index.vue +1 -1
- package/components/Resource/Detail/ResourceRow.vue +1 -1
- package/components/ResourceDetail/Masthead/latest.vue +12 -2
- package/components/ResourceList/index.vue +9 -0
- package/components/ResourceTable.vue +38 -4
- package/components/Tabbed/Tab.vue +4 -0
- package/components/Tabbed/index.vue +4 -1
- package/components/__tests__/ProjectRow.test.ts +60 -0
- package/components/form/ChangePassword.vue +41 -35
- package/components/form/ResourceQuota/Project.vue +42 -1
- package/components/form/ResourceQuota/ProjectRow.vue +71 -4
- package/components/form/ResourceQuota/__tests__/Project.test.ts +63 -0
- package/components/form/SelectOrCreateAuthSecret.vue +6 -1
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +35 -0
- package/components/formatter/KubeconfigClusters.vue +74 -0
- package/components/formatter/MachineSummaryGraph.vue +10 -2
- package/components/formatter/__tests__/KubeconfigClusters.test.ts +125 -0
- package/components/nav/TopLevelMenu.helper.ts +50 -2
- package/components/nav/TopLevelMenu.vue +14 -0
- package/components/nav/Type.vue +5 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +3 -3
- package/components/nav/__tests__/Type.test.ts +6 -4
- package/config/product/explorer.js +4 -3
- package/config/product/manager.js +47 -3
- package/config/router/navigation-guards/authentication.js +8 -9
- package/config/router/routes.js +4 -1
- package/config/types.js +10 -2
- package/detail/auditlog.cattle.io.auditpolicy.vue +19 -0
- package/detail/management.cattle.io.user.vue +1 -2
- package/detail/node.vue +0 -1
- package/detail/provisioning.cattle.io.cluster.vue +2 -1
- package/dialog/ChangePasswordDialog.vue +8 -0
- package/dialog/GenericPrompt.vue +20 -3
- package/dialog/ScaleMachineDownDialog.vue +65 -15
- package/dialog/SearchDialog.vue +10 -2
- package/dialog/__tests__/ScaleMachineDownDialog.test.ts +184 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +89 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -1
- package/edit/auditlog.cattle.io.auditpolicy/AdditionalRedactions.vue +114 -0
- package/edit/auditlog.cattle.io.auditpolicy/Filters.vue +119 -0
- package/edit/auditlog.cattle.io.auditpolicy/General.vue +180 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/AdditionalRedactions.test.ts +327 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/Filters.test.ts +449 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/General.test.ts +472 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/AdditionalRedactions.test.ts.snap +27 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/Filters.test.ts.snap +39 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +174 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +29 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/index.test.ts +215 -0
- package/edit/auditlog.cattle.io.auditpolicy/index.vue +104 -0
- package/edit/auditlog.cattle.io.auditpolicy/types.ts +28 -0
- package/edit/fleet.cattle.io.gitrepo.vue +16 -1
- package/edit/management.cattle.io.project.vue +8 -2
- package/edit/management.cattle.io.user.vue +29 -34
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +178 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +22 -2
- package/edit/provisioning.cattle.io.cluster/shared.ts +4 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +57 -2
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +109 -0
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +1 -0
- package/list/auditlog.cattle.io.auditpolicy.vue +63 -0
- package/list/ext.cattle.io.kubeconfig.vue +118 -0
- package/list/group.principal.vue +11 -15
- package/list/management.cattle.io.user.vue +11 -21
- package/machine-config/azure.vue +14 -0
- package/mixins/__tests__/chart.test.ts +147 -0
- package/mixins/browser-tab-visibility.js +5 -4
- package/mixins/chart.js +10 -8
- package/mixins/fetch.client.js +6 -0
- package/models/__tests__/auditlog.cattle.io.auditpolicy.test.ts +117 -0
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +364 -0
- package/models/__tests__/secret.test.ts +55 -0
- package/models/__tests__/workload.test.ts +49 -6
- package/models/auditlog.cattle.io.auditpolicy.js +46 -0
- package/models/cluster.x-k8s.io.machine.js +1 -1
- package/models/cluster.x-k8s.io.machinedeployment.js +5 -5
- package/models/event.js +5 -0
- package/models/ext.cattle.io.groupmembershiprefreshrequest.js +15 -0
- package/models/ext.cattle.io.kubeconfig.ts +97 -0
- package/models/ext.cattle.io.passwordchangerequest.js +15 -0
- package/models/ext.cattle.io.selfuser.js +15 -0
- package/models/fleet-application.js +17 -7
- package/models/management.cattle.io.user.js +28 -31
- package/models/schema.js +18 -0
- package/models/secret.js +28 -25
- package/models/steve-schema.ts +39 -2
- package/models/workload.js +3 -2
- package/package.json +2 -2
- package/pages/about.vue +3 -2
- package/pages/account/index.vue +23 -16
- package/pages/auth/login.vue +15 -8
- package/pages/auth/setup.vue +52 -15
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +38 -14
- package/pages/c/_cluster/apps/charts/index.vue +1 -0
- package/pages/home.vue +9 -3
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -3
- package/plugins/dashboard-store/actions.js +7 -0
- package/plugins/dashboard-store/getters.js +23 -1
- package/plugins/dashboard-store/index.js +3 -2
- package/plugins/dashboard-store/mutations.js +4 -0
- package/plugins/dashboard-store/resource-class.js +12 -5
- package/plugins/steve/__tests__/steve-class.test.ts +167 -0
- package/plugins/steve/schema.d.ts +5 -0
- package/plugins/steve/steve-class.js +19 -0
- package/plugins/steve/steve-pagination-utils.ts +2 -1
- package/rancher-components/RcItemCard/RcItemCard.test.ts +4 -2
- package/rancher-components/RcItemCard/RcItemCard.vue +27 -10
- package/store/auth.js +57 -19
- package/store/notifications.ts +1 -1
- package/store/type-map.js +12 -1
- package/types/shell/index.d.ts +24 -15
- package/types/store/dashboard-store.types.ts +7 -0
- package/utils/__tests__/chart.test.ts +96 -0
- package/utils/__tests__/version.test.ts +1 -19
- package/utils/chart.js +64 -0
- package/utils/pagination-wrapper.ts +11 -3
- package/utils/version.js +5 -17
- package/vue.config.js +26 -13
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { MANAGEMENT,
|
|
2
|
+
import { MANAGEMENT, SECRET } from '@shell/config/types';
|
|
3
3
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
4
4
|
import GlobalRoleBindings from '@shell/components/GlobalRoleBindings.vue';
|
|
5
5
|
import ChangePassword from '@shell/components/form/ChangePassword';
|
|
@@ -9,6 +9,7 @@ import { exceptionToErrorsArray } from '@shell/utils/error';
|
|
|
9
9
|
import { _CREATE, _EDIT } from '@shell/config/query-params';
|
|
10
10
|
import Loading from '@shell/components/Loading';
|
|
11
11
|
import { wait } from '@shell/utils/async';
|
|
12
|
+
import { base64Encode } from '@shell/utils/crypto';
|
|
12
13
|
|
|
13
14
|
export default {
|
|
14
15
|
components: {
|
|
@@ -128,26 +129,33 @@ export default {
|
|
|
128
129
|
},
|
|
129
130
|
|
|
130
131
|
async createUser() {
|
|
131
|
-
//
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
if (users.find((u) => u.username === this.form.username)) {
|
|
135
|
-
throw new Error(this.t('user.edit.credentials.username.exists'));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const user = await this.$store.dispatch('rancher/create', {
|
|
139
|
-
type: NORMAN.USER,
|
|
132
|
+
// never use "password" as it's deprecated!
|
|
133
|
+
const user = await this.$store.dispatch('management/create', {
|
|
134
|
+
type: MANAGEMENT.USER,
|
|
140
135
|
description: this.form.description,
|
|
141
136
|
enabled: true,
|
|
142
137
|
mustChangePassword: this.form.password.userChangeOnLogin,
|
|
143
|
-
|
|
144
|
-
password: this.form.password.password,
|
|
138
|
+
displayName: this.form.displayName,
|
|
145
139
|
username: this.form.username
|
|
146
140
|
});
|
|
147
141
|
|
|
148
|
-
const
|
|
142
|
+
const userSaved = await user.save();
|
|
143
|
+
|
|
144
|
+
if (this.form.password.password) {
|
|
145
|
+
// create secret to hold user password
|
|
146
|
+
const secret = await this.$store.dispatch('management/create', {
|
|
147
|
+
type: SECRET,
|
|
148
|
+
metadata: {
|
|
149
|
+
namespace: 'cattle-local-user-passwords',
|
|
150
|
+
name: userSaved.id
|
|
151
|
+
},
|
|
152
|
+
data: { password: base64Encode(this.form.password.password) }
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await secret.save();
|
|
156
|
+
}
|
|
149
157
|
|
|
150
|
-
return
|
|
158
|
+
return userSaved;
|
|
151
159
|
},
|
|
152
160
|
|
|
153
161
|
async editUser() {
|
|
@@ -155,16 +163,13 @@ export default {
|
|
|
155
163
|
return;
|
|
156
164
|
}
|
|
157
165
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
// never use this.value.password as it's deprecated!
|
|
167
|
+
this.value.description = this.form.description;
|
|
168
|
+
this.value.displayName = this.form.displayName;
|
|
169
|
+
this.value.mustChangePassword = this.form.password.userChangeOnLogin;
|
|
162
170
|
|
|
163
|
-
// Save change of password
|
|
164
|
-
// - Password must be changed before editing mustChangePassword (setpassword action sets this to false)
|
|
165
171
|
if (this.form.password.password) {
|
|
166
|
-
await this.$refs.changePassword.save(
|
|
167
|
-
|
|
172
|
+
await this.$refs.changePassword.save(this.value);
|
|
168
173
|
// Why the wait? Without these the user updates below are ignored
|
|
169
174
|
// - The update request succeeds and shows the correct values in it's response.
|
|
170
175
|
// - Fetching the norman user again sometimes shows the correct value, sometimes not
|
|
@@ -173,18 +178,7 @@ export default {
|
|
|
173
178
|
await wait(5000);
|
|
174
179
|
}
|
|
175
180
|
|
|
176
|
-
|
|
177
|
-
normanUser.description = this.form.description;
|
|
178
|
-
normanUser._name = this.form.displayName;
|
|
179
|
-
normanUser.mustChangePassword = this.form.password.userChangeOnLogin;
|
|
180
|
-
|
|
181
|
-
await normanUser.save();
|
|
182
|
-
|
|
183
|
-
return await this.$store.dispatch('management/find', {
|
|
184
|
-
type: MANAGEMENT.USER,
|
|
185
|
-
id: this.value.id,
|
|
186
|
-
opt: { force: true }
|
|
187
|
-
});
|
|
181
|
+
this.value.save();
|
|
188
182
|
},
|
|
189
183
|
|
|
190
184
|
async updateRoles(userId) {
|
|
@@ -267,6 +261,7 @@ export default {
|
|
|
267
261
|
v-model:value="form.password"
|
|
268
262
|
:mode="mode"
|
|
269
263
|
:must-change-password="value.mustChangePassword"
|
|
264
|
+
:user="value"
|
|
270
265
|
@valid="validation.password = $event"
|
|
271
266
|
/>
|
|
272
267
|
</div>
|
|
@@ -4,6 +4,7 @@ import { _CREATE } from '@shell/config/query-params';
|
|
|
4
4
|
import rke2 from '@shell/edit/provisioning.cattle.io.cluster/rke2.vue';
|
|
5
5
|
import { get } from '@shell/utils/object';
|
|
6
6
|
import { rke2TestTable } from './utils/rke2-test-data';
|
|
7
|
+
import { NGINX_SUPPORTED, INGRESS_CONTROLLER, INGRESS_NGINX } from '@shell/edit/provisioning.cattle.io.cluster/shared';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* DISCLAIMER ***************************************************************************************
|
|
@@ -612,4 +613,181 @@ describe('component: rke2', () => {
|
|
|
612
613
|
|
|
613
614
|
expect(wrapper.vm.value.spec.rkeConfig.chartValues).toStrictEqual(expected);
|
|
614
615
|
});
|
|
616
|
+
|
|
617
|
+
describe('should correctly update NGINX configuration', () => {
|
|
618
|
+
const k8sVersion = 'v1.25.0+rke2r1';
|
|
619
|
+
const createWrapper = () => {
|
|
620
|
+
return shallowMount(rke2, {
|
|
621
|
+
props: {
|
|
622
|
+
mode: _CREATE,
|
|
623
|
+
value: {
|
|
624
|
+
spec: {
|
|
625
|
+
...defaultSpec,
|
|
626
|
+
rkeConfig: {
|
|
627
|
+
machineGlobalConfig: {},
|
|
628
|
+
chartValues: {},
|
|
629
|
+
upgradeStrategy: {},
|
|
630
|
+
dataDirectories: {},
|
|
631
|
+
machineSelectorConfig: []
|
|
632
|
+
},
|
|
633
|
+
kubernetesVersion: k8sVersion,
|
|
634
|
+
},
|
|
635
|
+
agentConfig: {}
|
|
636
|
+
},
|
|
637
|
+
provider: 'custom',
|
|
638
|
+
},
|
|
639
|
+
global: {
|
|
640
|
+
mocks: {
|
|
641
|
+
...defaultMocks,
|
|
642
|
+
$store: { dispatch: () => jest.fn(), getters: defaultGetters },
|
|
643
|
+
$extension: { getDynamic: jest.fn(() => undefined ) },
|
|
644
|
+
},
|
|
645
|
+
stubs: defaultStubs,
|
|
646
|
+
},
|
|
647
|
+
});
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
it('should set ingress-controller to ingress-nginx by default when nginx is supported and not disabled', async() => {
|
|
651
|
+
const wrapper = createWrapper();
|
|
652
|
+
|
|
653
|
+
await wrapper.setData({
|
|
654
|
+
rke2Versions: [{
|
|
655
|
+
id: k8sVersion,
|
|
656
|
+
version: k8sVersion,
|
|
657
|
+
serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
|
|
658
|
+
}]
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
it('should set ingress-controller to undefined by default when nginx is not supported', async() => {
|
|
665
|
+
const wrapper = createWrapper();
|
|
666
|
+
|
|
667
|
+
await wrapper.setData({
|
|
668
|
+
rke2Versions: [{
|
|
669
|
+
id: k8sVersion,
|
|
670
|
+
version: k8sVersion,
|
|
671
|
+
serverArgs: { disable: { options: [] } }
|
|
672
|
+
}]
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBeUndefined();
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it('should set ingress-controller to ingress-nginx on change when nginx is supported and not disabled', () => {
|
|
679
|
+
const wrapper = createWrapper();
|
|
680
|
+
|
|
681
|
+
wrapper.setData({
|
|
682
|
+
rke2Versions: [{
|
|
683
|
+
id: k8sVersion,
|
|
684
|
+
version: k8sVersion,
|
|
685
|
+
serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
|
|
686
|
+
}]
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
(wrapper.vm as any).handleEnabledSystemServicesChanged([]);
|
|
690
|
+
|
|
691
|
+
expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
it('should set ingress-controller to undefined when nginx is supported but disabled', () => {
|
|
695
|
+
const wrapper = createWrapper();
|
|
696
|
+
|
|
697
|
+
wrapper.setData({
|
|
698
|
+
rke2Versions: [{
|
|
699
|
+
id: k8sVersion,
|
|
700
|
+
version: k8sVersion,
|
|
701
|
+
serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
|
|
702
|
+
}]
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
(wrapper.vm as any).handleEnabledSystemServicesChanged([NGINX_SUPPORTED]);
|
|
706
|
+
|
|
707
|
+
expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBeUndefined();
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
it('should set ingress-controller to undefined when nginx is not supported', () => {
|
|
711
|
+
const wrapper = createWrapper();
|
|
712
|
+
|
|
713
|
+
wrapper.setData({
|
|
714
|
+
rke2Versions: [{
|
|
715
|
+
id: k8sVersion,
|
|
716
|
+
version: k8sVersion,
|
|
717
|
+
serverArgs: { disable: { options: [] } }
|
|
718
|
+
}]
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
(wrapper.vm as any).handleEnabledSystemServicesChanged([]);
|
|
722
|
+
|
|
723
|
+
expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBeUndefined();
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
it('should correctly update disable list in serverConfig', () => {
|
|
727
|
+
const wrapper = createWrapper();
|
|
728
|
+
|
|
729
|
+
wrapper.setData({
|
|
730
|
+
rke2Versions: [{
|
|
731
|
+
id: k8sVersion,
|
|
732
|
+
version: k8sVersion,
|
|
733
|
+
serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
|
|
734
|
+
}]
|
|
735
|
+
});
|
|
736
|
+
const disabledServices = ['other-service'];
|
|
737
|
+
|
|
738
|
+
(wrapper.vm as any).handleEnabledSystemServicesChanged(disabledServices);
|
|
739
|
+
|
|
740
|
+
expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig.disable).toStrictEqual(disabledServices);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
it('should set ingress-controller to ingress-nginx on version change when nginx is supported', async() => {
|
|
744
|
+
const wrapper = createWrapper();
|
|
745
|
+
const newVersion = 'v1.26.0+rke2r1';
|
|
746
|
+
|
|
747
|
+
await wrapper.setData({
|
|
748
|
+
rke2Versions: [
|
|
749
|
+
{
|
|
750
|
+
id: k8sVersion,
|
|
751
|
+
version: k8sVersion,
|
|
752
|
+
serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
id: newVersion,
|
|
756
|
+
version: newVersion,
|
|
757
|
+
serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
|
|
758
|
+
}
|
|
759
|
+
]
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
wrapper.vm.value.spec.kubernetesVersion = newVersion;
|
|
763
|
+
(wrapper.vm as any).handleKubernetesChange(newVersion);
|
|
764
|
+
|
|
765
|
+
expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it('should not set ingress-controller to ingress-nginx on version change when nginx is not supported', async() => {
|
|
769
|
+
const wrapper = createWrapper();
|
|
770
|
+
const newVersion = 'v1.26.0+rke2r1';
|
|
771
|
+
|
|
772
|
+
await wrapper.setData({
|
|
773
|
+
k3sVersions: [
|
|
774
|
+
{
|
|
775
|
+
id: k8sVersion,
|
|
776
|
+
version: k8sVersion,
|
|
777
|
+
serverArgs: { disable: { options: [] } }
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
id: newVersion,
|
|
781
|
+
version: newVersion,
|
|
782
|
+
serverArgs: { disable: { options: [] } }
|
|
783
|
+
}
|
|
784
|
+
]
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
wrapper.vm.value.spec.kubernetesVersion = newVersion;
|
|
788
|
+
(wrapper.vm as any).handleKubernetesChange(newVersion);
|
|
789
|
+
|
|
790
|
+
expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBeUndefined();
|
|
791
|
+
});
|
|
792
|
+
});
|
|
615
793
|
});
|
|
@@ -66,6 +66,7 @@ import { DEFAULT_COMMON_BASE_PATH, DEFAULT_SUBDIRS } from '@shell/edit/provision
|
|
|
66
66
|
import ClusterAppearance from '@shell/components/form/ClusterAppearance';
|
|
67
67
|
import AddOnAdditionalManifest from '@shell/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest';
|
|
68
68
|
import VsphereUtils, { VMWARE_VSPHERE } from '@shell/utils/v-sphere';
|
|
69
|
+
import { RETENTION_DEFAULT, NGINX_SUPPORTED, INGRESS_CONTROLLER, INGRESS_NGINX } from '@shell/edit/provisioning.cattle.io.cluster/shared';
|
|
69
70
|
import { mapGetters } from 'vuex';
|
|
70
71
|
const HARVESTER = 'harvester';
|
|
71
72
|
const GOOGLE = 'google';
|
|
@@ -891,7 +892,13 @@ export default {
|
|
|
891
892
|
this.fvFormIsValid &&
|
|
892
893
|
this.etcdConfigValid;
|
|
893
894
|
},
|
|
895
|
+
nginxSupported() {
|
|
896
|
+
if (this.serverArgs?.disable?.options.includes(NGINX_SUPPORTED)) {
|
|
897
|
+
return true;
|
|
898
|
+
}
|
|
894
899
|
|
|
900
|
+
return false;
|
|
901
|
+
},
|
|
895
902
|
},
|
|
896
903
|
|
|
897
904
|
watch: {
|
|
@@ -999,7 +1006,7 @@ export default {
|
|
|
999
1006
|
|
|
1000
1007
|
this.localValue.spec.rkeConfig.networking.stackPreference = STACK_PREFS.IPV4;
|
|
1001
1008
|
}
|
|
1002
|
-
}
|
|
1009
|
+
}
|
|
1003
1010
|
},
|
|
1004
1011
|
|
|
1005
1012
|
created() {
|
|
@@ -1061,7 +1068,7 @@ export default {
|
|
|
1061
1068
|
this.rkeConfig.etcd = {
|
|
1062
1069
|
disableSnapshots: false,
|
|
1063
1070
|
s3: null,
|
|
1064
|
-
snapshotRetention:
|
|
1071
|
+
snapshotRetention: RETENTION_DEFAULT,
|
|
1065
1072
|
snapshotScheduleCron: '0 */5 * * *',
|
|
1066
1073
|
};
|
|
1067
1074
|
} else if (typeof this.rkeConfig.etcd.disableSnapshots === 'undefined') {
|
|
@@ -1882,6 +1889,7 @@ export default {
|
|
|
1882
1889
|
if (!this.serverConfig?.profile) {
|
|
1883
1890
|
this.serverConfig.profile = null;
|
|
1884
1891
|
}
|
|
1892
|
+
this.updateNginxConfiguration(this.serverConfig?.disable || []);
|
|
1885
1893
|
},
|
|
1886
1894
|
|
|
1887
1895
|
chartVersionKey(name) {
|
|
@@ -2116,6 +2124,8 @@ export default {
|
|
|
2116
2124
|
handleKubernetesChange(value, old) {
|
|
2117
2125
|
if (value) {
|
|
2118
2126
|
this.togglePsaDefault();
|
|
2127
|
+
// Need to make sure we explicitly set ingress due to a default change
|
|
2128
|
+
this.updateNginxConfiguration(this.serverConfig?.disable || []);
|
|
2119
2129
|
|
|
2120
2130
|
// If Harvester driver, reset cloud provider if not compatible
|
|
2121
2131
|
if (this.isHarvesterDriver && this.mode === _CREATE && this.isHarvesterIncompatible) {
|
|
@@ -2137,8 +2147,18 @@ export default {
|
|
|
2137
2147
|
this.machinePoolValidation[id] = value;
|
|
2138
2148
|
}
|
|
2139
2149
|
},
|
|
2150
|
+
|
|
2151
|
+
updateNginxConfiguration(val) {
|
|
2152
|
+
if (val.includes(NGINX_SUPPORTED) || !this.nginxSupported) {
|
|
2153
|
+
this.serverConfig[INGRESS_CONTROLLER] = undefined;
|
|
2154
|
+
} else if (this.serverConfig[INGRESS_CONTROLLER] !== INGRESS_NGINX) {
|
|
2155
|
+
this.serverConfig[INGRESS_CONTROLLER] = INGRESS_NGINX;
|
|
2156
|
+
}
|
|
2157
|
+
},
|
|
2158
|
+
|
|
2140
2159
|
handleEnabledSystemServicesChanged(val) {
|
|
2141
2160
|
this.serverConfig.disable = val;
|
|
2161
|
+
this.updateNginxConfiguration(val);
|
|
2142
2162
|
},
|
|
2143
2163
|
|
|
2144
2164
|
handleCiliumValuesChanged(neu) {
|
|
@@ -580,6 +580,7 @@ export default {
|
|
|
580
580
|
<div class="col span-6">
|
|
581
581
|
<!-- PSA template selector -->
|
|
582
582
|
<LabeledSelect
|
|
583
|
+
:key="value.isK3s"
|
|
583
584
|
v-model:value="value.spec.defaultPodSecurityAdmissionConfigurationTemplateName"
|
|
584
585
|
:mode="mode"
|
|
585
586
|
data-testid="rke2-custom-edit-psa"
|
|
@@ -4,6 +4,10 @@ import { LabeledInput } from '@components/Form/LabeledInput';
|
|
|
4
4
|
import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret';
|
|
5
5
|
import { NORMAN } from '@shell/config/types';
|
|
6
6
|
import FormValidation from '@shell/mixins/form-validation';
|
|
7
|
+
import UnitInput from '@shell/components/form/UnitInput';
|
|
8
|
+
import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
|
|
9
|
+
import { _CREATE } from '@shell/config/query-params';
|
|
10
|
+
import { RETENTION_DEFAULT } from '@shell/edit/provisioning.cattle.io.cluster/shared';
|
|
7
11
|
|
|
8
12
|
export default {
|
|
9
13
|
emits: ['update:value', 'validationChanged'],
|
|
@@ -12,6 +16,8 @@ export default {
|
|
|
12
16
|
LabeledInput,
|
|
13
17
|
Checkbox,
|
|
14
18
|
SelectOrCreateAuthSecret,
|
|
19
|
+
UnitInput,
|
|
20
|
+
RadioGroup
|
|
15
21
|
},
|
|
16
22
|
mixins: [FormValidation],
|
|
17
23
|
|
|
@@ -35,6 +41,10 @@ export default {
|
|
|
35
41
|
type: Function,
|
|
36
42
|
required: true,
|
|
37
43
|
},
|
|
44
|
+
localRetentionCount: {
|
|
45
|
+
type: Number,
|
|
46
|
+
default: null,
|
|
47
|
+
}
|
|
38
48
|
},
|
|
39
49
|
|
|
40
50
|
data() {
|
|
@@ -47,9 +57,11 @@ export default {
|
|
|
47
57
|
folder: '',
|
|
48
58
|
region: '',
|
|
49
59
|
skipSSLVerify: false,
|
|
60
|
+
retention: null,
|
|
50
61
|
...(this.value || {}),
|
|
51
62
|
},
|
|
52
|
-
|
|
63
|
+
differentRetention: false,
|
|
64
|
+
fvFormRuleSets: [
|
|
53
65
|
{
|
|
54
66
|
path: 'endpoint', rootObject: this.config, rules: ['awsStyleEndpoint']
|
|
55
67
|
},
|
|
@@ -59,6 +71,9 @@ export default {
|
|
|
59
71
|
]
|
|
60
72
|
};
|
|
61
73
|
},
|
|
74
|
+
mounted() {
|
|
75
|
+
this.differentRetention = !(this.mode === _CREATE || this.value?.retention === this.localRetentionCount);
|
|
76
|
+
},
|
|
62
77
|
|
|
63
78
|
computed: {
|
|
64
79
|
|
|
@@ -73,10 +88,25 @@ export default {
|
|
|
73
88
|
|
|
74
89
|
return {};
|
|
75
90
|
},
|
|
91
|
+
|
|
92
|
+
localCountToUse() {
|
|
93
|
+
return this.localRetentionCount === null || this.localRetentionCount === undefined ? RETENTION_DEFAULT : this.localRetentionCount;
|
|
94
|
+
},
|
|
95
|
+
retentionOptionsOptions() {
|
|
96
|
+
return [
|
|
97
|
+
{ label: this.t('cluster.rke2.etcd.s3config.snapshotRetention.options.localDefined', { count: this.localCountToUse }), value: false }, { label: this.t('cluster.rke2.etcd.s3config.snapshotRetention.options.manual'), value: true }
|
|
98
|
+
];
|
|
99
|
+
}
|
|
76
100
|
},
|
|
77
101
|
watch: {
|
|
78
102
|
fvFormIsValid(newValue) {
|
|
79
103
|
this.$emit('validationChanged', !!newValue);
|
|
104
|
+
},
|
|
105
|
+
localRetentionCount(neu) {
|
|
106
|
+
if (!this.differentRetention) {
|
|
107
|
+
this.config.retention = this.localCountToUse;
|
|
108
|
+
this.update();
|
|
109
|
+
}
|
|
80
110
|
}
|
|
81
111
|
},
|
|
82
112
|
|
|
@@ -86,6 +116,10 @@ export default {
|
|
|
86
116
|
|
|
87
117
|
this.$emit('update:value', out);
|
|
88
118
|
},
|
|
119
|
+
resetRetention() {
|
|
120
|
+
this.config.retention = this.localCountToUse;
|
|
121
|
+
this.update();
|
|
122
|
+
}
|
|
89
123
|
},
|
|
90
124
|
};
|
|
91
125
|
</script>
|
|
@@ -150,7 +184,6 @@ export default {
|
|
|
150
184
|
/>
|
|
151
185
|
</div>
|
|
152
186
|
</div>
|
|
153
|
-
|
|
154
187
|
<div
|
|
155
188
|
v-if="!ccData.defaultSkipSSLVerify"
|
|
156
189
|
class="mt-20"
|
|
@@ -172,5 +205,27 @@ export default {
|
|
|
172
205
|
@update:value="update"
|
|
173
206
|
/>
|
|
174
207
|
</div>
|
|
208
|
+
<div class="row mt-20">
|
|
209
|
+
<div class="col span-6">
|
|
210
|
+
<h4>{{ t('cluster.rke2.etcd.s3config.snapshotRetention.title') }}</h4>
|
|
211
|
+
<RadioGroup
|
|
212
|
+
v-model:value="differentRetention"
|
|
213
|
+
name="s3config-retention"
|
|
214
|
+
:mode="mode"
|
|
215
|
+
:options="retentionOptionsOptions"
|
|
216
|
+
:row="true"
|
|
217
|
+
@update:value="resetRetention"
|
|
218
|
+
/>
|
|
219
|
+
<UnitInput
|
|
220
|
+
v-if="differentRetention"
|
|
221
|
+
v-model:value="config.retention"
|
|
222
|
+
:label="t('cluster.rke2.etcd.s3config.snapshotRetention.label')"
|
|
223
|
+
:mode="mode"
|
|
224
|
+
:suffix="t('cluster.rke2.snapshots.s3Suffix')"
|
|
225
|
+
class="mt-10"
|
|
226
|
+
@update:value="update"
|
|
227
|
+
/>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
175
230
|
</div>
|
|
176
231
|
</template>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import S3Config from '@shell/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue';
|
|
3
|
+
|
|
4
|
+
describe('s3Config', () => {
|
|
5
|
+
const defaultProps = {
|
|
6
|
+
mode: 'create',
|
|
7
|
+
namespace: 'test-ns',
|
|
8
|
+
registerBeforeHook: jest.fn(),
|
|
9
|
+
localRetentionCount: 5,
|
|
10
|
+
value: {}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const mockStore = { getters: { 'rancher/byId': jest.fn() } };
|
|
14
|
+
|
|
15
|
+
const createWrapper = (props = {}) => {
|
|
16
|
+
return shallowMount(S3Config, {
|
|
17
|
+
propsData: {
|
|
18
|
+
...defaultProps,
|
|
19
|
+
...props
|
|
20
|
+
},
|
|
21
|
+
mocks: {
|
|
22
|
+
$store: mockStore,
|
|
23
|
+
t: (key) => key,
|
|
24
|
+
},
|
|
25
|
+
stubs: {
|
|
26
|
+
LabeledInput: true,
|
|
27
|
+
Checkbox: true,
|
|
28
|
+
SelectOrCreateAuthSecret: true,
|
|
29
|
+
UnitInput: true,
|
|
30
|
+
RadioGroup: true,
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
it('renders correctly', () => {
|
|
36
|
+
const wrapper = createWrapper();
|
|
37
|
+
|
|
38
|
+
expect(wrapper.exists()).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('initializes config with default values', () => {
|
|
42
|
+
const wrapper = createWrapper();
|
|
43
|
+
|
|
44
|
+
expect(wrapper.vm.config.bucket).toBe('');
|
|
45
|
+
expect(wrapper.vm.config.skipSSLVerify).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('initializes config with provided value prop', () => {
|
|
49
|
+
const value = {
|
|
50
|
+
bucket: 'test',
|
|
51
|
+
region: 'us-east-1',
|
|
52
|
+
retention: 2
|
|
53
|
+
};
|
|
54
|
+
const wrapper = createWrapper({ value });
|
|
55
|
+
|
|
56
|
+
expect(wrapper.vm.config.bucket).toBe('test');
|
|
57
|
+
expect(wrapper.vm.config.region).toBe('us-east-1');
|
|
58
|
+
expect(wrapper.vm.config.retention).toBe(2);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('retention Logic', () => {
|
|
62
|
+
it('computes localCountToUse correctly', () => {
|
|
63
|
+
const wrapper = createWrapper({ localRetentionCount: 3 });
|
|
64
|
+
|
|
65
|
+
expect(wrapper.vm.localCountToUse).toBe(3);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('uses default retention if localRetentionCount is null', () => {
|
|
69
|
+
const wrapper = createWrapper({ localRetentionCount: null });
|
|
70
|
+
|
|
71
|
+
expect(wrapper.vm.localCountToUse).toBe(5);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('sets differentRetention to false in create mode', () => {
|
|
75
|
+
const wrapper = createWrapper({ mode: 'create' });
|
|
76
|
+
|
|
77
|
+
expect(wrapper.vm.differentRetention).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('sets differentRetention to false in edit mode if retention matches', () => {
|
|
81
|
+
const wrapper = createWrapper({
|
|
82
|
+
mode: 'edit',
|
|
83
|
+
value: { retention: 5 },
|
|
84
|
+
localRetentionCount: 5
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(wrapper.vm.differentRetention).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('sets differentRetention to true in edit mode if retention differs', () => {
|
|
91
|
+
const wrapper = createWrapper({
|
|
92
|
+
mode: 'edit',
|
|
93
|
+
value: { retention: 2 },
|
|
94
|
+
localRetentionCount: 5
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(wrapper.vm.differentRetention).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('updates retention when localRetentionCount changes and differentRetention is false', async() => {
|
|
101
|
+
const wrapper = createWrapper({ localRetentionCount: 5 });
|
|
102
|
+
|
|
103
|
+
// differentRetention is false by default in create mode
|
|
104
|
+
await wrapper.setProps({ localRetentionCount: 10 });
|
|
105
|
+
expect(wrapper.vm.config.retention).toBe(10);
|
|
106
|
+
expect(wrapper.emitted('update:value')).toBeTruthy();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { BadgeState } from '@components/BadgeState';
|
|
3
|
+
import { AGE } from '@shell/config/table-headers';
|
|
4
|
+
import ResourceFetch from '@shell/mixins/resource-fetch';
|
|
5
|
+
import ResourceTable from '@shell/components/ResourceTable.vue';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
components: { BadgeState, ResourceTable },
|
|
9
|
+
mixins: [ResourceFetch],
|
|
10
|
+
props: {
|
|
11
|
+
resource: {
|
|
12
|
+
type: String,
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
schema: {
|
|
17
|
+
type: Object,
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
useQueryParamsForSimpleFiltering: {
|
|
22
|
+
type: Boolean,
|
|
23
|
+
default: false
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
async fetch() {
|
|
28
|
+
await this.$fetchType(this.resource);
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
computed: {
|
|
32
|
+
headers() {
|
|
33
|
+
const headersFromSchema = this.$store.getters['type-map/headersFor'](this.schema);
|
|
34
|
+
|
|
35
|
+
const headers = headersFromSchema.filter((h: {name : string}) => h.name !== 'valid' && h.name !== 'active');
|
|
36
|
+
|
|
37
|
+
headers.push(AGE);
|
|
38
|
+
|
|
39
|
+
return headers;
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
};
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<ResourceTable
|
|
48
|
+
:schema="schema"
|
|
49
|
+
:headers="headers"
|
|
50
|
+
:rows="rows"
|
|
51
|
+
:loading="loading"
|
|
52
|
+
:use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
|
|
53
|
+
:namespaced="schema.attributes.namespaced"
|
|
54
|
+
fetchSecondaryResources="fetchSecondaryResources"
|
|
55
|
+
>
|
|
56
|
+
<template #cell:enabled="{row}">
|
|
57
|
+
<BadgeState
|
|
58
|
+
:color="row.spec.enabled ? 'bg-success' : 'badge-disabled'"
|
|
59
|
+
:label="row.spec.enabled ? t('generic.enabled') : t('generic.disabled') "
|
|
60
|
+
/>
|
|
61
|
+
</template>
|
|
62
|
+
</ResourceTable>
|
|
63
|
+
</template>
|