@rancher/shell 0.3.16 → 0.3.18

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 (174) hide show
  1. package/assets/images/wechat-qr-code.jpg +0 -0
  2. package/assets/translations/en-us.yaml +75 -16
  3. package/assets/translations/zh-hans.yaml +151 -15
  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/components/AsyncButton.vue +1 -1
  8. package/components/CommunityLinks.vue +1 -0
  9. package/components/FileDiff.vue +92 -85
  10. package/components/Inactivity.vue +10 -0
  11. package/components/LazyImage.vue +2 -2
  12. package/components/PromptRestore.vue +7 -5
  13. package/components/ResourceDetail/Masthead.vue +1 -1
  14. package/components/ResourceDetail/index.vue +8 -14
  15. package/components/ResourceList/index.vue +1 -1
  16. package/components/ResourceTable.vue +50 -2
  17. package/components/YamlEditor.vue +1 -0
  18. package/components/__tests__/PromptRestore.test.ts +72 -0
  19. package/components/auth/AzureWarning.vue +1 -1
  20. package/components/auth/RoleDetailEdit.vue +1 -0
  21. package/components/fleet/FleetResources.vue +3 -64
  22. package/components/form/FileImageSelector.vue +9 -0
  23. package/components/form/FileSelector.vue +2 -1
  24. package/components/form/MatchExpressions.vue +1 -3
  25. package/components/form/NameNsDescription.vue +28 -12
  26. package/components/form/NodeAffinity.vue +2 -2
  27. package/components/form/PodAffinity.vue +2 -2
  28. package/components/form/ResourceTabs/index.vue +8 -2
  29. package/components/form/Select.vue +16 -0
  30. package/components/form/__tests__/FileImageSelector.test.ts +42 -0
  31. package/components/form/__tests__/FileSelector.test.ts +76 -0
  32. package/components/form/__tests__/NodeAffinity.test.ts +38 -0
  33. package/components/form/__tests__/PodAffinity.test.ts +46 -0
  34. package/components/formatter/ClusterLink.vue +8 -4
  35. package/components/formatter/ClusterProvider.vue +3 -1
  36. package/components/formatter/ImageName.vue +23 -0
  37. package/components/formatter/PodImages.vue +7 -1
  38. package/components/formatter/__tests__/ClusterLink.test.ts +101 -0
  39. package/components/formatter/__tests__/ClusterProvider.test.ts +24 -0
  40. package/components/nav/Header.vue +2 -2
  41. package/components/nav/WindowManager/ContainerShell.vue +60 -36
  42. package/components/nav/WindowManager/__tests__/ContainerShell.test.ts +561 -0
  43. package/config/__test__/home-links.test.ts +62 -0
  44. package/config/home-links.js +15 -3
  45. package/config/labels-annotations.js +7 -2
  46. package/config/persistentVolume.ts +108 -0
  47. package/config/product/manager.js +5 -1
  48. package/config/router.js +0 -4
  49. package/config/settings.ts +4 -0
  50. package/config/table-headers.js +6 -5
  51. package/config/types.js +2 -0
  52. package/config/uiplugins.js +50 -5
  53. package/core/plugin-helpers.js +39 -15
  54. package/core/plugin.ts +9 -0
  55. package/core/plugins.js +1 -1
  56. package/core/types-provisioning.ts +253 -0
  57. package/core/types.ts +21 -3
  58. package/detail/autoscaling.horizontalpodautoscaler/index.vue +50 -1
  59. package/detail/fleet.cattle.io.gitrepo.vue +10 -2
  60. package/detail/node.vue +6 -6
  61. package/detail/pod.vue +38 -9
  62. package/detail/provisioning.cattle.io.cluster.vue +46 -7
  63. package/detail/workload/index.vue +49 -18
  64. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +62 -0
  65. package/edit/__tests__/ui.cattle.io.navlink.test.ts +110 -0
  66. package/edit/auth/github.vue +1 -0
  67. package/edit/autoscaling.horizontalpodautoscaler/hpa-scaling-rule.vue +130 -0
  68. package/edit/autoscaling.horizontalpodautoscaler/index.vue +79 -0
  69. package/edit/fleet.cattle.io.clustergroup.vue +14 -3
  70. package/edit/fleet.cattle.io.gitrepo.vue +18 -1
  71. package/edit/namespace.vue +9 -1
  72. package/edit/networking.k8s.io.ingress/RulePath.vue +0 -2
  73. package/edit/persistentvolume/__tests__/persistentvolume.test.ts +82 -0
  74. package/edit/persistentvolume/index.vue +2 -1
  75. package/edit/persistentvolume/plugins/csi.vue +3 -1
  76. package/edit/persistentvolume/plugins/longhorn.vue +12 -12
  77. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +1 -30
  78. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +15 -11
  79. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +79 -1
  80. package/edit/provisioning.cattle.io.cluster/index.vue +53 -1
  81. package/edit/provisioning.cattle.io.cluster/rke2.vue +335 -151
  82. package/edit/storage.k8s.io.storageclass/index.vue +1 -2
  83. package/edit/ui.cattle.io.navlink.vue +213 -186
  84. package/initialize/App.js +3 -13
  85. package/initialize/layouts.ts +26 -0
  86. package/layouts/default.vue +1 -1
  87. package/list/group.principal.vue +1 -1
  88. package/list/provisioning.cattle.io.cluster.vue +8 -1
  89. package/middleware/authenticated.js +101 -5
  90. package/mixins/brand.js +39 -3
  91. package/mixins/child-hook.js +2 -2
  92. package/mixins/create-edit-view/impl.js +4 -4
  93. package/models/chart.js +1 -1
  94. package/models/fleet.cattle.io.cluster.js +33 -4
  95. package/models/fleet.cattle.io.gitrepo.js +113 -38
  96. package/models/management.cattle.io.kontainerdriver.js +14 -0
  97. package/models/persistentvolume.js +2 -111
  98. package/models/pod.js +30 -0
  99. package/models/provisioning.cattle.io.cluster.js +9 -1
  100. package/models/rke.cattle.io.etcdsnapshot.js +10 -7
  101. package/package.json +2 -2
  102. package/pages/about.vue +8 -2
  103. package/pages/auth/login.vue +1 -1
  104. package/pages/auth/logout.vue +11 -3
  105. package/pages/c/_cluster/apps/charts/index.vue +5 -2
  106. package/pages/c/_cluster/apps/charts/install.vue +5 -0
  107. package/pages/c/_cluster/auth/group.principal/assign-edit.vue +1 -1
  108. package/pages/c/_cluster/auth/roles/index.vue +1 -1
  109. package/pages/c/_cluster/explorer/index.vue +2 -11
  110. package/pages/c/_cluster/manager/cloudCredential/_id.vue +0 -1
  111. package/pages/c/_cluster/manager/cloudCredential/create.vue +0 -1
  112. package/pages/c/_cluster/settings/brand.vue +11 -8
  113. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +177 -0
  114. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +19 -3
  115. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +90 -21
  116. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +107 -37
  117. package/pages/c/_cluster/uiplugins/index.vue +160 -44
  118. package/pages/docs/_doc.vue +9 -3
  119. package/pages/home.vue +6 -6
  120. package/pages/support/index.vue +10 -4
  121. package/pkg/auto-import.js +1 -1
  122. package/plugins/clean-tooltip-directive.js +1 -1
  123. package/plugins/dashboard-store/__tests__/actions.spec.ts +165 -0
  124. package/plugins/dashboard-store/__tests__/getters.spec.ts +100 -0
  125. package/plugins/dashboard-store/__tests__/{mutations.spec.js → mutations.spec.ts} +2 -2
  126. package/plugins/dashboard-store/actions.js +1 -1
  127. package/plugins/dashboard-store/resource-class.js +39 -2
  128. package/plugins/plugin.js +9 -1
  129. package/plugins/steve/__tests__/getters.spec.ts +93 -0
  130. package/plugins/steve/getters.js +21 -1
  131. package/plugins/steve/subscribe.js +1 -3
  132. package/rancher-components/BadgeState/BadgeState.vue +5 -1
  133. package/rancher-components/Banner/Banner.test.ts +51 -1
  134. package/rancher-components/Banner/Banner.vue +134 -53
  135. package/rancher-components/Card/Card.test.ts +37 -0
  136. package/rancher-components/Card/Card.vue +24 -7
  137. package/rancher-components/Form/Checkbox/Checkbox.test.ts +20 -29
  138. package/rancher-components/Form/Checkbox/Checkbox.vue +45 -20
  139. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +2 -8
  140. package/rancher-components/Form/LabeledInput/LabeledInput.vue +22 -10
  141. package/rancher-components/Form/Radio/RadioButton.test.ts +31 -0
  142. package/rancher-components/Form/Radio/RadioButton.vue +30 -13
  143. package/rancher-components/Form/Radio/RadioGroup.vue +26 -7
  144. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +7 -6
  145. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +25 -38
  146. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +23 -11
  147. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +19 -5
  148. package/rancher-components/StringList/StringList.test.ts +453 -49
  149. package/rancher-components/StringList/StringList.vue +44 -26
  150. package/scripts/extension/publish +2 -2
  151. package/scripts/typegen.sh +11 -2
  152. package/server/server-middleware.js +4 -12
  153. package/store/index.js +14 -3
  154. package/store/prefs.js +0 -3
  155. package/store/store-types.js +2 -0
  156. package/store/type-map.js +17 -29
  157. package/types/api.d.ts +1 -0
  158. package/types/fleet.d.ts +1 -0
  159. package/types/shell/index.d.ts +931 -85
  160. package/types/userPreferences.d.ts +1 -1
  161. package/utils/__mocks__/socket.js +21 -0
  162. package/utils/grafana.js +23 -11
  163. package/utils/kube.js +9 -0
  164. package/utils/object.js +27 -0
  165. package/utils/selector.js +2 -1
  166. package/utils/settings.ts +2 -2
  167. package/utils/validators/formRules/index.ts +3 -3
  168. package/vue.config.js +3 -2
  169. package/components/.DS_Store +0 -0
  170. package/components/__tests__/.DS_Store +0 -0
  171. package/creators/pkg/package-lock.json +0 -37
  172. package/pages/safeMode.vue +0 -17
  173. package/plugins/steve/urloptions.js +0 -47
  174. package/yarn-error.log +0 -196
@@ -2,9 +2,9 @@
2
2
  import CreateEditView from '@shell/mixins/create-edit-view';
3
3
  import { NAMESPACE as NAMESPACE_COL } from '@shell/config/table-headers';
4
4
  import {
5
- POD, WORKLOAD_TYPES, SCALABLE_WORKLOAD_TYPES, SERVICE, INGRESS, NODE
5
+ POD, WORKLOAD_TYPES, SCALABLE_WORKLOAD_TYPES, SERVICE, INGRESS, NODE, NAMESPACE,
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';
@@ -16,6 +16,7 @@ import { mapGetters } from 'vuex';
16
16
  import { allDashboardsExist } from '@shell/utils/grafana';
17
17
  import PlusMinus from '@shell/components/form/PlusMinus';
18
18
  import { matches } from '@shell/utils/selector';
19
+ import { PROJECT } from '@shell/config/labels-annotations';
19
20
 
20
21
  const SCALABLE_TYPES = Object.values(SCALABLE_WORKLOAD_TYPES);
21
22
  const WORKLOAD_METRICS_DETAIL_URL = '/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-grafana:80/proxy/d/rancher-workload-pods-1/rancher-workload-pods?orgId=1';
@@ -46,7 +47,7 @@ export default {
46
47
  Loading,
47
48
  ResourceTabs,
48
49
  CountGauge,
49
- SortableTable,
50
+ ResourceTable,
50
51
  PlusMinus
51
52
  },
52
53
 
@@ -85,23 +86,37 @@ export default {
85
86
  const isMetricsSupportedKind = METRICS_SUPPORTED_KINDS.includes(this.value.type);
86
87
 
87
88
  this.showMetrics = isMetricsSupportedKind && await allDashboardsExist(this.$store, this.currentCluster.id, [WORKLOAD_METRICS_DETAIL_URL, WORKLOAD_METRICS_SUMMARY_URL]);
89
+ if (!this.showMetrics) {
90
+ const namespace = await this.$store.dispatch('cluster/find', { type: NAMESPACE, id: this.value.metadata.namespace });
88
91
 
92
+ const projectId = namespace?.metadata?.labels[PROJECT];
93
+
94
+ if (projectId) {
95
+ this.WORKLOAD_PROJECT_METRICS_DETAIL_URL = `/api/v1/namespaces/cattle-project-${ projectId }-monitoring/services/http:cattle-project-${ projectId }-monitoring-grafana:80/proxy/d/rancher-pod-containers-1/rancher-workload-pods?orgId=1'`;
96
+ this.WORKLOAD_PROJECT_METRICS_SUMMARY_URL = `/api/v1/namespaces/cattle-project-${ projectId }-monitoring/services/http:cattle-project-${ projectId }-monitoring-grafana:80/proxy/d/rancher-pod-1/rancher-workload?orgId=1`;
97
+
98
+ this.showProjectMetrics = await allDashboardsExist(this.$store, this.currentCluster.id, [this.WORKLOAD_PROJECT_METRICS_DETAIL_URL, this.WORKLOAD_PROJECT_METRICS_SUMMARY_URL], 'cluster', projectId);
99
+ }
100
+ }
89
101
  this.findMatchingServices();
90
102
  this.findMatchingIngresses();
91
103
  },
92
104
 
93
105
  data() {
94
106
  return {
95
- allPods: [],
96
- allServices: [],
97
- allIngresses: [],
98
- matchingServices: [],
99
- matchingIngresses: [],
100
- allJobs: [],
101
- allNodes: [],
107
+ allPods: [],
108
+ allServices: [],
109
+ allIngresses: [],
110
+ matchingServices: [],
111
+ matchingIngresses: [],
112
+ allJobs: [],
113
+ allNodes: [],
102
114
  WORKLOAD_METRICS_DETAIL_URL,
103
115
  WORKLOAD_METRICS_SUMMARY_URL,
104
- showMetrics: false,
116
+ POD_PROJECT_METRICS_DETAIL_URL: '',
117
+ POD_PROJECT_METRICS_SUMMARY_URL: '',
118
+ showMetrics: false,
119
+ showProjectMetrics: false,
105
120
  };
106
121
  },
107
122
 
@@ -380,12 +395,12 @@ export default {
380
395
  :label="t('tableHeaders.jobs')"
381
396
  :weight="4"
382
397
  >
383
- <SortableTable
398
+ <ResourceTable
384
399
  :rows="value.jobs"
385
400
  :headers="jobHeaders"
386
401
  key-field="id"
387
402
  :schema="jobSchema"
388
- :show-groups="false"
403
+ :groupable="false"
389
404
  :search="false"
390
405
  />
391
406
  </Tab>
@@ -395,7 +410,7 @@ export default {
395
410
  :label="t('tableHeaders.pods')"
396
411
  :weight="4"
397
412
  >
398
- <SortableTable
413
+ <ResourceTable
399
414
  v-if="value.pods"
400
415
  :rows="value.pods"
401
416
  :headers="podHeaders"
@@ -421,6 +436,22 @@ export default {
421
436
  />
422
437
  </template>
423
438
  </Tab>
439
+ <Tab
440
+ v-if="showProjectMetrics"
441
+ :label="t('workload.container.titles.metrics')"
442
+ name="workload-metrics"
443
+ :weight="3"
444
+ >
445
+ <template #default="props">
446
+ <DashboardMetrics
447
+ v-if="props.active"
448
+ :detail-url="WORKLOAD_PROJECT_METRICS_DETAIL_URL"
449
+ :summary-url="WORKLOAD_PROJECT_METRICS_SUMMARY_URL"
450
+ :vars="graphVars"
451
+ graph-height="550px"
452
+ />
453
+ </template>
454
+ </Tab>
424
455
  <Tab
425
456
  v-if="v1MonitoringUrl"
426
457
  name="v1Metrics"
@@ -458,13 +489,13 @@ export default {
458
489
  >
459
490
  {{ t('workload.detail.serviceListCaption') }}
460
491
  </p>
461
- <SortableTable
492
+ <ResourceTable
462
493
  v-if="serviceSchema && matchingServices.length > 0"
463
494
  :rows="matchingServices"
464
495
  :headers="serviceHeaders"
465
496
  key-field="id"
466
497
  :schema="serviceSchema"
467
- :show-groups="false"
498
+ :groupable="false"
468
499
  :search="false"
469
500
  :table-actions="false"
470
501
  />
@@ -499,13 +530,13 @@ export default {
499
530
  >
500
531
  {{ t('workload.detail.ingressListCaption') }}
501
532
  </p>
502
- <SortableTable
533
+ <ResourceTable
503
534
  v-if="ingressSchema && matchingIngresses.length > 0"
504
535
  :rows="matchingIngresses"
505
536
  :headers="ingressHeaders"
506
537
  key-field="id"
507
538
  :schema="ingressSchema"
508
- :show-groups="false"
539
+ :groupable="false"
509
540
  :search="false"
510
541
  :table-actions="false"
511
542
  />
@@ -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,110 @@
1
+ /* eslint-disable jest/no-hooks */
2
+ import { mount } from '@vue/test-utils';
3
+ import Navlink from '@shell/edit/ui.cattle.io.navlink.vue';
4
+ import { _CREATE } from '@shell/config/query-params';
5
+ import CruResource from '@shell/components/CruResource';
6
+
7
+ describe('view: ui.cattle.io.navlink should', () => {
8
+ const name = 'test';
9
+ const url = 'http://test.com';
10
+ let wrapper: any;
11
+
12
+ const requiredSetup = () => ({
13
+ // Remove all these mocks after migration to Vue 2.7/3 due mixin logic
14
+ mocks: {
15
+ $store: {
16
+ getters: {
17
+ currentStore: () => 'current_store',
18
+ 'current_store/schemaFor': jest.fn(),
19
+ 'current_store/all': jest.fn(),
20
+ 'i18n/t': (val) => val,
21
+ 'i18n/exists': jest.fn(),
22
+ }
23
+ },
24
+ $route: { query: { AS: '' } },
25
+ $router: { applyQuery: jest.fn() },
26
+ },
27
+ propsData: {
28
+ metadata: { namespace: 'test' },
29
+ spec: { template: {} },
30
+ targetInfo: { mode: 'all' },
31
+ value: {},
32
+ mode: _CREATE,
33
+ },
34
+
35
+ });
36
+
37
+ beforeEach(() => {
38
+ wrapper = mount(Navlink, { ...requiredSetup() });
39
+ });
40
+
41
+ afterEach(() => {
42
+ wrapper.destroy();
43
+ });
44
+
45
+ it('have "Create" button disabled before fields are filled in', () => {
46
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
47
+
48
+ expect(saveButton.disabled).toBe(true);
49
+ });
50
+ it('have "Create" button disabled when Link type is URL and only name is filled in', async() => {
51
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
52
+ const nameField = wrapper.find('[data-testid="Navlink-name-field"]').find('input');
53
+
54
+ nameField.setValue(name);
55
+
56
+ await wrapper.vm.$nextTick();
57
+
58
+ expect(saveButton.disabled).toBe(true);
59
+ });
60
+ it('have "Create" button enabled when Link type is URL and all required fields are filled in', async() => {
61
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
62
+ const nameField = wrapper.find('[data-testid="Navlink-name-field"]').find('input');
63
+ const urlField = wrapper.find('[data-testid="Navlink-url-field"]');
64
+
65
+ nameField.setValue(name);
66
+ urlField.setValue(url);
67
+
68
+ await wrapper.vm.$nextTick();
69
+
70
+ expect(saveButton.disabled).toBe(false);
71
+ });
72
+
73
+ it('have "Create" button disabled when Link type is Service and and only name is filled in', async() => {
74
+ const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
75
+ const nameField = wrapper.find('[data-testid="Navlink-name-field"]').find('input');
76
+ const rg = wrapper.find('[data-testid="Navlink-link-radiogroup"]');
77
+
78
+ const serviceBttn = rg.findAll('.radio-label').at(1);
79
+
80
+ nameField.setValue(name);
81
+ serviceBttn.trigger('click');
82
+ await wrapper.vm.$nextTick();
83
+
84
+ expect(saveButton.disabled).toBe(true);
85
+ });
86
+
87
+ it('have "Create" button enabled when Link type is Service and and all required fields are filled in', async() => {
88
+ const nameField = wrapper.find('[data-testid="Navlink-name-field"]').find('input');
89
+ const rg = wrapper.find('[data-testid="Navlink-link-radiogroup"]');
90
+
91
+ const serviceBttn = rg.findAll('.radio-label').at(1);
92
+
93
+ nameField.setValue(name);
94
+ serviceBttn.trigger('click');
95
+ await wrapper.vm.$nextTick();
96
+
97
+ const schemeField = wrapper.find('[data-testid="Navlink-scheme-field"]');
98
+ const serviceField = wrapper.find('[data-testid="Navlink-currentService-field"]');
99
+
100
+ schemeField.find('button').trigger('click');
101
+ await wrapper.trigger('keydown.down');
102
+ await wrapper.trigger('keydown.enter');
103
+
104
+ serviceField.find('button').trigger('click');
105
+ await wrapper.trigger('keydown.down');
106
+ await wrapper.trigger('keydown.enter');
107
+
108
+ expect(CruResource.computed.canSave()).toBe(true);
109
+ });
110
+ });
@@ -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')"
@@ -11,6 +11,7 @@ import { set } from '@shell/utils/object';
11
11
  import { FLEET } from '@shell/config/types';
12
12
  import { convert, matching, simplify } from '@shell/utils/selector';
13
13
  import throttle from 'lodash/throttle';
14
+ import { allHash } from '@shell/utils/promise';
14
15
 
15
16
  export default {
16
17
  name: 'CruClusterGroup',
@@ -27,10 +28,20 @@ export default {
27
28
  mixins: [CreateEditView],
28
29
 
29
30
  async fetch() {
30
- if (this.$store.getters['management/schemaFor']( FLEET.CLUSTER )) {
31
- this.allClusters = await this.$store.getters['management/all'](FLEET.CLUSTER);
31
+ const _hash = {};
32
+
33
+ if (this.$store.getters['management/schemaFor'](FLEET.CLUSTER)) {
34
+ _hash.allClusters = await this.$store.dispatch('management/findAll', { type: FLEET.CLUSTER });
35
+ }
36
+
37
+ if (this.$store.getters['management/schemaFor'](FLEET.WORKSPACE)) {
38
+ _hash.allWorkspaces = this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE });
32
39
  }
33
- this.allWorkspaces = await this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE });
40
+
41
+ const hash = await allHash(_hash);
42
+
43
+ this.allClusters = hash.allClusters || [];
44
+ this.allWorkspaces = hash.allWorkspaces || [];
34
45
 
35
46
  if ( !this.value.spec?.selector ) {
36
47
  this.value.spec = this.value.spec || {};
@@ -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'" />
@@ -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>