@rancher/shell 3.0.0-rc.9 → 3.0.0

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.
Files changed (64) hide show
  1. package/assets/translations/en-us.yaml +12 -11
  2. package/assets/translations/zh-hans.yaml +1 -4
  3. package/components/EmberPage.vue +0 -8
  4. package/components/ResourceTable.vue +26 -1
  5. package/components/SortableTable/actions.js +1 -1
  6. package/components/SortableTable/index.vue +17 -1
  7. package/components/SortableTable/selection.js +1 -1
  8. package/components/SortableTable/sorting.js +11 -3
  9. package/components/fleet/FleetClusters.vue +0 -3
  10. package/components/form/HookOption.vue +31 -29
  11. package/components/form/LabeledSelect.vue +0 -1
  12. package/components/form/LifecycleHooks.vue +2 -2
  13. package/components/formatter/SecretData.vue +1 -1
  14. package/components/nav/Header.vue +10 -13
  15. package/components/nav/TopLevelMenu.vue +0 -40
  16. package/components/nav/WorkspaceSwitcher.vue +0 -1
  17. package/config/private-label.js +2 -1
  18. package/config/router/routes.js +2 -26
  19. package/config/settings.ts +5 -0
  20. package/config/version.js +2 -0
  21. package/detail/catalog.cattle.io.app.vue +17 -4
  22. package/detail/fleet.cattle.io.cluster.vue +11 -9
  23. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  24. package/edit/cis.cattle.io.clusterscan.vue +4 -3
  25. package/edit/fleet.cattle.io.gitrepo.vue +11 -8
  26. package/edit/management.cattle.io.project.vue +2 -1
  27. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +5 -5
  28. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -1
  29. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +26 -0
  30. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +63 -149
  31. package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -1
  32. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +7 -2
  33. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +108 -35
  34. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +1 -1
  35. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +1 -1
  36. package/edit/workload/mixins/workload.js +1 -1
  37. package/mixins/browser-tab-visibility.js +1 -1
  38. package/mixins/chart.js +6 -2
  39. package/mixins/metric-poller.js +1 -1
  40. package/mixins/resource-fetch.js +1 -1
  41. package/models/catalog.cattle.io.app.js +108 -21
  42. package/models/cloudcredential.js +4 -4
  43. package/models/fleet.cattle.io.gitrepo.js +8 -13
  44. package/models/management.cattle.io.cluster.js +13 -2
  45. package/models/management.cattle.io.project.js +4 -0
  46. package/models/provisioning.cattle.io.cluster.js +1 -2
  47. package/models/workload.js +1 -1
  48. package/package.json +3 -3
  49. package/pages/auth/logout.vue +7 -9
  50. package/pages/auth/setup.vue +3 -0
  51. package/pages/c/_cluster/apps/charts/install.vue +11 -3
  52. package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
  53. package/pages/c/_cluster/explorer/index.vue +1 -2
  54. package/pages/c/_cluster/fleet/index.vue +11 -5
  55. package/pages/c/_cluster/uiplugins/index.vue +4 -2
  56. package/pages/diagnostic.vue +1 -0
  57. package/plugins/steve/mutations.js +4 -1
  58. package/plugins/steve/subscribe.js +3 -4
  59. package/types/shell/index.d.ts +13 -0
  60. package/utils/__tests__/object.test.ts +152 -1
  61. package/utils/object.js +37 -0
  62. package/utils/string.js +9 -0
  63. package/utils/validators/formRules/index.ts +1 -1
  64. package/config/product/multi-cluster-apps.js +0 -61
@@ -9,7 +9,8 @@ import RelatedResources from '@shell/components/RelatedResources';
9
9
  import jsyaml from 'js-yaml';
10
10
  import merge from 'lodash/merge';
11
11
  import { CATALOG } from '@shell/config/types';
12
- import { sortBy } from '~shell/utils/sort';
12
+ import { sortBy } from '@shell/utils/sort';
13
+ import { allHash } from '@shell/utils/promise';
13
14
 
14
15
  export default {
15
16
  name: 'DetailRelease',
@@ -30,9 +31,15 @@ export default {
30
31
  },
31
32
 
32
33
  async fetch() {
33
- await this.$store.dispatch('catalog/load');
34
+ const promises = {
35
+ catalog: this.$store.dispatch('catalog/load'),
36
+ allOperations: this.$store.dispatch('cluster/findAll', { type: CATALOG.OPERATION }),
37
+ secrets: this.value.fetchValues(true),
38
+ };
34
39
 
35
- this.allOperations = await this.$store.dispatch('cluster/findAll', { type: CATALOG.OPERATION });
40
+ const res = await allHash(promises);
41
+
42
+ this.allOperations = res.allOperations;
36
43
  },
37
44
 
38
45
  computed: {
@@ -45,7 +52,7 @@ export default {
45
52
  },
46
53
 
47
54
  valuesYaml() {
48
- const combined = merge(merge({}, this.value?.spec?.chart?.values || {}), this.value?.spec?.values || {});
55
+ const combined = merge(merge({}, this.value?.chartValues || {}), this.value?.values || {});
49
56
 
50
57
  return jsyaml.dump(combined);
51
58
  },
@@ -95,6 +102,12 @@ export default {
95
102
  }
96
103
  },
97
104
  },
105
+
106
+ watch: {
107
+ 'value.secretId'(neu, old) {
108
+ this.value.fetchValues(true);
109
+ }
110
+ },
98
111
  };
99
112
  </script>
100
113
 
@@ -6,6 +6,7 @@ import ResourceTabs from '@shell/components/form/ResourceTabs';
6
6
  import Tab from '@shell/components/Tabbed/Tab';
7
7
  import { MANAGEMENT, FLEET } from '@shell/config/types';
8
8
  import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
9
+ import { allHash } from 'utils/promise';
9
10
 
10
11
  export default {
11
12
  name: 'FleetDetailCluster',
@@ -29,17 +30,18 @@ export default {
29
30
 
30
31
  async fetch() {
31
32
  const clusterId = this.value?.metadata?.labels[FLEET_LABELS.CLUSTER_NAME];
32
-
33
- this.rancherCluster = await this.$store.dispatch('management/find', {
34
- type: MANAGEMENT.CLUSTER,
35
- id: clusterId
33
+ const hash = await allHash({
34
+ rancherCluster: this.$store.dispatch('management/find', {
35
+ type: MANAGEMENT.CLUSTER,
36
+ id: clusterId
37
+ }),
38
+ repos: this.$store.dispatch('management/findAll', { type: FLEET.GIT_REPO }),
39
+ workspaces: this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE }),
40
+ bundleDeployments: this.$store.dispatch('management/findAll', { type: FLEET.BUNDLE_DEPLOYMENT })
36
41
  });
37
42
 
38
- this.allRepos = await this.$store.dispatch('management/findAll', { type: FLEET.GIT_REPO });
39
-
40
- await this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE });
41
-
42
- await this.$store.dispatch('management/findAll', { type: FLEET.BUNDLE_DEPLOYMENT });
43
+ this.rancherCluster = hash.rancherCluster;
44
+ this.allRepos = hash.repos;
43
45
  },
44
46
 
45
47
  data() {
@@ -42,7 +42,7 @@ export default {
42
42
  },
43
43
  computed: {
44
44
  gitRepoHasClusters() {
45
- return this.value?.clusterResourceStatus?.length;
45
+ return this.value.status.desiredReadyClusters;
46
46
  },
47
47
  clusterSchema() {
48
48
  return this.$store.getters['management/schemaFor'](FLEET.CLUSTER);
@@ -12,7 +12,7 @@ import { allHash } from '@shell/utils/promise';
12
12
  import { Checkbox } from '@components/Form/Checkbox';
13
13
  import { RadioGroup } from '@components/Form/Radio';
14
14
  import { get } from '@shell/utils/object';
15
- import { _VIEW, _CREATE, _EDIT } from '@shell/config/query-params';
15
+ import { _VIEW, _CREATE } from '@shell/config/query-params';
16
16
  import { isValidCron } from 'cron-validator';
17
17
  import { fetchSpecsScheduledScanConfig } from '@shell/models/cis.cattle.io.clusterscan';
18
18
 
@@ -43,7 +43,8 @@ export default {
43
43
  // in the clusterscan edit/create views the "canBeScheduled" won't run properly
44
44
  await this.schema.fetchResourceFields();
45
45
 
46
- if (this.realMode === _CREATE || this.realMode === _EDIT) {
46
+ // Only initialize on create or if we don't have a spec object (added for resilience)
47
+ if (this.realMode === _CREATE || !this.value.spec) {
47
48
  const includeScheduling = this.value.canBeScheduled();
48
49
  const spec = this.value.spec || {};
49
50
 
@@ -326,7 +327,7 @@ export default {
326
327
  </div>
327
328
  <div class="col span-6">
328
329
  <UnitInput
329
- v-model.number="scheduledScanConfig.retentionCount"
330
+ v-model:value="scheduledScanConfig.retentionCount"
330
331
  :suffix="t('cis.reports')"
331
332
  type="number"
332
333
  :mode="mode"
@@ -25,6 +25,7 @@ import { CAPI, CATALOG, FLEET as FLEET_LABELS } from '@shell/config/labels-annot
25
25
  import { SECRET_TYPES } from '@shell/config/secret';
26
26
  import { checkSchemasForFindAllHash } from '@shell/utils/auth';
27
27
  import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
28
+ import FormValidation from '@shell/mixins/form-validation';
28
29
 
29
30
  const _VERIFY = 'verify';
30
31
  const _SKIP = 'skip';
@@ -33,6 +34,8 @@ const _SPECIFY = 'specify';
33
34
  export default {
34
35
  name: 'CruGitRepo',
35
36
 
37
+ inheritAttrs: false,
38
+
36
39
  emits: ['input'],
37
40
 
38
41
  components: {
@@ -50,7 +53,7 @@ export default {
50
53
  SelectOrCreateAuthSecret,
51
54
  },
52
55
 
53
- mixins: [CreateEditView],
56
+ mixins: [CreateEditView, FormValidation],
54
57
 
55
58
  async fetch() {
56
59
  const hash = await checkSchemasForFindAllHash({
@@ -153,7 +156,8 @@ export default {
153
156
  stepRepoInfo,
154
157
  stepTargetInfo,
155
158
  addRepositorySteps,
156
- displayHelmRepoURLRegex: false
159
+ displayHelmRepoURLRegex: false,
160
+ fvFormRuleSets: [{ path: 'spec.repo', rules: ['required'] }]
157
161
  };
158
162
  },
159
163
 
@@ -253,7 +257,7 @@ export default {
253
257
  },
254
258
 
255
259
  stepOneRequires() {
256
- return !!this.value.metadata.name && !!this.refValue;
260
+ return !!this.value.metadata.name && !!this.refValue && !!this.fvFormIsValid;
257
261
  },
258
262
  },
259
263
 
@@ -265,6 +269,8 @@ export default {
265
269
  targetAdvanced: 'updateTargets',
266
270
  tlsMode: 'updateTls',
267
271
  caBundle: 'updateTls',
272
+ 'value.metadata.name': 'stepOneReady',
273
+ 'value.spec.repo': 'stepOneReady',
268
274
 
269
275
  workspace(neu) {
270
276
  if ( this.isCreate ) {
@@ -456,10 +462,6 @@ export default {
456
462
  this.tlsMode = event;
457
463
  },
458
464
 
459
- onUpdateRepoName() {
460
- this.stepOneReady();
461
- },
462
-
463
465
  stepOneReady() {
464
466
  this.addRepositorySteps[0]['ready'] = this.stepOneRequires;
465
467
  },
@@ -528,7 +530,6 @@ export default {
528
530
  :namespaced="false"
529
531
  :mode="mode"
530
532
  @update:value="$emit('input', $event)"
531
- @change="onUpdateRepoName"
532
533
  />
533
534
 
534
535
  <div class="row">
@@ -552,6 +553,8 @@ export default {
552
553
  :mode="mode"
553
554
  label-key="fleet.gitRepo.repo.label"
554
555
  :placeholder="t('fleet.gitRepo.repo.placeholder', null, true)"
556
+ :required="true"
557
+ :rules="fvGetAndReportPathRules('spec.repo')"
555
558
  />
556
559
  </div>
557
560
  <div class="col span-6">
@@ -103,7 +103,8 @@ export default {
103
103
  this.value.metadata['namespace'] = this.$store.getters['currentCluster'].id;
104
104
  this.value['spec'] = this.value.spec || {};
105
105
  this.value.spec['containerDefaultResourceLimit'] = this.value.spec.containerDefaultResourceLimit || {};
106
- if (!this.$store.getters['auth/principalId'].includes('local://')) {
106
+ // norman (and matching steve) resources treat annotations containing `cattle.io` as immutable, so only do this for the create world
107
+ if (this.isCreate && !this.$store.getters['auth/principalId'].includes('local://')) {
107
108
  this.value.metadata.annotations[CREATOR_PRINCIPAL_ID] = this.$store.getters['auth/principalId'];
108
109
  }
109
110
  },
@@ -102,7 +102,7 @@ export default {
102
102
  if (isEmpty(this.value?.annotations)) {
103
103
  this.value['annotations'] = { summary: '' };
104
104
  } else {
105
- this.value.annotations['summary'] = '';
105
+ this.value.annotations = { ...this.value.annotations, summary: '' };
106
106
  }
107
107
  } else {
108
108
  this.value.annotations['summary'] = '';
@@ -124,7 +124,7 @@ export default {
124
124
  if (isEmpty(this.value?.annotations)) {
125
125
  this.value['annotations'] = { message: '' };
126
126
  } else {
127
- this.value.annotations['message'] = '';
127
+ this.value.annotations = { ...this.value.annotations, message: '' };
128
128
  }
129
129
  } else {
130
130
  this.value.annotations['message'] = '';
@@ -146,7 +146,7 @@ export default {
146
146
  if (isEmpty(this.value?.annotations)) {
147
147
  this.value['annotations'] = { description: '' };
148
148
  } else {
149
- this.value.annotations['description'] = '';
149
+ this.value.annotations = { ...this.value.annotations, description: '' };
150
150
  }
151
151
  } else {
152
152
  this.value.annotations['description'] = '';
@@ -168,7 +168,7 @@ export default {
168
168
  if (isEmpty(this.value?.annotations)) {
169
169
  this.value['annotations'] = { runbook_url: '' };
170
170
  } else {
171
- this.value.annotations['runbook_url'] = '';
171
+ this.value.annotations = { ...this.value.annotations, runbook_url: '' };
172
172
  }
173
173
  } else {
174
174
  this.value.annotations['runbook_url'] = '';
@@ -213,7 +213,7 @@ export default {
213
213
  return undefined;
214
214
  },
215
215
  set(v) {
216
- this.value['for'] = [null, undefined].includes(v ? undefined : `${ v }s`);
216
+ this.value['for'] = [null, undefined].includes(v) ? undefined : `${ v }s`;
217
217
  }
218
218
  }
219
219
  },
@@ -113,7 +113,7 @@ export default {
113
113
  },
114
114
 
115
115
  updateGroupInterval(group, interval) {
116
- group['interval'] = [null, undefined].includes(interval ? undefined : `${ interval }s`);
116
+ group['interval'] = [null, undefined].includes(interval) ? undefined : `${ interval }s`;
117
117
  },
118
118
 
119
119
  getGroupInterval(interval) {
@@ -128,6 +128,32 @@ describe('component: Advanced', () => {
128
128
 
129
129
  expect(checkbox.value).toBe('true');
130
130
  });
131
+
132
+ it(`should update agentConfig when 'protect-kernel-defaults' exists`, async() => {
133
+ const value = clone(PROV_CLUSTER);
134
+
135
+ value.spec.rkeConfig.machineGlobalConfig['protect-kernel-defaults'] = false;
136
+ value.spec.rkeConfig.machineSelectorConfig = [{
137
+ config: { 'protect-kernel-defaults': true },
138
+ machineLabelSelector: {}
139
+ }];
140
+ value.agentConfig = value.spec.rkeConfig.machineSelectorConfig[0].config;
141
+
142
+ mountOptions.propsData.mode = _EDIT; // Use edit mode to allow interactions
143
+ mountOptions.propsData.value = value;
144
+
145
+ wrapper = mount(Advanced, mountOptions);
146
+
147
+ const checkboxLabel = wrapper
148
+ .find(`[data-testid="protect-kernel-defaults"]`)
149
+ .find('label');
150
+
151
+ checkboxLabel.trigger('click');
152
+ await wrapper.vm.$nextTick();
153
+
154
+ // Verify that agentConfig is updated
155
+ expect(value.spec.rkeConfig.machineSelectorConfig[0].config['protect-kernel-defaults']).toBe(false);
156
+ });
131
157
  });
132
158
 
133
159
  describe(`'kubelet-arg'`, () => {
@@ -1,7 +1,7 @@
1
1
  import { nextTick } from 'vue';
2
2
  /* eslint-disable jest/no-hooks */
3
3
  import { mount, Wrapper } from '@vue/test-utils';
4
- import DirectoryConfig from '@shell/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue';
4
+ import DirectoryConfig, { DATA_DIR_RADIO_OPTIONS, DEFAULT_SUBDIRS } from '@shell/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue';
5
5
  import { _EDIT, _CREATE } from '@shell/config/query-params';
6
6
  import { clone } from '@shell/utils/object';
7
7
 
@@ -15,7 +15,8 @@ describe('component: DirectoryConfig', () => {
15
15
  provisioning: '',
16
16
  k8sDistro: '',
17
17
  },
18
- mode: _CREATE,
18
+ k8sVersion: 'k3s',
19
+ mode: _CREATE,
19
20
  },
20
21
  mocks: {
21
22
  $store: {
@@ -34,203 +35,119 @@ describe('component: DirectoryConfig', () => {
34
35
  );
35
36
 
36
37
  const title = wrapper.find('h3');
37
- const checkbox = wrapper.find('[data-testid="rke2-directory-config-individual-config-checkbox"]');
38
+ const radioInput = wrapper.find('[data-testid="rke2-directory-config-radio-input"]');
38
39
  const commonInput = wrapper.find('[data-testid="rke2-directory-config-common-data-dir"]');
39
40
  const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
40
41
  const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
41
42
  const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
42
43
 
43
44
  expect(title.exists()).toBe(true);
44
- expect(checkbox.exists()).toBe(true);
45
- // for the default config, checkbox should be checked
46
- expect(wrapper.vm.isSettingCommonConfig).toBe(true);
47
- expect(commonInput.exists()).toBe(true);
45
+ expect(radioInput.exists()).toBe(true);
46
+
47
+ // for the default config, the default radio input should be "default"
48
+ expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.DEFAULT);
48
49
 
49
- // since we have all of the vars empty, then the individual inputs should not be there
50
+ // since we have all of the vars empty, then the inputs should not be there
51
+ expect(commonInput.exists()).toBe(false);
50
52
  expect(systemAgentInput.exists()).toBe(false);
51
53
  expect(provisioningInput.exists()).toBe(false);
52
54
  expect(k8sDistroInput.exists()).toBe(false);
53
55
  });
54
56
 
55
- it('should render the component with configuration being an empty object, without errors', () => {
57
+ it('updating common base directory should set the correct values on each data dir variable', async() => {
56
58
  const newMountOptions = clone(mountOptions);
57
59
 
58
- newMountOptions.propsData.value = {};
59
-
60
- wrapper = mount(
61
- DirectoryConfig,
62
- newMountOptions
63
- );
64
-
65
- const title = wrapper.find('h3');
66
- const checkbox = wrapper.find('[data-testid="rke2-directory-config-individual-config-checkbox"]');
67
- const commonInput = wrapper.find('[data-testid="rke2-directory-config-common-data-dir"]');
68
- const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
69
- const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
70
- const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
71
-
72
- expect(title.exists()).toBe(true);
73
- expect(checkbox.exists()).toBe(true);
74
- // for the default config, checkbox should be checked
75
- expect(wrapper.vm.isSettingCommonConfig).toBe(true);
76
- expect(commonInput.exists()).toBe(true);
77
-
78
- // since we have all of the vars empty, then the individual inputs should not be there
79
- expect(systemAgentInput.exists()).toBe(false);
80
- expect(provisioningInput.exists()).toBe(false);
81
- expect(k8sDistroInput.exists()).toBe(false);
82
- });
83
-
84
- it('updating common config path should set the correct values on each data dir variable', async() => {
85
60
  wrapper = mount(
86
61
  DirectoryConfig,
87
- mountOptions
62
+ {
63
+ ...newMountOptions,
64
+ // couldn't use setData, so this is the next best solution
65
+ data() {
66
+ return { dataConfigRadioValue: DATA_DIR_RADIO_OPTIONS.COMMON };
67
+ }
68
+ }
88
69
  );
89
70
 
90
71
  const inputPath = 'some-data-dir';
91
72
  const commonInput = wrapper.find('[data-testid="rke2-directory-config-common-data-dir"]');
92
73
 
74
+ // update base dir value
75
+ expect(commonInput.exists()).toBe(true);
93
76
  commonInput.setValue(inputPath);
94
77
  await nextTick();
95
78
 
96
- expect(wrapper.vm.value.systemAgent).toStrictEqual(inputPath);
97
- expect(wrapper.vm.value.provisioning).toStrictEqual(inputPath);
98
- expect(wrapper.vm.value.k8sDistro).toStrictEqual(inputPath);
79
+ expect(wrapper.vm.value.systemAgent).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.AGENT }`);
80
+ expect(wrapper.vm.value.provisioning).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.PROVISIONING }`);
81
+ expect(wrapper.vm.value.k8sDistro).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.K8S_DISTRO_K3S }`);
99
82
  });
100
83
 
101
84
  it('updating each individual data dir should set the correct values on each data dir variable', async() => {
102
- wrapper = mount(
103
- DirectoryConfig,
104
- mountOptions
105
- );
106
-
107
- const inputPath = 'some-data-dir';
108
- const checkbox = wrapper.find('[data-testid="rke2-directory-config-individual-config-checkbox"]');
109
-
110
- await checkbox.find('label').trigger('click');
111
- await nextTick();
112
- await nextTick();
113
-
114
- expect(wrapper.vm.isSettingCommonConfig).toBe(false);
115
-
116
- const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
117
- const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
118
- const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
119
-
120
- systemAgentInput.setValue(inputPath);
121
- provisioningInput.setValue(inputPath);
122
- k8sDistroInput.setValue(inputPath);
123
- await nextTick();
124
-
125
- expect(wrapper.vm.value.systemAgent).toStrictEqual(inputPath);
126
- expect(wrapper.vm.value.provisioning).toStrictEqual(inputPath);
127
- expect(wrapper.vm.value.k8sDistro).toStrictEqual(inputPath);
128
- });
129
-
130
- it('checkbox should be checked if all data dir values are the same (with all data dir values filled)', () => {
131
85
  const newMountOptions = clone(mountOptions);
132
- const inputPath = 'some-data-dir';
133
-
134
- newMountOptions.propsData.value.systemAgent = inputPath;
135
- newMountOptions.propsData.value.provisioning = inputPath;
136
- newMountOptions.propsData.value.k8sDistro = inputPath;
137
86
 
138
87
  wrapper = mount(
139
88
  DirectoryConfig,
140
- newMountOptions
89
+ {
90
+ ...newMountOptions,
91
+ // couldn't use setData, so this is the next best solution
92
+ data() {
93
+ return { dataConfigRadioValue: DATA_DIR_RADIO_OPTIONS.CUSTOM };
94
+ }
95
+ }
141
96
  );
97
+ const inputPath = 'some-data-dir';
98
+ const agentValue = `${ inputPath }/${ DEFAULT_SUBDIRS.AGENT }`;
99
+ const provisioningValue = `${ inputPath }/${ DEFAULT_SUBDIRS.PROVISIONING }`;
100
+ const k8sDistroValue = `${ inputPath }/${ DEFAULT_SUBDIRS.K8S_DISTRO_RKE2 }`;
142
101
 
143
- const checkbox = wrapper.find('[data-testid="rke2-directory-config-individual-config-checkbox"]');
144
-
145
- expect(checkbox.exists()).toBe(true);
146
- expect(wrapper.vm.isSettingCommonConfig).toBe(true);
147
-
148
- const commonInput = wrapper.find('[data-testid="rke2-directory-config-common-data-dir"]');
149
102
  const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
150
103
  const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
151
104
  const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
152
105
 
153
- expect(commonInput.exists()).toBe(true);
154
- expect(systemAgentInput.exists()).toBe(false);
155
- expect(provisioningInput.exists()).toBe(false);
156
- expect(k8sDistroInput.exists()).toBe(false);
106
+ systemAgentInput.setValue(agentValue);
107
+ provisioningInput.setValue(provisioningValue);
108
+ k8sDistroInput.setValue(k8sDistroValue);
109
+ await nextTick();
157
110
 
158
- expect(wrapper.vm.value.systemAgent).toStrictEqual(inputPath);
159
- expect(wrapper.vm.value.provisioning).toStrictEqual(inputPath);
160
- expect(wrapper.vm.value.k8sDistro).toStrictEqual(inputPath);
111
+ expect(wrapper.vm.value.systemAgent).toStrictEqual(agentValue);
112
+ expect(wrapper.vm.value.provisioning).toStrictEqual(provisioningValue);
113
+ expect(wrapper.vm.value.k8sDistro).toStrictEqual(k8sDistroValue);
161
114
  });
162
115
 
163
- it('checkbox should NOT be checked if some data dir values are the different (with all data dir values filled)', () => {
116
+ it('should render the component with configuration being an empty object, without errors and radio be of value DATA_DIR_RADIO_OPTIONS.DEFAULT (edit scenario)', () => {
164
117
  const newMountOptions = clone(mountOptions);
165
- const inputPath1 = 'some-data-dir1';
166
- const inputPath2 = 'some-data-dir2';
167
- const inputPath3 = 'some-data-dir3';
168
118
 
169
- newMountOptions.propsData.value.systemAgent = inputPath1;
170
- newMountOptions.propsData.value.provisioning = inputPath2;
171
- newMountOptions.propsData.value.k8sDistro = inputPath3;
119
+ newMountOptions.propsData.value = {};
120
+ newMountOptions.propsData.mode = _EDIT;
172
121
 
173
122
  wrapper = mount(
174
123
  DirectoryConfig,
175
124
  newMountOptions
176
125
  );
177
126
 
178
- expect(wrapper.vm.isSettingCommonConfig).toBe(false);
179
-
180
- const commonInput = wrapper.find('[data-testid="rke2-directory-config-common-data-dir"]');
127
+ const title = wrapper.find('h3');
128
+ const radioInput = wrapper.find('[data-testid="rke2-directory-config-radio-input"]');
181
129
  const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
182
130
  const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
183
131
  const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
184
132
 
185
- expect(commonInput.exists()).toBe(false);
186
- expect(systemAgentInput.exists()).toBe(true);
187
- expect(provisioningInput.exists()).toBe(true);
188
- expect(k8sDistroInput.exists()).toBe(true);
189
-
190
- expect(wrapper.vm.value.systemAgent).toStrictEqual(inputPath1);
191
- expect(wrapper.vm.value.provisioning).toStrictEqual(inputPath2);
192
- expect(wrapper.vm.value.k8sDistro).toStrictEqual(inputPath3);
193
- });
194
-
195
- it('on a mode different than _CREATE all visible inputs should be disabled (with common config)', () => {
196
- const newMountOptions = clone(mountOptions);
197
- const inputPath = 'some-data-dir';
198
-
199
- newMountOptions.propsData.value.systemAgent = inputPath;
200
- newMountOptions.propsData.value.provisioning = inputPath;
201
- newMountOptions.propsData.value.k8sDistro = inputPath;
202
- newMountOptions.propsData.mode = _EDIT;
203
-
204
- wrapper = mount(
205
- DirectoryConfig,
206
- newMountOptions
207
- );
133
+ expect(title.exists()).toBe(true);
134
+ expect(radioInput.exists()).toBe(true);
208
135
 
209
- const checkbox = wrapper.find('[data-testid="rke2-directory-config-individual-config-checkbox"]');
210
- const commonInput = wrapper.find('[data-testid="rke2-directory-config-common-data-dir"]');
211
- const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
212
- const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
213
- const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
136
+ expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.DEFAULT);
214
137
 
215
- expect(checkbox.exists()).toBe(true);
216
- expect(commonInput.exists()).toBe(true);
138
+ // since we have all of the vars empty, then the inputs should not be there
217
139
  expect(systemAgentInput.exists()).toBe(false);
218
140
  expect(provisioningInput.exists()).toBe(false);
219
141
  expect(k8sDistroInput.exists()).toBe(false);
220
-
221
- expect(checkbox.find('label').classes('disabled')).toBe(true);
222
- expect(commonInput.attributes('disabled')).toBe('');
223
142
  });
224
143
 
225
- it('on a mode different than _CREATE all visible inputs should be disabled (with different values)', () => {
144
+ it('radio input should be set to DATA_DIR_RADIO_OPTIONS.CUSTOM with all data dir values existing and different (edit scenario)', async() => {
226
145
  const newMountOptions = clone(mountOptions);
227
- const inputPath1 = 'some-data-dir1';
228
- const inputPath2 = 'some-data-dir2';
229
- const inputPath3 = 'some-data-dir3';
146
+ const inputPath = 'some-data-dir';
230
147
 
231
- newMountOptions.propsData.value.systemAgent = inputPath1;
232
- newMountOptions.propsData.value.provisioning = inputPath2;
233
- newMountOptions.propsData.value.k8sDistro = inputPath3;
148
+ newMountOptions.propsData.value.systemAgent = `${ inputPath }/${ DEFAULT_SUBDIRS.AGENT }`;
149
+ newMountOptions.propsData.value.provisioning = `${ inputPath }/${ DEFAULT_SUBDIRS.PROVISIONING }`;
150
+ newMountOptions.propsData.value.k8sDistro = `${ inputPath }/${ DEFAULT_SUBDIRS.K8S_DISTRO_K3S }`;
234
151
  newMountOptions.propsData.mode = _EDIT;
235
152
 
236
153
  wrapper = mount(
@@ -238,21 +155,18 @@ describe('component: DirectoryConfig', () => {
238
155
  newMountOptions
239
156
  );
240
157
 
241
- const checkbox = wrapper.find('[data-testid="rke2-directory-config-individual-config-checkbox"]');
242
- const commonInput = wrapper.find('[data-testid="rke2-directory-config-common-data-dir"]');
158
+ expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.CUSTOM);
159
+
243
160
  const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
244
161
  const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
245
162
  const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
246
163
 
247
- expect(checkbox.exists()).toBe(true);
248
- expect(commonInput.exists()).toBe(false);
249
- expect(systemAgentInput.exists()).toBe(true);
250
- expect(provisioningInput.exists()).toBe(true);
251
- expect(k8sDistroInput.exists()).toBe(true);
164
+ expect(systemAgentInput.isVisible()).toBe(true);
165
+ expect(provisioningInput.isVisible()).toBe(true);
166
+ expect(k8sDistroInput.isVisible()).toBe(true);
252
167
 
253
- expect(checkbox.find('label').classes('disabled')).toBe(true);
254
- expect(systemAgentInput.attributes('disabled')).toBe('');
255
- expect(provisioningInput.attributes('disabled')).toBe('');
256
- expect(k8sDistroInput.attributes('disabled')).toBe('');
168
+ expect(wrapper.vm.value.systemAgent).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.AGENT }`);
169
+ expect(wrapper.vm.value.provisioning).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.PROVISIONING }`);
170
+ expect(wrapper.vm.value.k8sDistro).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.K8S_DISTRO_K3S }`);
257
171
  });
258
172
  });
@@ -62,6 +62,7 @@ import Upgrade from '@shell/edit/provisioning.cattle.io.cluster/tabs/upgrade';
62
62
  import Registries from '@shell/edit/provisioning.cattle.io.cluster/tabs/registries';
63
63
  import AddOnConfig from '@shell/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig';
64
64
  import Advanced from '@shell/edit/provisioning.cattle.io.cluster/tabs/Advanced';
65
+ import { DEFAULT_COMMON_BASE_PATH, DEFAULT_SUBDIRS } from '@shell/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig';
65
66
  import ClusterAppearance from '@shell/components/form/ClusterAppearance';
66
67
  import AddOnAdditionalManifest from '@shell/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest';
67
68
  import VsphereUtils from '@shell/utils/v-sphere';
@@ -1483,7 +1484,8 @@ export default {
1483
1484
  set(this.chartValues, `${ HARVESTER_CLOUD_PROVIDER }.global.cattle.clusterName`, this.value.metadata.name);
1484
1485
  }
1485
1486
 
1486
- const distroRoot = this.value?.spec?.rkeConfig?.dataDirectories?.k8sDistro?.length ? this.value?.spec?.rkeConfig?.dataDirectories?.k8sDistro : '/var/lib/rancher/rke2';
1487
+ const distroSubdir = this.value?.spec?.kubernetesVersion?.includes('k3s') ? DEFAULT_SUBDIRS.K8S_DISTRO_K3S : DEFAULT_SUBDIRS.K8S_DISTRO_RKE2;
1488
+ const distroRoot = this.value?.spec?.rkeConfig?.dataDirectories?.k8sDistro?.length ? this.value?.spec?.rkeConfig?.dataDirectories?.k8sDistro : `${ DEFAULT_COMMON_BASE_PATH }/${ distroSubdir }`;
1487
1489
 
1488
1490
  set(this.chartValues, `${ HARVESTER_CLOUD_PROVIDER }.cloudConfigPath`, `${ distroRoot }/etc/config-files/cloud-provider-config`);
1489
1491
  }
@@ -80,10 +80,14 @@ export default {
80
80
  return i !== 0 || !this.agentConfig;
81
81
  },
82
82
  showEmptyKubeletArg(config) {
83
- return !this.serverArg?.['kubelet-arg']?.length && !config?.['kubelet-arg']?.length;
83
+ return !this.serverArgs?.['kubelet-arg']?.length && !config?.['kubelet-arg']?.length;
84
84
  },
85
85
  onInputProtectKernelDefaults(value) {
86
- this.agentConfig ? this.agentConfig = value : this.serverConfig['protect-kernel-defaults'] = value;
86
+ if (this.agentConfig && this.agentConfig['protect-kernel-defaults'] !== undefined ) {
87
+ this.agentConfig['protect-kernel-defaults'] = value;
88
+ } else {
89
+ this.serverConfig['protect-kernel-defaults'] = value;
90
+ }
87
91
  }
88
92
  }
89
93
  };
@@ -100,6 +104,7 @@ export default {
100
104
  <template v-if="haveArgInfo">
101
105
  <DirectoryConfig
102
106
  v-model:value="value.spec.rkeConfig.dataDirectories"
107
+ :k8s-version="value.spec.kubernetesVersion"
103
108
  :mode="mode"
104
109
  />
105
110
  <h3>{{ t('cluster.advanced.argInfo.title') }}</h3>