@rancher/shell 0.3.15 → 0.3.17

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 (155) hide show
  1. package/assets/images/wechat-qr-code.jpg +0 -0
  2. package/assets/translations/en-us.yaml +70 -15
  3. package/assets/translations/zh-hans.yaml +155 -33
  4. package/chart/__tests__/S3.test.ts +50 -0
  5. package/chart/rancher-backup/S3.vue +21 -0
  6. package/chart/rancher-backup/index.vue +4 -0
  7. package/cloud-credential/generic.vue +1 -1
  8. package/components/BannerGraphic.vue +1 -0
  9. package/components/CommunityLinks.vue +1 -0
  10. package/components/CruResource.vue +1 -1
  11. package/components/EmberPage.vue +1 -0
  12. package/components/FileDiff.vue +92 -85
  13. package/components/GrafanaDashboard.vue +7 -1
  14. package/components/ResourceDetail/index.vue +4 -12
  15. package/components/ResourceList/index.vue +1 -1
  16. package/components/ResourceTable.vue +50 -2
  17. package/components/SimpleBox.vue +1 -0
  18. package/components/SortableTable/index.vue +5 -1
  19. package/components/YamlEditor.vue +1 -0
  20. package/components/auth/RoleDetailEdit.vue +1 -0
  21. package/components/form/GitPicker.vue +1 -1
  22. package/components/form/NameNsDescription.vue +28 -12
  23. package/components/form/NodeAffinity.vue +2 -2
  24. package/components/form/PodAffinity.vue +8 -3
  25. package/components/form/ResourceTabs/index.vue +8 -2
  26. package/components/form/Select.vue +16 -0
  27. package/components/form/__tests__/NodeAffinity.test.ts +38 -0
  28. package/components/form/__tests__/PodAffinity.test.ts +46 -0
  29. package/components/formatter/ClusterLink.vue +8 -4
  30. package/components/formatter/ImageName.vue +23 -0
  31. package/components/formatter/PodImages.vue +7 -1
  32. package/components/formatter/__tests__/ClusterLink.test.ts +101 -0
  33. package/components/nav/Header.vue +2 -2
  34. package/config/__test__/home-links.test.ts +62 -0
  35. package/config/home-links.js +15 -3
  36. package/config/labels-annotations.js +5 -1
  37. package/config/product/auth.js +1 -1
  38. package/config/router.js +0 -9
  39. package/config/settings.ts +4 -0
  40. package/config/table-headers.js +6 -5
  41. package/config/uiplugins.js +50 -5
  42. package/core/plugin-helpers.js +20 -12
  43. package/core/plugin.ts +9 -0
  44. package/core/plugins.js +1 -1
  45. package/core/types-provisioning.ts +253 -0
  46. package/core/types.ts +17 -3
  47. package/detail/autoscaling.horizontalpodautoscaler/index.vue +50 -1
  48. package/detail/catalog.cattle.io.clusterrepo.vue +8 -1
  49. package/detail/node.vue +6 -6
  50. package/detail/pod.vue +2 -6
  51. package/detail/provisioning.cattle.io.cluster.vue +46 -7
  52. package/detail/workload/index.vue +9 -9
  53. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +62 -0
  54. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +56 -0
  55. package/edit/auth/github.vue +1 -0
  56. package/edit/autoscaling.horizontalpodautoscaler/hpa-scaling-rule.vue +130 -0
  57. package/edit/autoscaling.horizontalpodautoscaler/index.vue +79 -0
  58. package/edit/fleet.cattle.io.gitrepo.vue +18 -1
  59. package/edit/monitoring.coreos.com.prometheusrule/index.vue +8 -3
  60. package/edit/namespace.vue +9 -1
  61. package/edit/networking.k8s.io.ingress/RulePath.vue +0 -2
  62. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +1 -30
  63. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +79 -1
  64. package/edit/provisioning.cattle.io.cluster/index.vue +52 -0
  65. package/edit/provisioning.cattle.io.cluster/rke2.vue +330 -150
  66. package/edit/ui.cattle.io.navlink.vue +2 -1
  67. package/initialize/App.js +3 -13
  68. package/initialize/layouts.ts +26 -0
  69. package/list/provisioning.cattle.io.cluster.vue +8 -1
  70. package/middleware/authenticated.js +93 -5
  71. package/mixins/brand.js +39 -3
  72. package/mixins/child-hook.js +2 -2
  73. package/mixins/create-edit-view/impl.js +2 -2
  74. package/models/fleet.cattle.io.gitrepo.js +1 -0
  75. package/models/provisioning.cattle.io.cluster.js +9 -1
  76. package/package.json +3 -3
  77. package/pages/about.vue +8 -2
  78. package/pages/auth/login.vue +10 -0
  79. package/pages/auth/logout.vue +11 -3
  80. package/pages/auth/setup.vue +4 -0
  81. package/pages/c/_cluster/apps/charts/index.vue +5 -2
  82. package/pages/c/_cluster/apps/charts/install.vue +5 -0
  83. package/pages/c/_cluster/auth/roles/index.vue +1 -1
  84. package/pages/c/_cluster/explorer/index.vue +1 -10
  85. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +177 -0
  86. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +19 -3
  87. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +90 -21
  88. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +107 -37
  89. package/pages/c/_cluster/uiplugins/index.vue +155 -44
  90. package/pages/docs/_doc.vue +9 -3
  91. package/pages/home.vue +10 -5
  92. package/pages/support/index.vue +10 -4
  93. package/pkg/auto-import.js +1 -1
  94. package/plugins/clean-tooltip-directive.js +1 -1
  95. package/plugins/dashboard-store/resource-class.js +35 -2
  96. package/plugins/plugin.js +9 -1
  97. package/plugins/steve/actions.js +22 -0
  98. package/rancher-components/BadgeState/BadgeState.vue +5 -1
  99. package/rancher-components/Banner/Banner.test.ts +51 -1
  100. package/rancher-components/Banner/Banner.vue +134 -53
  101. package/rancher-components/Card/Card.vue +24 -7
  102. package/rancher-components/Form/Checkbox/Checkbox.test.ts +20 -29
  103. package/rancher-components/Form/Checkbox/Checkbox.vue +45 -20
  104. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +2 -8
  105. package/rancher-components/Form/LabeledInput/LabeledInput.vue +22 -10
  106. package/rancher-components/Form/Radio/RadioButton.vue +30 -13
  107. package/rancher-components/Form/Radio/RadioGroup.vue +26 -7
  108. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +7 -6
  109. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +25 -38
  110. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +23 -11
  111. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +19 -5
  112. package/rancher-components/StringList/StringList.test.ts +453 -49
  113. package/rancher-components/StringList/StringList.vue +92 -58
  114. package/scripts/extension/publish +2 -2
  115. package/scripts/typegen.sh +1 -0
  116. package/server/server-middleware.js +4 -12
  117. package/store/index.js +13 -0
  118. package/store/prefs.js +0 -3
  119. package/store/type-map.js +17 -29
  120. package/types/shell/index.d.ts +243 -90
  121. package/utils/kube.js +9 -0
  122. package/utils/object.js +27 -0
  123. package/utils/settings.ts +2 -2
  124. package/vue.config.js +3 -2
  125. package/pages/safeMode.vue +0 -17
  126. package/rancher-components/components/BadgeState/BadgeState.spec.ts +0 -12
  127. package/rancher-components/components/BadgeState/BadgeState.vue +0 -111
  128. package/rancher-components/components/BadgeState/index.ts +0 -1
  129. package/rancher-components/components/Banner/Banner.test.ts +0 -63
  130. package/rancher-components/components/Banner/Banner.vue +0 -244
  131. package/rancher-components/components/Banner/index.ts +0 -1
  132. package/rancher-components/components/Card/Card.vue +0 -167
  133. package/rancher-components/components/Card/index.ts +0 -1
  134. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +0 -68
  135. package/rancher-components/components/Form/Checkbox/Checkbox.vue +0 -420
  136. package/rancher-components/components/Form/Checkbox/index.ts +0 -1
  137. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +0 -23
  138. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +0 -355
  139. package/rancher-components/components/Form/LabeledInput/index.ts +0 -1
  140. package/rancher-components/components/Form/Radio/RadioButton.vue +0 -287
  141. package/rancher-components/components/Form/Radio/RadioGroup.vue +0 -254
  142. package/rancher-components/components/Form/Radio/index.ts +0 -2
  143. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +0 -170
  144. package/rancher-components/components/Form/TextArea/index.ts +0 -1
  145. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +0 -94
  146. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +0 -149
  147. package/rancher-components/components/Form/ToggleSwitch/index.ts +0 -1
  148. package/rancher-components/components/Form/index.ts +0 -5
  149. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -151
  150. package/rancher-components/components/LabeledTooltip/index.ts +0 -1
  151. package/rancher-components/components/StringList/StringList.test.ts +0 -484
  152. package/rancher-components/components/StringList/StringList.vue +0 -611
  153. package/rancher-components/components/StringList/index.ts +0 -1
  154. /package/rancher-components/{components/Card → Card}/Card.test.ts +0 -0
  155. /package/rancher-components/{components/Form → Form}/Radio/RadioButton.test.ts +0 -0
@@ -81,6 +81,24 @@ export default {
81
81
 
82
82
  async fetch() {
83
83
  await this.value.waitForProvisioner();
84
+
85
+ const extClass = this.$plugin.getDynamic('provisioner', this.value.machineProvider);
86
+
87
+ if (extClass) {
88
+ this.extProvider = new extClass({
89
+ dispatch: this.$store.dispatch,
90
+ getters: this.$store.getters,
91
+ axios: this.$store.$axios,
92
+ $plugin: this.$store.app.$plugin,
93
+ $t: this.t
94
+ });
95
+ this.extDetailTabs = {
96
+ ...this.extDetailTabs,
97
+ ...this.extProvider.detailTabs
98
+ };
99
+ this.extCustomParams = { provider: this.value.machineProvider };
100
+ }
101
+
84
102
  const fetchOne = {};
85
103
 
86
104
  if ( this.$store.getters['management/canList'](CAPI.MACHINE_DEPLOYMENT) ) {
@@ -206,6 +224,18 @@ export default {
206
224
  logSocket: null,
207
225
  logs: [],
208
226
 
227
+ extProvider: null,
228
+ extCustomParams: null,
229
+ extDetailTabs: {
230
+ machines: true, // in this component
231
+ logs: true, // in this component
232
+ registration: true, // in this component
233
+ snapshots: true, // in this component
234
+ related: true, // in ResourceTabs
235
+ events: true, // in ResourceTabs
236
+ conditions: true, // in ResourceTabs
237
+ },
238
+
209
239
  showWindowsWarning: false
210
240
  };
211
241
  },
@@ -336,7 +366,9 @@ export default {
336
366
  },
337
367
 
338
368
  showMachines() {
339
- return this.haveMachines && (this.value.isRke2 || !!this.machines.length);
369
+ const showMachines = this.haveMachines && (this.value.isRke2 || !!this.machines.length);
370
+
371
+ return showMachines && this.extDetailTabs.machines;
340
372
  },
341
373
 
342
374
  showNodes() {
@@ -345,9 +377,9 @@ export default {
345
377
 
346
378
  showSnapshots() {
347
379
  if (this.value.isRke1) {
348
- return this.$store.getters['rancher/canList'](NORMAN.ETCD_BACKUP);
380
+ return this.$store.getters['rancher/canList'](NORMAN.ETCD_BACKUP) && this.extDetailTabs.snapshots;
349
381
  } else if (this.value.isRke2) {
350
- return this.$store.getters['management/canList'](SNAPSHOT);
382
+ return this.$store.getters['management/canList'](SNAPSHOT) && this.extDetailTabs.snapshots;
351
383
  }
352
384
 
353
385
  return false;
@@ -476,15 +508,15 @@ export default {
476
508
  }
477
509
 
478
510
  if ( this.value.isImported ) {
479
- return !this.value.mgmt?.isReady;
511
+ return !this.value.mgmt?.isReady && this.extDetailTabs.registration;
480
512
  }
481
513
 
482
514
  if ( this.value.isCustom ) {
483
- return true;
515
+ return this.extDetailTabs.registration;
484
516
  }
485
517
 
486
518
  if ( this.value.isHostedKubernetesProvider && !this.isClusterReady ) {
487
- return true;
519
+ return this.extDetailTabs.registration;
488
520
  }
489
521
 
490
522
  return false;
@@ -495,7 +527,9 @@ export default {
495
527
  },
496
528
 
497
529
  showLog() {
498
- return this.value.mgmt?.hasLink('log');
530
+ const showLog = this.value.mgmt?.hasLink('log');
531
+
532
+ return showLog && this.extDetailTabs.logs;
499
533
  },
500
534
 
501
535
  dateTimeFormatStr() {
@@ -686,6 +720,10 @@ export default {
686
720
  v-model="value"
687
721
  :default-tab="defaultTab"
688
722
  :need-related="hasLocalAccess"
723
+ :extension-params="extCustomParams"
724
+ :needRelated="extDetailTabs.related"
725
+ :needEvents="extDetailTabs.events"
726
+ :needConditions="extDetailTabs.conditions"
689
727
  >
690
728
  <Tab
691
729
  v-if="showMachines"
@@ -773,6 +811,7 @@ export default {
773
811
  </template>
774
812
  </ResourceTable>
775
813
  </Tab>
814
+
776
815
  <Tab
777
816
  v-else-if="showNodes"
778
817
  name="node-pools"
@@ -4,7 +4,7 @@ import { NAMESPACE as NAMESPACE_COL } from '@shell/config/table-headers';
4
4
  import {
5
5
  POD, WORKLOAD_TYPES, SCALABLE_WORKLOAD_TYPES, SERVICE, INGRESS, NODE
6
6
  } from '@shell/config/types';
7
- import SortableTable from '@shell/components/SortableTable';
7
+ import ResourceTable from '@shell/components/ResourceTable';
8
8
  import Tab from '@shell/components/Tabbed/Tab';
9
9
  import Loading from '@shell/components/Loading';
10
10
  import ResourceTabs from '@shell/components/form/ResourceTabs';
@@ -46,7 +46,7 @@ export default {
46
46
  Loading,
47
47
  ResourceTabs,
48
48
  CountGauge,
49
- SortableTable,
49
+ ResourceTable,
50
50
  PlusMinus
51
51
  },
52
52
 
@@ -380,12 +380,12 @@ export default {
380
380
  :label="t('tableHeaders.jobs')"
381
381
  :weight="4"
382
382
  >
383
- <SortableTable
383
+ <ResourceTable
384
384
  :rows="value.jobs"
385
385
  :headers="jobHeaders"
386
386
  key-field="id"
387
387
  :schema="jobSchema"
388
- :show-groups="false"
388
+ :groupable="false"
389
389
  :search="false"
390
390
  />
391
391
  </Tab>
@@ -395,7 +395,7 @@ export default {
395
395
  :label="t('tableHeaders.pods')"
396
396
  :weight="4"
397
397
  >
398
- <SortableTable
398
+ <ResourceTable
399
399
  v-if="value.pods"
400
400
  :rows="value.pods"
401
401
  :headers="podHeaders"
@@ -458,13 +458,13 @@ export default {
458
458
  >
459
459
  {{ t('workload.detail.serviceListCaption') }}
460
460
  </p>
461
- <SortableTable
461
+ <ResourceTable
462
462
  v-if="serviceSchema && matchingServices.length > 0"
463
463
  :rows="matchingServices"
464
464
  :headers="serviceHeaders"
465
465
  key-field="id"
466
466
  :schema="serviceSchema"
467
- :show-groups="false"
467
+ :groupable="false"
468
468
  :search="false"
469
469
  :table-actions="false"
470
470
  />
@@ -499,13 +499,13 @@ export default {
499
499
  >
500
500
  {{ t('workload.detail.ingressListCaption') }}
501
501
  </p>
502
- <SortableTable
502
+ <ResourceTable
503
503
  v-if="ingressSchema && matchingIngresses.length > 0"
504
504
  :rows="matchingIngresses"
505
505
  :headers="ingressHeaders"
506
506
  key-field="id"
507
507
  :schema="ingressSchema"
508
- :show-groups="false"
508
+ :groupable="false"
509
509
  :search="false"
510
510
  :table-actions="false"
511
511
  />
@@ -0,0 +1,62 @@
1
+ import { mount, createLocalVue } from '@vue/test-utils';
2
+ import GitRepo from '@shell/edit/fleet.cattle.io.gitrepo.vue';
3
+ import Vuex from 'vuex';
4
+
5
+ const localVue = createLocalVue();
6
+
7
+ localVue.use(Vuex);
8
+
9
+ describe('view: fleet.cattle.io.gitrepo should', () => {
10
+ const mockStore = {
11
+ getters: {
12
+ 'i18n/t': (text: string) => text,
13
+ t: (text: string) => text,
14
+ currentStore: () => 'current_store',
15
+ 'current_store/schemaFor': jest.fn(),
16
+ 'current_store/all': jest.fn(),
17
+ workspace: jest.fn(),
18
+ }
19
+ };
20
+ const mocks = {
21
+ $store: mockStore,
22
+ $fetchState: { pending: false },
23
+ $route: {
24
+ query: { AS: '' },
25
+ name: {
26
+ endsWith: () => {
27
+ return false;
28
+ }
29
+ }
30
+ }
31
+ };
32
+ const values = {
33
+ metadata: { namespace: 'test' }, spec: { template: {}, correctDrift: { enabled: false } }, targetInfo: { mode: 'all' },
34
+ };
35
+ const wrapper = mount(GitRepo, {
36
+ localVue,
37
+ propsData: { value: values },
38
+ mocks
39
+ });
40
+
41
+ it('should have self-healing checkbox and banner', () => {
42
+ const correctDriftCheckbox = wrapper.find('[data-testid="GitRepo-correctDrift-checkbox"]');
43
+ const correctDriftBanner = wrapper.find('[data-testid="GitRepo-correctDrift-banner"]');
44
+
45
+ expect(correctDriftCheckbox.exists()).toBeTruthy();
46
+ expect(correctDriftBanner.exists()).toBeTruthy();
47
+ expect(correctDriftCheckbox.props().value).toBeFalsy();
48
+ });
49
+
50
+ it('should enable drift if self-healing is checked', async() => {
51
+ const correctDriftCheckbox = wrapper.find('[data-testid="GitRepo-correctDrift-checkbox"]');
52
+ const correctDriftContainer = wrapper.find('[data-testid="GitRepo-correctDrift-checkbox"] .checkbox-container');
53
+
54
+ expect(correctDriftContainer.exists()).toBeTruthy();
55
+
56
+ await correctDriftContainer.trigger('click');
57
+
58
+ expect(correctDriftCheckbox.emitted('input')).toHaveLength(1);
59
+ expect(correctDriftCheckbox.emitted('input')![0][0]).toBe(true);
60
+ expect(correctDriftCheckbox.props().value).toBeTruthy();
61
+ });
62
+ });
@@ -0,0 +1,56 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import FormValidation from '@shell/mixins/form-validation';
3
+ import Monitoring from '@shell/edit/monitoring.coreos.com.prometheusrule/index.vue';
4
+ import { _EDIT } from '@shell/config/query-params';
5
+ import { cleanHtmlDirective } from '@shell/plugins/clean-html-directive';
6
+
7
+ describe('edit: management.cattle.io.setting should', () => {
8
+ const MOCKED_ERRORS = ['error1', 'error2', 'error3', 'error4', 'error5'];
9
+ const ERROR_BANNER_SELECTOR = '[data-testid="banner-close"]';
10
+ const requiredSetup = () => ({
11
+ // Remove all these mocks after migration to Vue 2.7/3 due mixin logic
12
+ mocks: {
13
+ $store: {
14
+ getters: {
15
+ currentStore: () => 'current_store',
16
+ 'current_store/schemaFor': jest.fn(),
17
+ 'current_store/all': jest.fn(),
18
+ 'i18n/t': jest.fn(),
19
+ 'i18n/exists': jest.fn(),
20
+ namespaces: () => ({})
21
+ }
22
+ },
23
+ $route: { query: { AS: '' }, name: '' },
24
+ $router: { applyQuery: jest.fn() }
25
+ }
26
+ });
27
+
28
+ it('should close error banners', async() => {
29
+ jest.spyOn(FormValidation.computed, 'fvUnreportedValidationErrors').mockReturnValue(MOCKED_ERRORS);
30
+
31
+ const wrapper = mount(Monitoring, {
32
+ propsData: {
33
+ canYaml: false,
34
+ mode: _EDIT,
35
+ resource: {},
36
+ value: { value: 'anything' },
37
+ name: ''
38
+ },
39
+ directives: { cleanHtmlDirective },
40
+ ...requiredSetup()
41
+ });
42
+
43
+ const errorBanners = wrapper.findAll(ERROR_BANNER_SELECTOR);
44
+
45
+ // Assert that all the error banners are rendered
46
+ expect(errorBanners).toHaveLength(MOCKED_ERRORS.length);
47
+
48
+ for (let i = 0; i < MOCKED_ERRORS.length; i++) {
49
+ await errorBanners.at(0).trigger('click');
50
+ const resultErrorBanners = wrapper.findAll(ERROR_BANNER_SELECTOR);
51
+
52
+ // Assert that an error banner is closed until the last one
53
+ expect(resultErrorBanners).toHaveLength(MOCKED_ERRORS.length - 1 - i);
54
+ }
55
+ });
56
+ });
@@ -163,6 +163,7 @@ export default {
163
163
  <RadioGroup
164
164
  v-model="targetType"
165
165
  name="targetType"
166
+ data-testid="authConfig-gitHub"
166
167
  :options="['public','private']"
167
168
  :mode="mode"
168
169
  :labels="[ t(`authConfig.${NAME}.target.public`), t(`authConfig.${NAME}.target.private`)]"
@@ -0,0 +1,130 @@
1
+ <script>
2
+
3
+ import { _EDIT } from '@shell/config/query-params';
4
+ import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
5
+ import { LabeledInput } from '@components/Form/LabeledInput';
6
+ import ArrayListGrouped from '@shell/components/form/ArrayListGrouped.vue';
7
+
8
+ export default {
9
+ components: {
10
+ ArrayListGrouped,
11
+ LabeledInput,
12
+ LabeledSelect
13
+ },
14
+ props: {
15
+ value: {
16
+ type: Object,
17
+ default: () => {
18
+ }
19
+ },
20
+ mode: {
21
+ type: String,
22
+ default: _EDIT
23
+ },
24
+ type: {
25
+ type: String,
26
+ default: 'scaleUp'
27
+ },
28
+ },
29
+ data() {
30
+ if (!this.value.spec.behavior[this.type].policies) {
31
+ this.$set(this.value.spec.behavior[this.type], 'policies', []);
32
+ }
33
+ if (!this.value.spec.behavior[this.type].selectPolicy) {
34
+ this.$set(this.value.spec.behavior[this.type], 'selectPolicy', 'Max');
35
+ }
36
+ if (this.value.spec.behavior[this.type].stabilizationWindowSeconds === null || typeof this.value.spec.behavior[this.type].stabilizationWindowSeconds === 'undefined') {
37
+ if (this.type === 'scaleUp') {
38
+ this.$set(this.value.spec.behavior[this.type], 'stabilizationWindowSeconds', 0);
39
+ }
40
+ if (this.type === 'scaleDown') {
41
+ this.$set(this.value.spec.behavior[this.type], 'stabilizationWindowSeconds', 300);
42
+ }
43
+ }
44
+
45
+ return { selectPolicyOptions: ['Max', 'Min', 'Disabled'], policyTypeOptions: ['Pods', 'Percent'] };
46
+ },
47
+ };
48
+ </script>
49
+
50
+ <template>
51
+ <div>
52
+ <div class="row mb-20">
53
+ <div class="col span-12">
54
+ <ArrayListGrouped
55
+ v-model="value.spec.behavior[type].policies"
56
+ :add-label="t('hpa.scalingRule.addPolicy')"
57
+ :default-add-value="{}"
58
+ :mode="mode"
59
+ >
60
+ <template #default="props">
61
+ <div class="row">
62
+ <div class="col span-4">
63
+ <LabeledSelect
64
+ v-model="props.row.value.type"
65
+ :mode="mode"
66
+ :options="policyTypeOptions"
67
+ :multiple="false"
68
+ :taggable="true"
69
+ :searchable="true"
70
+ :required="true"
71
+ :tooltip="t('hpa.scalingRule.policy.typeTooltip')"
72
+ :label="t('hpa.scalingRule.policy.type')"
73
+ />
74
+ </div>
75
+ <div class="col span-4">
76
+ <LabeledInput
77
+ v-model.number="props.row.value.value"
78
+ :mode="mode"
79
+ type="number"
80
+ min="1"
81
+ :required="true"
82
+ :tooltip="t('hpa.scalingRule.policy.valueTooltip')"
83
+ :label="t('hpa.scalingRule.policy.value')"
84
+ />
85
+ </div>
86
+ <div class="col span-4">
87
+ <LabeledInput
88
+ v-model.number="props.row.value.periodSeconds"
89
+ :mode="mode"
90
+ type="number"
91
+ min="1"
92
+ max="1800"
93
+ :required="true"
94
+ :tooltip="t('hpa.scalingRule.policy.periodSecondsTooltip')"
95
+ :label="t('hpa.scalingRule.policy.periodSeconds')"
96
+ />
97
+ </div>
98
+ </div>
99
+ </template>
100
+ </ArrayListGrouped>
101
+ </div>
102
+ </div>
103
+ <div class="row">
104
+ <div class="col span-6">
105
+ <LabeledSelect
106
+ v-model="value.spec.behavior[type].selectPolicy"
107
+ :mode="mode"
108
+ :options="selectPolicyOptions"
109
+ :multiple="false"
110
+ :taggable="true"
111
+ :searchable="true"
112
+ :label="t('hpa.scalingRule.selectPolicy')"
113
+ :tooltip="t('hpa.scalingRule.selectPolicyTooltip')"
114
+ />
115
+ </div>
116
+ <div class="col span-6">
117
+ <LabeledInput
118
+ v-model.number="value.spec.behavior[type].stabilizationWindowSeconds"
119
+ :mode="mode"
120
+ type="number"
121
+ min="0"
122
+ max="3600"
123
+ :label="t('hpa.scalingRule.stabilizationWindowSeconds')"
124
+ :tooltip="t('hpa.scalingRule.stabilizationWindowSecondsTooltip')"
125
+ />
126
+ </div>
127
+ </div>
128
+ <div class="row mb-40" />
129
+ </div>
130
+ </template>
@@ -12,12 +12,14 @@ import Tabbed from '@shell/components/Tabbed';
12
12
  import MetricsRow from '@shell/edit/autoscaling.horizontalpodautoscaler/metrics-row';
13
13
  import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
14
14
  import { DEFAULT_RESOURCE_METRIC } from '@shell/edit/autoscaling.horizontalpodautoscaler/resource-metric';
15
+ import { Checkbox } from '@components/Form/Checkbox';
15
16
 
16
17
  import { API_SERVICE, SCALABLE_WORKLOAD_TYPES } from '@shell/config/types';
17
18
  import isEmpty from 'lodash/isEmpty';
18
19
  import find from 'lodash/find';
19
20
  import endsWith from 'lodash/endsWith';
20
21
  import { findBy } from '@shell/utils/array';
22
+ import HpaScalingRule from '@shell/edit/autoscaling.horizontalpodautoscaler/hpa-scaling-rule.vue';
21
23
 
22
24
  const RESOURCE_METRICS_API_GROUP = 'metrics.k8s.io';
23
25
 
@@ -25,7 +27,9 @@ export default {
25
27
  name: 'CruHPA',
26
28
 
27
29
  components: {
30
+ HpaScalingRule,
28
31
  ArrayListGrouped,
32
+ Checkbox,
29
33
  CruResource,
30
34
  LabeledInput,
31
35
  LabeledSelect,
@@ -107,6 +111,40 @@ export default {
107
111
 
108
112
  return match ?? null;
109
113
  },
114
+ hasScaleDownRules: {
115
+ get() {
116
+ return !!this.value.spec.behavior?.scaleDown;
117
+ },
118
+ set(hasScaleDownRules) {
119
+ if (hasScaleDownRules) {
120
+ if (!this.value.spec.behavior) {
121
+ this.$set(this.value.spec, 'behavior', {});
122
+ }
123
+ if (!this.value.spec.behavior?.scaleDown) {
124
+ this.$set(this.value.spec.behavior, 'scaleDown', {});
125
+ }
126
+ } else {
127
+ this.$delete(this.value.spec.behavior, 'scaleDown');
128
+ }
129
+ }
130
+ },
131
+ hasScaleUpRules: {
132
+ get() {
133
+ return !!this.value.spec.behavior?.scaleUp;
134
+ },
135
+ set(hasScaleUpRules) {
136
+ if (hasScaleUpRules) {
137
+ if (!this.value.spec.behavior) {
138
+ this.$set(this.value.spec, 'behavior', {});
139
+ }
140
+ if (!this.value.spec.behavior?.scaleUp) {
141
+ this.$set(this.value.spec.behavior, 'scaleUp', {});
142
+ }
143
+ } else {
144
+ this.$delete(this.value.spec.behavior, 'scaleUp');
145
+ }
146
+ }
147
+ },
110
148
  },
111
149
 
112
150
  created() {
@@ -242,6 +280,47 @@ export default {
242
280
  </template>
243
281
  </ArrayListGrouped>
244
282
  </Tab>
283
+ <Tab
284
+ name="behavior"
285
+ :label="t('hpa.tabs.behavior')"
286
+ >
287
+ <div class="col span-12 mb-10">
288
+ <h3>
289
+ {{ t('hpa.scaleDownRules.label') }}
290
+ </h3>
291
+ <div class="row mb-10">
292
+ <Checkbox
293
+ v-model="hasScaleDownRules"
294
+ :mode="mode"
295
+ :label="t('hpa.scaleDownRules.enable')"
296
+ />
297
+ </div>
298
+ <HpaScalingRule
299
+ v-if="hasScaleDownRules"
300
+ v-model="value"
301
+ type="scaleDown"
302
+ :mode="mode"
303
+ />
304
+ </div>
305
+ <div class="col span-12">
306
+ <h3>
307
+ {{ t('hpa.scaleUpRules.label') }}
308
+ </h3>
309
+ <div class="row mb-10">
310
+ <Checkbox
311
+ v-model="hasScaleUpRules"
312
+ :mode="mode"
313
+ :label="t('hpa.scaleUpRules.enable')"
314
+ />
315
+ </div>
316
+ <HpaScalingRule
317
+ v-if="hasScaleUpRules"
318
+ v-model="value"
319
+ type="scaleUp"
320
+ :mode="mode"
321
+ />
322
+ </div>
323
+ </Tab>
245
324
  <Tab
246
325
  name="labels-and-annotations"
247
326
  :label="t('hpa.tabs.labels')"
@@ -604,6 +604,23 @@ export default {
604
604
  </template>
605
605
  <div class="spacer" />
606
606
  <h2 v-t="'fleet.gitRepo.resources.label'" />
607
+ <div>
608
+ <Checkbox
609
+ v-model="value.spec.correctDrift.enabled"
610
+ data-testid="GitRepo-correctDrift-checkbox"
611
+ class="check"
612
+ type="checkbox"
613
+ label-key="fleet.gitRepo.resources.correctDrift"
614
+ :mode="mode"
615
+ />
616
+ <Banner
617
+ data-testid="GitRepo-correctDrift-banner"
618
+ color="info"
619
+ >
620
+ {{ t('fleet.gitRepo.resources.correctDriftBanner') }}
621
+ </Banner>
622
+ </div>
623
+
607
624
  <Checkbox
608
625
  v-model="value.spec.keepResources"
609
626
  class="check"
@@ -614,7 +631,7 @@ export default {
614
631
  <Banner
615
632
  color="info"
616
633
  >
617
- {{ t('fleet.gitRepo.resources.resourceBanner') }}
634
+ {{ t('fleet.gitRepo.resources.keepResourcesBanner') }}
618
635
  </Banner>
619
636
  <div class="spacer" />
620
637
  <h2 v-t="'fleet.gitRepo.paths.label'" />
@@ -42,7 +42,10 @@ export default {
42
42
  },
43
43
 
44
44
  data() {
45
- return { fvFormRuleSets: [{ path: 'metadata.name', rules: ['dnsLabel'] }] };
45
+ return {
46
+ fvFormRuleSets: [{ path: 'metadata.name', rules: ['dnsLabel'] }],
47
+ closedErrorMessages: []
48
+ };
46
49
  },
47
50
 
48
51
  computed: {
@@ -54,7 +57,7 @@ export default {
54
57
  return [this.t('validation.prometheusRule.noEdit')];
55
58
  }
56
59
 
57
- return this.fvUnreportedValidationErrors;
60
+ return this.fvUnreportedValidationErrors.filter((e) => !this.closedErrorMessages.includes(e));
58
61
  }
59
62
  },
60
63
 
@@ -102,6 +105,8 @@ export default {
102
105
  }
103
106
  });
104
107
 
108
+ this.closedErrorMessages = [];
109
+
105
110
  return true;
106
111
  },
107
112
 
@@ -125,7 +130,7 @@ export default {
125
130
  :mode="mode"
126
131
  :resource="value"
127
132
  :validation-passed="fvFormIsValid"
128
- @error="(e) => (errors = e)"
133
+ @error="(_, closedError) => closedErrorMessages.push(closedError)"
129
134
  @finish="save"
130
135
  >
131
136
  <div class="row">
@@ -17,6 +17,7 @@ import Loading from '@shell/components/Loading';
17
17
  import { HARVESTER_TYPES, RANCHER_TYPES } from '@shell/components/form/ResourceQuota/shared';
18
18
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
19
19
  import Labels from '@shell/components/form/Labels';
20
+ import { randomStr } from '@shell/utils/string';
20
21
 
21
22
  export default {
22
23
  components: {
@@ -58,6 +59,7 @@ export default {
58
59
  projects: null,
59
60
  viewMode: _VIEW,
60
61
  containerResourceLimits: this.value.annotations?.[CONTAINER_DEFAULT_RESOURCE_LIMIT] || this.getDefaultContainerResourceLimits(projectName),
62
+ rerenderNums: randomStr(4),
61
63
  projectName,
62
64
  HARVESTER_TYPES,
63
65
  RANCHER_TYPES,
@@ -152,6 +154,11 @@ export default {
152
154
  const project = projects.find((p) => p.id.includes(projectName));
153
155
 
154
156
  return project?.spec?.containerDefaultResourceLimit || {};
157
+ },
158
+
159
+ PSAChanged($event) {
160
+ this.value.setLabels($event);
161
+ this.rerenderNums = randomStr(4);
155
162
  }
156
163
  },
157
164
  };
@@ -242,6 +249,7 @@ export default {
242
249
  :weight="-1"
243
250
  >
244
251
  <Labels
252
+ :key="rerenderNums"
245
253
  default-container-class="labels-and-annotations-container"
246
254
  :value="value"
247
255
  :mode="mode"
@@ -258,7 +266,7 @@ export default {
258
266
  :labels="value.labels"
259
267
  :mode="mode"
260
268
  labels-prefix="pod-security.kubernetes.io/"
261
- @updateLabels="value.setLabels($event)"
269
+ @updateLabels="PSAChanged"
262
270
  />
263
271
  </Tab>
264
272
  </Tabbed>
@@ -140,8 +140,6 @@ export default {
140
140
  >
141
141
  <Select
142
142
  v-model="serviceName"
143
- option-label="label"
144
- option-key="label"
145
143
  :options="serviceTargets"
146
144
  :status="serviceTargetStatus"
147
145
  :taggable="true"