@rancher/shell 3.0.8 → 3.0.9-rc.2

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 (192) hide show
  1. package/apis/intf/modal.ts +38 -0
  2. package/apis/intf/slide-in.ts +3 -1
  3. package/apis/shell/__tests__/slide-in.test.ts +36 -0
  4. package/apis/shell/slide-in.ts +5 -1
  5. package/assets/styles/base/_color.scss +1 -0
  6. package/assets/styles/base/_typography.scss +14 -5
  7. package/assets/styles/themes/_light.scss +1 -1
  8. package/assets/styles/themes/_modern.scss +1 -1
  9. package/assets/translations/en-us.yaml +94 -33
  10. package/assets/translations/zh-hans.yaml +0 -2
  11. package/components/ActionMenuShell.vue +4 -4
  12. package/components/CodeMirror.vue +4 -3
  13. package/components/DetailText.vue +54 -7
  14. package/components/Drawer/Chrome.vue +11 -4
  15. package/components/Drawer/DrawerCard.vue +19 -0
  16. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +3 -11
  17. package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +2 -2
  18. package/components/Drawer/ResourceDetailDrawer/index.vue +3 -20
  19. package/components/Drawer/types.ts +1 -0
  20. package/components/DynamicContent/DynamicContentCloseButton.vue +2 -2
  21. package/components/LocaleSelector.vue +1 -1
  22. package/components/Markdown.vue +1 -1
  23. package/components/PopoverCard.vue +3 -3
  24. package/components/Resource/Detail/Card/ExtrasCard.vue +39 -0
  25. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +142 -0
  26. package/components/Resource/Detail/Card/StateCard/composables.ts +41 -11
  27. package/components/Resource/Detail/Card/StateCard/index.vue +3 -9
  28. package/components/Resource/Detail/Card/StateCard/types.ts +6 -0
  29. package/components/Resource/Detail/Card/{PodsCard → StatusCard}/index.vue +11 -10
  30. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +24 -25
  31. package/components/Resource/Detail/Cards.vue +27 -0
  32. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +70 -0
  33. package/components/Resource/Detail/Masthead/index.vue +5 -0
  34. package/components/Resource/Detail/Metadata/KeyValueRow.vue +4 -2
  35. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -2
  36. package/components/Resource/Detail/ResourceRow.types.ts +14 -0
  37. package/components/Resource/Detail/ResourceRow.vue +23 -35
  38. package/components/Resource/Detail/StatusRow.vue +5 -2
  39. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +38 -7
  40. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +106 -2
  41. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  42. package/components/Resource/Detail/TitleBar/index.vue +41 -6
  43. package/components/ResourceDetail/Masthead/__tests__/index.test.ts +49 -1
  44. package/components/ResourceDetail/Masthead/__tests__/latest.test.ts +85 -0
  45. package/components/ResourceDetail/Masthead/index.vue +1 -0
  46. package/components/ResourceDetail/Masthead/latest.vue +8 -1
  47. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  48. package/components/Setting.vue +1 -1
  49. package/components/SortableTable/index.vue +25 -0
  50. package/components/SortableTable/selection.js +25 -12
  51. package/components/SortableTable/sorting.js +1 -1
  52. package/components/Tabbed/Tab.vue +1 -0
  53. package/components/Tabbed/index.vue +29 -6
  54. package/components/Window/ContainerShell.vue +10 -13
  55. package/components/fleet/FleetClusterTargets/TargetsList.vue +47 -29
  56. package/components/fleet/FleetClusterTargets/index.vue +82 -29
  57. package/components/fleet/FleetClusters.vue +26 -12
  58. package/components/fleet/FleetGitRepoPaths.vue +2 -2
  59. package/components/fleet/FleetResources.vue +14 -0
  60. package/components/fleet/FleetValuesFrom.vue +2 -2
  61. package/components/fleet/__tests__/FleetClusterTargets.test.ts +531 -0
  62. package/components/fleet/__tests__/FleetClusters.test.ts +576 -0
  63. package/components/fleet/dashboard/ResourceDetails.vue +96 -123
  64. package/components/form/Conditions.vue +1 -15
  65. package/components/form/HookOption.vue +5 -0
  66. package/components/form/LabeledSelect.vue +1 -1
  67. package/components/form/LifecycleHooks.vue +2 -6
  68. package/components/form/ResourceLabeledSelect.vue +12 -1
  69. package/components/form/SeccompProfile.vue +113 -0
  70. package/components/form/Security.vue +244 -133
  71. package/components/form/__tests__/LabeledSelect.test.ts +1 -1
  72. package/components/form/__tests__/SeccompProfile.test.js +124 -0
  73. package/components/form/__tests__/Security.test.ts +125 -37
  74. package/components/formatter/Autoscaler.vue +2 -2
  75. package/components/formatter/FleetSummaryGraph.vue +4 -1
  76. package/components/nav/Group.vue +5 -0
  77. package/components/nav/Header.vue +3 -3
  78. package/components/nav/HeaderPageActionMenu.vue +1 -1
  79. package/components/nav/NamespaceFilter.vue +6 -6
  80. package/components/nav/NotificationCenter/index.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +41 -16
  82. package/components/nav/TopLevelMenu.vue +45 -25
  83. package/components/nav/WorkspaceSwitcher.vue +1 -1
  84. package/components/nav/__tests__/TopLevelMenu.helper.test.ts +277 -0
  85. package/components/nav/__tests__/TopLevelMenu.test.ts +160 -4
  86. package/components/templates/default.vue +0 -3
  87. package/components/templates/home.vue +0 -3
  88. package/components/templates/plain.vue +0 -3
  89. package/composables/useClickOutside.ts +1 -1
  90. package/config/product/explorer.js +1 -2
  91. package/config/types.js +41 -8
  92. package/detail/__tests__/workload.test.ts +8 -16
  93. package/detail/catalog.cattle.io.app.vue +6 -0
  94. package/detail/fleet.cattle.io.cluster.vue +6 -0
  95. package/detail/workload/index.vue +7 -109
  96. package/edit/__tests__/projectsecret.test.ts +42 -0
  97. package/edit/auth/__tests__/oidc.test.ts +50 -0
  98. package/edit/auth/oidc.vue +68 -44
  99. package/edit/autoscaling.horizontalpodautoscaler/index.vue +140 -59
  100. package/edit/autoscaling.horizontalpodautoscaler/metrics-row.vue +41 -5
  101. package/edit/projectsecret.vue +29 -0
  102. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +89 -200
  103. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +58 -17
  104. package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -0
  105. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +3 -63
  106. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +82 -14
  107. package/edit/workload/__tests__/index.test.ts +122 -85
  108. package/edit/workload/index.vue +48 -29
  109. package/edit/workload/mixins/workload.js +85 -32
  110. package/list/catalog.cattle.io.clusterrepo.vue +1 -1
  111. package/list/projectsecret.vue +2 -2
  112. package/machine-config/__tests__/vmwarevsphere.test.ts +64 -0
  113. package/machine-config/amazonec2.vue +2 -2
  114. package/machine-config/vmwarevsphere.vue +58 -4
  115. package/mixins/__tests__/brand.spec.ts +18 -13
  116. package/mixins/__tests__/chart.test.ts +63 -0
  117. package/mixins/chart.js +56 -51
  118. package/models/__tests__/catalog.cattle.io.app.test.ts +33 -0
  119. package/models/__tests__/workload.test.ts +333 -0
  120. package/models/catalog.cattle.io.app.js +8 -0
  121. package/models/pod.js +14 -0
  122. package/models/secret.js +1 -1
  123. package/models/workload.js +93 -27
  124. package/package.json +4 -4
  125. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +91 -0
  126. package/pages/c/_cluster/apps/charts/install.vue +4 -4
  127. package/pages/c/_cluster/explorer/EventsTable.vue +2 -2
  128. package/pages/c/_cluster/fleet/index.vue +18 -12
  129. package/pages/c/_cluster/manager/hostedprovider/index.vue +1 -19
  130. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  131. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  132. package/plugins/dashboard-store/__tests__/resource-class.test.ts +234 -0
  133. package/plugins/dashboard-store/actions.js +9 -8
  134. package/plugins/dashboard-store/resource-class.js +97 -1
  135. package/plugins/steve/__tests__/revision.test.ts +84 -0
  136. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +30 -0
  137. package/plugins/steve/__tests__/subscribe.spec.ts +134 -0
  138. package/plugins/steve/mutations.js +9 -0
  139. package/plugins/steve/revision.ts +26 -0
  140. package/plugins/steve/steve-pagination-utils.ts +6 -5
  141. package/plugins/steve/subscribe.js +211 -51
  142. package/plugins/subscribe-events.ts +2 -2
  143. package/rancher-components/Form/Checkbox/Checkbox.vue +13 -0
  144. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -1
  145. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +1 -1
  146. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +3 -1
  147. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -1
  148. package/rancher-components/Pill/RcTag/RcTag.vue +1 -1
  149. package/rancher-components/Pill/index.ts +4 -0
  150. package/rancher-components/RcButton/RcButton.test.ts +53 -9
  151. package/rancher-components/RcButton/RcButton.vue +217 -25
  152. package/rancher-components/RcButton/types.ts +27 -1
  153. package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -4
  154. package/rancher-components/RcDropdown/types.ts +3 -3
  155. package/rancher-components/RcIcon/RcIcon.test.ts +42 -0
  156. package/rancher-components/RcIcon/RcIcon.vue +9 -6
  157. package/rancher-components/RcIcon/types.ts +13 -9
  158. package/rancher-components/utils/status.test.ts +10 -15
  159. package/rancher-components/utils/status.ts +5 -6
  160. package/store/aws.js +18 -12
  161. package/store/index.js +4 -8
  162. package/store/type-map.utils.ts +1 -1
  163. package/types/kube/kube-api.ts +29 -3
  164. package/types/rancher/steve.api.ts +40 -0
  165. package/types/shell/index.d.ts +99 -0
  166. package/types/store/dashboard-store.types.ts +29 -7
  167. package/types/store/pagination.types.ts +1 -0
  168. package/types/store/subscribe-events.types.ts +1 -0
  169. package/utils/__tests__/azure.test.ts +56 -0
  170. package/utils/__tests__/back-off.test.ts +364 -245
  171. package/utils/__tests__/error.test.ts +44 -0
  172. package/utils/__tests__/fleet.test.ts +8 -1
  173. package/utils/__tests__/pagination-wrapper.test.ts +167 -0
  174. package/utils/__tests__/version.test.ts +55 -1
  175. package/utils/azure.js +12 -0
  176. package/utils/back-off.ts +302 -69
  177. package/utils/cspAdaptor.ts +32 -14
  178. package/utils/dynamic-content/__tests__/index.test.ts +1 -1
  179. package/utils/dynamic-content/__tests__/new-release.test.ts +48 -7
  180. package/utils/dynamic-content/__tests__/support-notice.test.ts +1 -4
  181. package/utils/dynamic-content/index.ts +1 -6
  182. package/utils/dynamic-content/new-release.ts +5 -3
  183. package/utils/dynamic-content/types.d.ts +0 -1
  184. package/utils/error.js +9 -0
  185. package/utils/fleet.ts +2 -2
  186. package/utils/inactivity.ts +2 -3
  187. package/utils/pagination-wrapper.ts +101 -17
  188. package/utils/validators/formRules/index.ts +3 -0
  189. package/utils/version.js +38 -0
  190. package/components/auth/AzureWarning.vue +0 -77
  191. /package/components/Resource/Detail/{Card/PodsCard/Bubble.vue → Bubble.vue} +0 -0
  192. /package/components/Resource/Detail/Card/{PodsCard → StatusCard}/composable.ts +0 -0
@@ -17,9 +17,8 @@ describe('findMatchingIngresses', () => {
17
17
  const mockThis = {
18
18
  ingressSchema: true,
19
19
  allIngresses: [],
20
- matchingServices: [],
21
20
  matchingIngresses: [],
22
- value: { metadata: { namespace: 'test' } }
21
+ value: { metadata: { namespace: 'test' }, relatedServices: [] }
23
22
  };
24
23
 
25
24
  findMatchingIngresses.call(mockThis);
@@ -35,9 +34,8 @@ describe('findMatchingIngresses', () => {
35
34
  spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service1' } } }] } }] }
36
35
  }
37
36
  ],
38
- matchingServices: [{ metadata: { name: 'service1' } }],
39
37
  matchingIngresses: [],
40
- value: { metadata: { namespace: 'test' } }
38
+ value: { metadata: { namespace: 'test' }, relatedServices: [{ metadata: { name: 'service1' } }] }
41
39
  };
42
40
 
43
41
  findMatchingIngresses.call(mockThis);
@@ -54,9 +52,8 @@ describe('findMatchingIngresses', () => {
54
52
  spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service1' } } }] } }] }
55
53
  }
56
54
  ],
57
- matchingServices: [{ metadata: { name: 'service1' } }],
58
55
  matchingIngresses: [],
59
- value: { metadata: { namespace: 'test' } }
56
+ value: { metadata: { namespace: 'test' }, relatedServices: [{ metadata: { name: 'service1' } }] }
60
57
  };
61
58
 
62
59
  findMatchingIngresses.call(mockThis);
@@ -72,9 +69,8 @@ describe('findMatchingIngresses', () => {
72
69
  spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service2' } } }] } }] }
73
70
  }
74
71
  ],
75
- matchingServices: [{ metadata: { name: 'service1' } }],
76
72
  matchingIngresses: [],
77
- value: { metadata: { namespace: 'test' } }
73
+ value: { metadata: { namespace: 'test' }, relatedServices: [{ metadata: { name: 'service1' } }] }
78
74
  };
79
75
 
80
76
  findMatchingIngresses.call(mockThis);
@@ -90,9 +86,8 @@ describe('findMatchingIngresses', () => {
90
86
  spec: { }
91
87
  }
92
88
  ],
93
- matchingServices: [{ metadata: { name: 'service1' } }],
94
89
  matchingIngresses: [],
95
- value: { metadata: { namespace: 'test' } }
90
+ value: { metadata: { namespace: 'test' }, relatedServices: [{ metadata: { name: 'service1' } }] }
96
91
  };
97
92
 
98
93
  findMatchingIngresses.call(mockThis);
@@ -108,9 +103,8 @@ describe('findMatchingIngresses', () => {
108
103
  spec: { rules: [{ http: {} }] }
109
104
  }
110
105
  ],
111
- matchingServices: [{ metadata: { name: 'service1' } }],
112
106
  matchingIngresses: [],
113
- value: { metadata: { namespace: 'test' } }
107
+ value: { metadata: { namespace: 'test' }, relatedServices: [{ metadata: { name: 'service1' } }] }
114
108
  };
115
109
 
116
110
  findMatchingIngresses.call(mockThis);
@@ -126,9 +120,8 @@ describe('findMatchingIngresses', () => {
126
120
  spec: { rules: [{ http: { paths: [{ backend: {} }] } }] }
127
121
  }
128
122
  ],
129
- matchingServices: [{ metadata: { name: 'service1' } }],
130
123
  matchingIngresses: [],
131
- value: { metadata: { namespace: 'test' } }
124
+ value: { metadata: { namespace: 'test' }, relatedServices: [{ metadata: { name: 'service1' } }] }
132
125
  };
133
126
 
134
127
  findMatchingIngresses.call(mockThis);
@@ -152,9 +145,8 @@ describe('findMatchingIngresses', () => {
152
145
  spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service2' } } }] } }] }
153
146
  }
154
147
  ],
155
- matchingServices: [{ metadata: { name: 'service1' } }],
156
148
  matchingIngresses: [],
157
- value: { metadata: { namespace: 'test' } }
149
+ value: { metadata: { namespace: 'test' }, relatedServices: [{ metadata: { name: 'service1' } }] }
158
150
  };
159
151
 
160
152
  findMatchingIngresses.call(mockThis);
@@ -53,6 +53,11 @@ export default {
53
53
  },
54
54
 
55
55
  valuesYaml() {
56
+ // Prevent crash if data hasn't been fetched yet (e.g. secret during upgrade)
57
+ if (!this.value?.valuesLoaded) {
58
+ return '';
59
+ }
60
+
56
61
  const combined = mergeWithReplace(
57
62
  merge({}, this.value?.chartValues || {}),
58
63
  this.value?.values || {},
@@ -159,6 +164,7 @@ export default {
159
164
  :scrolling="false"
160
165
  :value="valuesYaml"
161
166
  editor-mode="VIEW_CODE"
167
+ mode="view"
162
168
  />
163
169
  </Tab>
164
170
  <Tab
@@ -59,6 +59,12 @@ export default {
59
59
  };
60
60
  },
61
61
 
62
+ created() {
63
+ if (this.workspace !== this.value.namespace) {
64
+ this.$store.commit('updateWorkspace', { value: this.value.namespace, getters: this.$store.getters });
65
+ }
66
+ },
67
+
62
68
  computed: {
63
69
  clusterId() {
64
70
  return this.value.id;
@@ -2,22 +2,18 @@
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, NAMESPACE, WORKLOAD_TYPE_TO_KIND_MAPPING, METRICS_SUPPORTED_KINDS
5
+ POD, WORKLOAD_TYPES, SERVICE, INGRESS, NODE, NAMESPACE, WORKLOAD_TYPE_TO_KIND_MAPPING, METRICS_SUPPORTED_KINDS
6
6
  } from '@shell/config/types';
7
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';
11
- import CountGauge from '@shell/components/CountGauge';
12
11
  import { allHash } from '@shell/utils/promise';
13
12
  import DashboardMetrics from '@shell/components/DashboardMetrics';
14
13
  import { mapGetters } from 'vuex';
15
14
  import { allDashboardsExist } from '@shell/utils/grafana';
16
- import PlusMinus from '@shell/components/form/PlusMinus';
17
- import { matches } from '@shell/utils/selector';
18
15
  import { PROJECT } from '@shell/config/labels-annotations';
19
16
 
20
- const SCALABLE_TYPES = Object.values(SCALABLE_WORKLOAD_TYPES);
21
17
  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';
22
18
  const WORKLOAD_METRICS_SUMMARY_URL = '/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-grafana:80/proxy/d/rancher-workload-1/rancher-workload?orgId=1';
23
19
 
@@ -27,9 +23,7 @@ export default {
27
23
  Tab,
28
24
  Loading,
29
25
  ResourceTabs,
30
- CountGauge,
31
26
  ResourceTable,
32
- PlusMinus
33
27
  },
34
28
 
35
29
  mixins: [CreateEditView],
@@ -57,10 +51,6 @@ export default {
57
51
  hash.pods = this.value.fetchPods();
58
52
  }
59
53
 
60
- if (this.serviceSchema) {
61
- hash.servicesInNamespace = this.$store.dispatch('cluster/findAll', { type: SERVICE, opt: { namespaced: this.value.metadata.namespace } });
62
- }
63
-
64
54
  if (this.value.type === WORKLOAD_TYPES.CRON_JOB) {
65
55
  hash.jobs = this.value.matchingJobs();
66
56
  }
@@ -85,7 +75,6 @@ export default {
85
75
  this.showProjectMetrics = await allDashboardsExist(this.$store, this.currentCluster.id, [this.WORKLOAD_PROJECT_METRICS_DETAIL_URL, this.WORKLOAD_PROJECT_METRICS_SUMMARY_URL], 'cluster', projectId);
86
76
  }
87
77
  }
88
- this.findMatchingServices();
89
78
  this.findMatchingIngresses();
90
79
  },
91
80
 
@@ -97,9 +86,7 @@ export default {
97
86
 
98
87
  data() {
99
88
  return {
100
- servicesInNamespace: [],
101
89
  allIngresses: [],
102
- matchingServices: [],
103
90
  matchingIngresses: [],
104
91
  allNodes: [],
105
92
  WORKLOAD_METRICS_DETAIL_URL,
@@ -223,55 +210,10 @@ export default {
223
210
  const total = this.isCronJob ? this.totalRuns : this.value.pods.length;
224
211
 
225
212
  return !jobGauges.find((jg) => jg.count === total);
226
- },
227
-
228
- canScale() {
229
- return !!SCALABLE_TYPES.includes(this.value.type) && this.value.canUpdate;
230
- },
213
+ }
231
214
  },
232
215
 
233
216
  methods: {
234
- async scale(isUp) {
235
- try {
236
- if (isUp) {
237
- await this.value.scaleUp();
238
- } else {
239
- await this.value.scaleDown();
240
- }
241
- } catch (err) {
242
- this.$store.dispatch('growl/fromError', {
243
- title: this.t('workload.list.errorCannotScale', { direction: isUp ? 'up' : 'down', workloadName: this.value.name }),
244
- err
245
- },
246
- { root: true });
247
- }
248
- },
249
- async scaleDown() {
250
- await this.scale(false);
251
- },
252
- async scaleUp() {
253
- await this.scale(true);
254
- },
255
- findMatchingServices() {
256
- if (!this.serviceSchema) {
257
- return [];
258
- }
259
-
260
- // Find Services that have selectors that match this workload's Pod(s).
261
- this.matchingServices = this.servicesInNamespace.filter((service) => {
262
- const selector = service.spec.selector;
263
-
264
- for (let i = 0; i < this.value.pods.length; i++) {
265
- const pod = this.value.pods[i];
266
-
267
- if (service.metadata?.namespace === this.value.metadata?.namespace && matches(pod, selector)) {
268
- return true;
269
- }
270
- }
271
-
272
- return false;
273
- });
274
- },
275
217
  findMatchingIngresses() {
276
218
  if (!this.ingressSchema) {
277
219
  return [];
@@ -297,8 +239,8 @@ export default {
297
239
 
298
240
  if (!targetServiceName) continue;
299
241
 
300
- for (let k = 0; k < this.matchingServices.length; k++) {
301
- const service = this.matchingServices[k];
242
+ for (let k = 0; k < this.value.relatedServices.length; k++) {
243
+ const service = this.value.relatedServices[k];
302
244
  const matchingServiceName = service?.metadata?.name;
303
245
 
304
246
  if (ingress.metadata?.namespace === this.value.metadata?.namespace && matchingServiceName === targetServiceName) {
@@ -331,50 +273,6 @@ export default {
331
273
  <template>
332
274
  <Loading v-if="$fetchState.pending" />
333
275
  <div v-else>
334
- <div
335
- v-if="canScale"
336
- class="right-align flex"
337
- >
338
- <PlusMinus
339
- class="text-right"
340
- :label="t('tableHeaders.scale')"
341
- :value="value.spec.replicas"
342
- :disabled="!isScalable"
343
- @minus="scaleDown"
344
- @plus="scaleUp"
345
- />
346
- </div>
347
- <h3>
348
- {{ isJob || isCronJob ? t('workload.detailTop.runs') :t('workload.detailTop.pods') }}
349
- </h3>
350
- <div
351
- v-if="value.pods || value.jobGauges"
352
- class="gauges mb-20"
353
- :class="{'gauges__pods': !!value.pods}"
354
- >
355
- <template v-if="value.jobGauges">
356
- <CountGauge
357
- v-for="(group, key) in value.jobGauges"
358
- :key="key"
359
- :total="isCronJob? totalRuns : value.pods.length"
360
- :useful="group.count || 0"
361
- :graphical="showJobGaugeCircles"
362
- :primary-color-var="`--sizzle-${group.color}`"
363
- :name="t(`workload.gaugeStates.${key}`)"
364
- />
365
- </template>
366
- <template v-else>
367
- <CountGauge
368
- v-for="(group, key) in podGauges"
369
- :key="key"
370
- :total="value.pods.length"
371
- :useful="group.count || 0"
372
- :graphical="showPodGaugeCircles"
373
- :primary-color-var="`--sizzle-${group.color}`"
374
- :name="key"
375
- />
376
- </template>
377
- </div>
378
276
  <ResourceTabs
379
277
  :value="value"
380
278
  >
@@ -455,7 +353,7 @@ export default {
455
353
  {{ t('workload.detail.cannotViewServices') }}
456
354
  </p>
457
355
  <p
458
- v-else-if="matchingServices.length === 0"
356
+ v-else-if="value.relatedServices.length === 0"
459
357
  class="caption"
460
358
  >
461
359
  {{ t('workload.detail.cannotFindServices') }}
@@ -467,8 +365,8 @@ export default {
467
365
  {{ t('workload.detail.serviceListCaption') }}
468
366
  </p>
469
367
  <ResourceTable
470
- v-if="serviceSchema && matchingServices.length > 0"
471
- :rows="matchingServices"
368
+ v-if="serviceSchema && value.relatedServices.length > 0"
369
+ :rows="value.relatedServices"
472
370
  :headers="serviceHeaders"
473
371
  key-field="id"
474
372
  :schema="serviceSchema"
@@ -0,0 +1,42 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import ProjectSecretProxy from '@shell/edit/projectsecret.vue';
3
+ import Secret from '@shell/edit/secret/index.vue';
4
+
5
+ describe('component: ProjectSecretProxy', () => {
6
+ it('should render Secret component', () => {
7
+ const wrapper = shallowMount(ProjectSecretProxy, {
8
+ global: { stubs: { Secret: true } },
9
+ attrs: { someAttr: 'someValue' }
10
+ });
11
+
12
+ const secretComponent = wrapper.findComponent(Secret);
13
+
14
+ expect(secretComponent.exists()).toBe(true);
15
+ });
16
+
17
+ it('should pass attributes to Secret component', () => {
18
+ const wrapper = shallowMount(ProjectSecretProxy, {
19
+ global: { stubs: { Secret: true } },
20
+ attrs: {
21
+ mode: 'create',
22
+ value: { metadata: { name: 'test' } }
23
+ }
24
+ });
25
+
26
+ const secretComponent = wrapper.findComponent(Secret);
27
+
28
+ expect(secretComponent.attributes('mode')).toBe('create');
29
+ });
30
+
31
+ it('should pass event listeners to Secret component', () => {
32
+ const onInput = jest.fn();
33
+ const wrapper = shallowMount(ProjectSecretProxy, {
34
+ global: { stubs: { Secret: true } },
35
+ attrs: { onInput }
36
+ });
37
+
38
+ const secretComponent = wrapper.findComponent(Secret);
39
+
40
+ expect(secretComponent.vm.$attrs.onInput).toBe(onInput);
41
+ });
42
+ });
@@ -247,5 +247,55 @@ describe('oidc.vue', () => {
247
247
  expect(groupsClaim.exists()).toBe(true);
248
248
  expect(emailClaim.exists()).toBe(true);
249
249
  });
250
+
251
+ it('should render addCustomClaims and supportsGroupSearch checkbox when provider is keycloak', async() => {
252
+ wrapper.vm.model.id = 'keycloakoidc';
253
+ await nextTick();
254
+
255
+ const addCustomClaimsCheckbox = wrapper.find('[data-testid="input-add-custom-claims"]');
256
+ const groupSearchCheckbox = wrapper.find('[data-testid="input-group-search"]');
257
+
258
+ expect(addCustomClaimsCheckbox.exists()).toBe(true);
259
+ expect(groupSearchCheckbox.exists()).toBe(true);
260
+ });
261
+
262
+ it('should render custom claims section when provider is keycloak and addCustomClaims is true', async() => {
263
+ wrapper.vm.model.id = 'keycloakoidc';
264
+ wrapper.vm.addCustomClaims = true;
265
+ await nextTick();
266
+
267
+ const nameClaim = wrapper.find('[data-testid="input-name-claim"]');
268
+ const groupsClaim = wrapper.find('[data-testid="input-groups-claim"]');
269
+ const emailClaim = wrapper.find('[data-testid="input-email-claim"]');
270
+
271
+ expect(nameClaim.exists()).toBe(true);
272
+ expect(groupsClaim.exists()).toBe(true);
273
+ expect(emailClaim.exists()).toBe(true);
274
+ });
275
+
276
+ it('should render both addCustomClaims and groupSearch checkboxes when provider is genericoidc', async() => {
277
+ wrapper.vm.model.id = 'genericoidc';
278
+ await nextTick();
279
+
280
+ const addCustomClaimsCheckbox = wrapper.find('[data-testid="input-add-custom-claims"]');
281
+ const groupSearchCheckbox = wrapper.find('[data-testid="input-group-search"]');
282
+
283
+ expect(addCustomClaimsCheckbox.exists()).toBe(true);
284
+ expect(groupSearchCheckbox.exists()).toBe(true);
285
+ });
286
+
287
+ it('should NOT render custom claims section when provider is keycloak and addCustomClaims is false', async() => {
288
+ wrapper.vm.model.id = 'keycloakoidc';
289
+ wrapper.vm.addCustomClaims = false;
290
+ await nextTick();
291
+
292
+ const nameClaim = wrapper.find('[data-testid="input-name-claim"]');
293
+ const groupsClaim = wrapper.find('[data-testid="input-groups-claim"]');
294
+ const emailClaim = wrapper.find('[data-testid="input-email-claim"]');
295
+
296
+ expect(nameClaim.exists()).toBe(false);
297
+ expect(groupsClaim.exists()).toBe(false);
298
+ expect(emailClaim.exists()).toBe(false);
299
+ });
250
300
  });
251
301
  });
@@ -17,6 +17,8 @@ import { BASE_SCOPES } from '@shell/store/auth';
17
17
  import CopyToClipboardText from '@shell/components/CopyToClipboardText.vue';
18
18
  import isUrl from 'is-url';
19
19
 
20
+ const PKCE_S256 = 'S256';
21
+
20
22
  export default {
21
23
  components: {
22
24
  Banner,
@@ -38,6 +40,10 @@ export default {
38
40
 
39
41
  mixins: [CreateEditView, AuthConfig],
40
42
 
43
+ setup() {
44
+ return { PKCE_S256 };
45
+ },
46
+
41
47
  data() {
42
48
  return {
43
49
  customEndpoint: {
@@ -149,6 +155,14 @@ export default {
149
155
  return this.model?.id === 'genericoidc';
150
156
  },
151
157
 
158
+ isKeycloak() {
159
+ return this.model?.id === 'keycloakoidc';
160
+ },
161
+
162
+ supportsCustomClaims() {
163
+ return this.isGenericOidc || this.isKeycloak;
164
+ },
165
+
152
166
  isLogoutAllSupported() {
153
167
  return this.model?.logoutAllSupported;
154
168
  },
@@ -169,7 +183,7 @@ export default {
169
183
 
170
184
  sloEndSessionEndpointUiEnabled() {
171
185
  return this.sloType === SLO_OPTION_VALUES.all || this.sloType === SLO_OPTION_VALUES.both;
172
- }
186
+ },
173
187
  },
174
188
 
175
189
  watch: {
@@ -269,7 +283,7 @@ export default {
269
283
  },
270
284
 
271
285
  willSave() {
272
- if (this.isGenericOidc && !this.addCustomClaims) {
286
+ if (this.supportsCustomClaims && !this.addCustomClaims) {
273
287
  this.model.nameClaim = undefined;
274
288
  this.model.groupsClaim = undefined;
275
289
  this.model.emailClaim = undefined;
@@ -310,6 +324,9 @@ export default {
310
324
  <tr v-if="model.authEndpoint">
311
325
  <td>{{ t('authConfig.oidc.authEndpoint') }}: </td><td>{{ model.authEndpoint }}</td>
312
326
  </tr>
327
+ <tr v-if="model.pkceMethod">
328
+ <td>{{ t('authConfig.oidc.pkceMethod.label') }}: </td><td>{{ model.pkceMethod }}</td>
329
+ </tr>
313
330
  <tr v-if="isLogoutAllSupported">
314
331
  <td>{{ t('authConfig.slo.sloTitle') }}: </td><td>{{ sloTypeText }}</td>
315
332
  </tr>
@@ -416,34 +433,38 @@ export default {
416
433
  </div>
417
434
  </div>
418
435
 
419
- <template v-if="isGenericOidc || supportsGroupSearch">
420
- <div
421
- class="row mb-20"
422
- >
423
- <div class="col span-6 checkbox-flex">
424
- <!-- Allow group search -->
425
- <Checkbox
426
- v-if="supportsGroupSearch"
427
- v-model:value="model.groupSearchEnabled"
428
- data-testid="input-group-search"
429
- :label="t('authConfig.oidc.groupSearch.label')"
430
- :tooltip="t('authConfig.oidc.groupSearch.tooltip')"
431
- :mode="mode"
432
- />
433
- <Checkbox
434
- v-if="isGenericOidc"
435
- v-model:value="addCustomClaims"
436
- data-testid="input-add-custom-claims"
437
- :label="t('authConfig.oidc.customClaims.enable.label')"
438
- :tooltip="t('authConfig.oidc.customClaims.enable.tooltip')"
439
- :mode="mode"
440
- />
441
- </div>
436
+ <div
437
+ class="row mb-20"
438
+ >
439
+ <div class="col span-6 checkbox-flex">
440
+ <!-- Allow group search -->
441
+ <Checkbox
442
+ v-model:value="model.pkceMethod"
443
+ :value-when-true="PKCE_S256"
444
+ :label="t('authConfig.oidc.pkce.label')"
445
+ :tooltip="t('authConfig.oidc.pkce.tooltip')"
446
+ />
447
+ <Checkbox
448
+ v-if="supportsGroupSearch"
449
+ v-model:value="model.groupSearchEnabled"
450
+ data-testid="input-group-search"
451
+ :label="t('authConfig.oidc.groupSearch.label')"
452
+ :tooltip="t('authConfig.oidc.groupSearch.tooltip')"
453
+ :mode="mode"
454
+ />
455
+ <Checkbox
456
+ v-if="supportsCustomClaims"
457
+ v-model:value="addCustomClaims"
458
+ data-testid="input-add-custom-claims"
459
+ :label="t('authConfig.oidc.customClaims.enable.label')"
460
+ :tooltip="t('authConfig.oidc.customClaims.enable.tooltip')"
461
+ :mode="mode"
462
+ />
442
463
  </div>
443
- </template>
464
+ </div>
444
465
 
445
466
  <!-- Custom Claims -->
446
- <template v-if="addCustomClaims && isGenericOidc">
467
+ <template v-if="addCustomClaims && supportsCustomClaims">
447
468
  <h4>{{ t('authConfig.oidc.customClaims.label') }}</h4>
448
469
  <div class="row mb-20">
449
470
  <div class="col span-6">
@@ -475,20 +496,6 @@ export default {
475
496
  </div>
476
497
  </template>
477
498
 
478
- <!-- Scopes -->
479
- <div class="row mb-20">
480
- <div class="col span-6">
481
- <ArrayList
482
- v-model:value="oidcScope"
483
- :mode="mode"
484
- :title="t('authConfig.oidc.scope.label')"
485
- :value-placeholder="t('authConfig.oidc.scope.placeholder')"
486
- :protip="t('authConfig.oidc.scope.protip', {}, true)"
487
- @update:value="updateScope"
488
- />
489
- </div>
490
- </div>
491
-
492
499
  <template v-if="!isAmazonCognito">
493
500
  <!-- Generated vs Specific Endpoints -->
494
501
  <div class="row mb-20">
@@ -546,7 +553,7 @@ export default {
546
553
  </div>
547
554
  </div>
548
555
 
549
- <div class="row mb-20">
556
+ <div class="row">
550
557
  <div class="col span-6">
551
558
  <LabeledInput
552
559
  v-model:value="model.issuer"
@@ -570,7 +577,10 @@ export default {
570
577
  </div>
571
578
 
572
579
  <!-- Advanced section -->
573
- <AdvancedSection :mode="mode">
580
+ <AdvancedSection
581
+ class="mb-40"
582
+ :mode="mode"
583
+ >
574
584
  <div class="row mb-20">
575
585
  <div class="col span-6">
576
586
  <LabeledInput
@@ -626,10 +636,24 @@ export default {
626
636
  </div>
627
637
  </template>
628
638
 
639
+ <!-- Scopes -->
640
+ <div class="row mb-20">
641
+ <div class="col span-6">
642
+ <ArrayList
643
+ v-model:value="oidcScope"
644
+ :mode="mode"
645
+ :title="t('authConfig.oidc.scope.label')"
646
+ :value-placeholder="t('authConfig.oidc.scope.placeholder')"
647
+ :protip="t('authConfig.oidc.scope.protip', {}, true)"
648
+ @update:value="updateScope"
649
+ />
650
+ </div>
651
+ </div>
652
+
629
653
  <!-- SLO logout -->
630
654
  <div
631
655
  v-if="isLogoutAllSupported"
632
- class="mt-40 mb-20"
656
+ class="mb-20"
633
657
  >
634
658
  <div class="row">
635
659
  <div class="col span-12">