@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
@@ -20,7 +20,7 @@ export default {
20
20
  'update:value', 'cluster-cidr-changed', 'local-cluster-auth-endpoint-changed',
21
21
  'service-cidr-changed', 'cluster-domain-changed', 'cluster-dns-changed',
22
22
  'truncate-hostname-changed', 'ca-certs-changed', 'service-node-port-range-changed',
23
- 'fqdn-changed', 'tls-san-changed', 'stack-preference-changed', 'validationChanged'
23
+ 'fqdn-changed', 'tls-san-changed', 'stack-preference-changed', 'validationChanged', 'enable-flannel-masq-changed'
24
24
  ],
25
25
 
26
26
  components: {
@@ -57,9 +57,31 @@ export default {
57
57
  hasSomeIpv6Pools: {
58
58
  type: Boolean,
59
59
  default: false
60
+ },
61
+
62
+ enableFlannelMasq: {
63
+ type: Boolean,
64
+ default: false
60
65
  }
61
66
  },
62
67
 
68
+ watch: {
69
+ isProbablyIPv6(neu) {
70
+ if (this.mode === _CREATE && this.showFlannelMasq) {
71
+ this.$emit('enable-flannel-masq-changed', neu);
72
+ }
73
+ },
74
+
75
+ isK3s(neu) {
76
+ if (!neu) {
77
+ this.$emit('enable-flannel-masq-changed', null);
78
+ } else if (this.isProbablyIPv6) {
79
+ this.$emit('enable-flannel-masq-changed', true);
80
+ }
81
+ },
82
+
83
+ },
84
+
63
85
  data() {
64
86
  return { STACK_PREFS };
65
87
  },
@@ -68,12 +90,15 @@ export default {
68
90
  truncateHostnames() {
69
91
  return this.truncateLimit === NETBIOS_TRUNCATION_LENGTH;
70
92
  },
93
+
71
94
  serverConfig() {
72
95
  return this.value.spec.rkeConfig.machineGlobalConfig;
73
96
  },
97
+
74
98
  serverArgs() {
75
99
  return this.selectedVersion?.serverArgs || {};
76
100
  },
101
+
77
102
  stackPreferenceOptions() {
78
103
  return [{
79
104
  label: this.t('cluster.rke2.stackPreference.options.ipv4'),
@@ -88,21 +113,19 @@ export default {
88
113
  },
89
114
  ];
90
115
  },
91
- showIpv6Warning() {
92
- const clusterCIDR = this.serverConfig['cluster-cidr'] || '';
93
- const serviceCIDR = this.serverConfig['service-cidr'] || '';
94
116
 
95
- return clusterCIDR.includes(':') || serviceCIDR.includes(':');
96
- },
97
117
  hostnameTruncationManuallySet() {
98
118
  return !!this.truncateLimit && this.truncateLimit !== NETBIOS_TRUNCATION_LENGTH;
99
119
  },
120
+
100
121
  isEdit() {
101
122
  return this.mode === _EDIT;
102
123
  },
124
+
103
125
  isView() {
104
126
  return this.mode === _VIEW;
105
127
  },
128
+
106
129
  localValue: {
107
130
  get() {
108
131
  return this.value;
@@ -123,6 +146,31 @@ export default {
123
146
  this.localValue.spec.networking.stackPreference = neu;
124
147
  }
125
148
  },
149
+
150
+ isK3s() {
151
+ return (this.selectedVersion?.label || '').includes('k3s');
152
+ },
153
+
154
+ showFlannelMasq() {
155
+ const flannelEnabled = this.value?.spec?.rkeConfig?.machineGlobalConfig?.['flannel-backend'] !== 'none';
156
+
157
+ return this.isK3s && flannelEnabled;
158
+ },
159
+
160
+ hasIpv6StackPref() {
161
+ return [STACK_PREFS.IPV6, STACK_PREFS.DUAL].includes(this.value?.spec?.rkeConfig?.networking?.stackPreference);
162
+ },
163
+
164
+ hasIpv6ServerConfig() {
165
+ const clusterCIDR = this.serverConfig['cluster-cidr'] || '';
166
+ const serviceCIDR = this.serverConfig['service-cidr'] || '';
167
+
168
+ return clusterCIDR.includes(':') || serviceCIDR.includes(':');
169
+ },
170
+
171
+ isProbablyIPv6() {
172
+ return this.hasSomeIpv6Pools || this.hasIpv6StackPref || this.hasIpv6ServerConfig;
173
+ },
126
174
  },
127
175
 
128
176
  methods: {
@@ -140,7 +188,7 @@ export default {
140
188
  } else {
141
189
  return null;
142
190
  }
143
- }
191
+ },
144
192
  }
145
193
  };
146
194
  </script>
@@ -154,13 +202,6 @@ export default {
154
202
  class="icon icon-info"
155
203
  />
156
204
  </h3>
157
- <Banner
158
- v-if="showIpv6Warning"
159
- color="warning"
160
- data-testid="network-tab-ipv6StackPreferenceWarning"
161
- >
162
- {{ t('cluster.rke2.address.ipv6.warning') }}
163
- </Banner>
164
205
  <div class="row mb-20">
165
206
  <div
166
207
  v-if="serverArgs['cluster-cidr']"
@@ -298,5 +339,32 @@ export default {
298
339
  />
299
340
  </div>
300
341
  </div>
342
+ <template v-if="showFlannelMasq">
343
+ <h3
344
+ v-t="'cluster.k3s.flannelMasq.title'"
345
+ class="mt-20"
346
+ />
347
+ <Banner
348
+ v-if="isProbablyIPv6"
349
+ color="warning"
350
+ data-testid="cluster-rke2-flannel-masq-banner"
351
+ :label="t('cluster.k3s.flannelMasq.banner', null, true)"
352
+ />
353
+ <div
354
+ class="row mb-20 "
355
+ >
356
+ <div
357
+ class="col"
358
+ >
359
+ <Checkbox
360
+ :value="enableFlannelMasq"
361
+ data-testid="cluster-rke2-flannel-masq-checkbox"
362
+ :mode="mode"
363
+ :label="t('cluster.k3s.flannelMasq.label')"
364
+ @update:value="e=>$emit('enable-flannel-masq-changed', e)"
365
+ />
366
+ </div>
367
+ </div>
368
+ </template>
301
369
  </div>
302
370
  </template>
@@ -4,100 +4,137 @@ import Workload from '@shell/edit/workload/index.vue';
4
4
  jest.mock('@shell/models/secret', () => ({ onmessage: jest.fn() }));
5
5
 
6
6
  describe('component: Workload', () => {
7
- it.each([
8
- [
9
- `pods \"test\" is forbidden: violates PodSecurity \"restricted:latest\": allowPrivilegeEscalation != false (container \"container-0\" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container \"container-0\" must set securityContext.capabilities.drop=[\"ALL\"]), runAsNonRoot != true (container \"container-0\" must not set securityContext.runAsNonRoot=false), seccompProfile (pod or container \"container-0\" must set securityContext.seccompProfile.type to \"RuntimeDefault\" or \"Localhost\")`,
10
- `workload.error, \"test\",\"restricted:latest\"`
11
- ]
12
- ])('should map error message into object', (oldMessage, newMessage) => {
13
- const mockedValidationMixin = {
14
- methods: {
15
- fvFormIsValid: jest.fn(),
16
- type: jest.fn(),
17
- fvGetAndReportPathRules: jest.fn(),
18
- },
19
- computed: { fvUnreportedValidationErrors: jest.fn().mockReturnValue([]) }
20
- };
21
- const mockedCREMixin = {};
22
- const mockedWorkloadMixin = {
23
- methods: {
24
- doneRoute: jest.fn(),
25
- workloadSubTypes: jest.fn(),
26
- applyHooks: jest.fn(),
27
- save: jest.fn(),
28
- selectType: jest.fn(),
29
- isCronJob: jest.fn(),
30
- spec: jest.fn(),
31
- isReplicable: jest.fn(),
32
- isStatefulSet: jest.fn(),
33
- headlessServices: jest.fn(),
34
- defaultTab: jest.fn(),
35
- allContainers: jest.fn(),
36
- isPod: jest.fn(),
37
- tabWeightMap: jest.fn(),
38
- podLabels: jest.fn(),
39
- podTemplateSpec: jest.fn(),
40
- isLoadingSecondaryResources: jest.fn(),
41
- allNodes: jest.fn(),
42
- allNodeObjects: jest.fn(),
43
- clearPvcFormState: jest.fn(),
44
- savePvcHookName: jest.fn(),
45
- namespacedConfigMaps: jest.fn(),
46
- podAnnotations: jest.fn(),
47
- isJob: jest.fn(),
48
- podFsGroup: jest.fn(),
49
- namespacedSecrets: jest.fn(),
50
- registerBeforeHook: jest.fn(),
51
- pvcs: jest.fn(),
52
- // tabWeightMap: jest.fn(),
53
- }
54
- };
7
+ const baseMockedValidationMixin = {
8
+ methods: {
9
+ fvFormIsValid: jest.fn(),
10
+ type: jest.fn(),
11
+ fvGetAndReportPathRules: jest.fn(),
12
+ },
13
+ computed: { fvUnreportedValidationErrors: jest.fn().mockReturnValue([]) }
14
+ };
15
+ const baseMockedCREMixin = {};
16
+ const baseMockedWorkloadMixin = {
17
+ methods: {
18
+ doneRoute: jest.fn(),
19
+ workloadSubTypes: jest.fn(),
20
+ applyHooks: jest.fn(),
21
+ save: jest.fn(),
22
+ selectType: jest.fn(),
23
+ isCronJob: jest.fn(),
24
+ spec: jest.fn(),
25
+ isReplicable: jest.fn(),
26
+ isStatefulSet: jest.fn(),
27
+ headlessServices: jest.fn(),
28
+ defaultTab: jest.fn(),
29
+ isPod: jest.fn(),
30
+ tabWeightMap: jest.fn(),
31
+ podLabels: jest.fn(),
32
+ podTemplateSpec: jest.fn(),
33
+ isLoadingSecondaryResources: jest.fn(),
34
+ allNodes: jest.fn(),
35
+ clearPvcFormState: jest.fn(),
36
+ savePvcHookName: jest.fn(),
37
+ namespacedConfigMaps: jest.fn(),
38
+ podAnnotations: jest.fn(),
39
+ isJob: jest.fn(),
40
+ namespacedSecrets: jest.fn(),
41
+ registerBeforeHook: jest.fn(),
42
+ pvcs: jest.fn(),
43
+ },
44
+ computed: { allContainers: jest.fn(() => []) }
45
+ };
55
46
 
56
- const MockedWorkload = { ...Workload, mixins: [mockedValidationMixin, mockedCREMixin, mockedWorkloadMixin] };
57
- const wrapper = shallowMount(MockedWorkload, {
58
- props: {
59
- value: { metadata: {}, spec: { template: {} } },
60
- params: {},
61
- fvFormIsValid: {}
62
- },
47
+ describe('component: Workload', () => {
48
+ it.each([
49
+ [
50
+ `pods \"test\" is forbidden: violates PodSecurity \"restricted:latest\": allowPrivilegeEscalation != false (container \"container-0\" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container \"container-0\" must set securityContext.capabilities.drop=[\"ALL\"]), runAsNonRoot != true (container \"container-0\" must not set securityContext.runAsNonRoot=false), seccompProfile (pod or container \"container-0\" must set securityContext.seccompProfile.type to \"RuntimeDefault\" or \"Localhost\")`,
51
+ `workload.error, \"test\",\"restricted:latest\"`
52
+ ]
53
+ ])('should map error message into object', (oldMessage, newMessage) => {
54
+ // For this test, allNodeObjects is just a jest.fn() in the base mixin
55
+ const MockedWorkload = { ...Workload, mixins: [baseMockedValidationMixin, baseMockedCREMixin, { ...baseMockedWorkloadMixin, computed: { ...baseMockedWorkloadMixin.computed, allNodeObjects: jest.fn() } }] };
56
+ const wrapper = shallowMount(MockedWorkload, {
57
+ props: {
58
+ value: { metadata: {}, spec: { template: {} } },
59
+ params: {},
60
+ fvFormIsValid: {}
61
+ },
63
62
 
64
- global: {
65
- mocks: {
66
- $route: { params: {}, query: {} },
67
- $router: { applyQuery: jest.fn() },
68
- $fetchState: { pending: false },
69
- $store: {
70
- getters: {
71
- 'cluster/schemaFor': jest.fn(),
72
- 'type-map/labelFor': jest.fn(),
73
- 'i18n/t': (text: string, v: {[key:string]: string}) => {
74
- return `${ text }, ${ Object.values(v || {}) }`;
63
+ global: {
64
+ mocks: {
65
+ $route: { params: {}, query: {} },
66
+ $router: { applyQuery: jest.fn() },
67
+ $fetchState: { pending: false },
68
+ $store: {
69
+ getters: {
70
+ 'cluster/schemaFor': jest.fn(),
71
+ 'type-map/labelFor': jest.fn(),
72
+ 'i18n/t': (text: string, v: {[key:string]: string}) => {
73
+ return `${ text }, ${ Object.values(v || {}) }`;
74
+ },
75
75
  },
76
76
  },
77
77
  },
78
- },
79
78
 
80
- stubs: {
81
- Tab: true,
82
- LabeledInput: true,
83
- VolumeClaimTemplate: true,
84
- Networking: true,
85
- Job: true,
86
- NodeScheduling: true,
87
- PodAffinity: true,
88
- Tolerations: true,
89
- Storage: true,
90
- Tabbed: true,
91
- LabeledSelect: true,
92
- NameNsDescription: true,
93
- CruResource: true,
94
- KeyValue: true
79
+ stubs: {
80
+ Tab: true,
81
+ LabeledInput: true,
82
+ VolumeClaimTemplate: true,
83
+ Networking: true,
84
+ Job: true,
85
+ NodeScheduling: true,
86
+ PodAffinity: true,
87
+ Tolerations: true,
88
+ Storage: true,
89
+ Tabbed: true,
90
+ LabeledSelect: true,
91
+ NameNsDescription: true,
92
+ CruResource: true,
93
+ KeyValue: true
94
+ },
95
95
  },
96
- },
96
+ });
97
+
98
+ const result = (wrapper.vm as any).mapError(oldMessage).message;
99
+
100
+ expect(result).toStrictEqual(newMessage);
97
101
  });
98
102
 
99
- const result = (wrapper.vm as any).mapError(oldMessage).message;
103
+ describe('secondaryResourceDataConfig', () => {
104
+ it('should filter out nodes with control-plane or etcd taints from workerNodes parsingFunc', () => {
105
+ const allNodeObjects = [
106
+ {
107
+ id: 'node-1',
108
+ spec: { taints: [{ key: 'node-role.kubernetes.io/control-plane', effect: 'NoSchedule' }] }
109
+ },
110
+ {
111
+ id: 'node-2',
112
+ spec: { taints: [{ key: 'node-role.kubernetes.io/etcd', effect: 'NoSchedule' }] }
113
+ },
114
+ {
115
+ id: 'node-3',
116
+ spec: { taints: [{ key: 'node-role.kubernetes.io/worker', effect: 'NoSchedule' }] }
117
+ },
118
+ {
119
+ id: 'node-4',
120
+ spec: { taints: [] }
121
+ },
122
+ {
123
+ id: 'node-5',
124
+ spec: {}
125
+ },
126
+ {
127
+ id: 'node-6',
128
+ spec: null
129
+ }
130
+ ];
131
+
132
+ const { data } = (Workload.mixins[2] as any).methods.secondaryResourceDataConfig.apply({ value: { metadata: { namespace: 'test' } } });
133
+ const workerNodesParsingFunc = data.node.applyTo.find((r: any) => r.var === 'workerNodes').parsingFunc;
134
+ const result = workerNodesParsingFunc(allNodeObjects);
100
135
 
101
- expect(result).toStrictEqual(newMessage);
136
+ expect(result).toStrictEqual(['node-3', 'node-4', 'node-5', 'node-6']);
137
+ });
138
+ });
102
139
  });
103
140
  });
@@ -3,6 +3,7 @@ import CreateEditView from '@shell/mixins/create-edit-view';
3
3
  import FormValidation from '@shell/mixins/form-validation';
4
4
  import WorkLoadMixin from '@shell/edit/workload/mixins/workload';
5
5
  import { mapGetters } from 'vuex';
6
+ import { FORM_TYPES } from '@shell/components/form/Security';
6
7
 
7
8
  export default {
8
9
  name: 'Workload',
@@ -20,10 +21,22 @@ export default {
20
21
  },
21
22
  },
22
23
  data() {
23
- return { selectedName: null, errors: [] };
24
+ return {
25
+ selectedName: null,
26
+ errors: [],
27
+ FORM_TYPES
28
+ };
24
29
  },
25
- computed: { ...mapGetters({ t: 'i18n/t' }) },
26
- methods: {
30
+ computed: {
31
+ ...mapGetters({ t: 'i18n/t' }),
32
+
33
+ isFormValid() {
34
+ const hasContainerErrors = this.allContainers.some(this.hasContainerError);
35
+
36
+ return this.fvFormIsValid && !hasContainerErrors;
37
+ }
38
+ },
39
+ methods: {
27
40
  changed(tab) {
28
41
  const key = this.idKey;
29
42
 
@@ -35,6 +48,10 @@ export default {
35
48
  }
36
49
  },
37
50
 
51
+ hasContainerError(tab) {
52
+ return Object.values(tab.error || {}).some((error) => !!error);
53
+ },
54
+
38
55
  /**
39
56
  * Find error exceptions to be mapped for each case
40
57
  */
@@ -81,7 +98,7 @@ export default {
81
98
  class="filled-height"
82
99
  >
83
100
  <CruResource
84
- :validation-passed="fvFormIsValid"
101
+ :validation-passed="isFormValid"
85
102
  :selected-subtype="type"
86
103
  :resource="value"
87
104
  :mode="mode"
@@ -165,9 +182,10 @@ export default {
165
182
  :label="tab.name"
166
183
  :name="tab[idKey]"
167
184
  :weight="tab.weight"
168
- :error="!!tab.error"
185
+ :error="hasContainerError(tab)"
169
186
  >
170
187
  <Tabbed
188
+ name="containerTabs"
171
189
  :side-tabs="true"
172
190
  :weight="99"
173
191
  :data-testid="`workload-container-tabs-${i}`"
@@ -177,7 +195,7 @@ export default {
177
195
  :label="t('workload.container.titles.general')"
178
196
  name="general"
179
197
  :weight="tabWeightMap['general']"
180
- :error="tabErrors.general"
198
+ :error="!!tab.error.general"
181
199
  >
182
200
  <template
183
201
  #tab-header-right
@@ -210,6 +228,7 @@ export default {
210
228
  name="initContainer"
211
229
  :options="[true, false]"
212
230
  :labels="[t('workload.container.init'), t('workload.container.standard')]"
231
+ :aria-label="t('workload.container.initContainer.label')"
213
232
  @update:value="updateInitContainer($event, allContainers[i])"
214
233
  />
215
234
  </div>
@@ -222,7 +241,6 @@ export default {
222
241
  :mode="mode"
223
242
  :label="t('workload.container.image')"
224
243
  :placeholder="t('generic.placeholder', {text: 'nginx:latest'}, true)"
225
- :rules="fvGetAndReportPathRules('image')"
226
244
  />
227
245
  </div>
228
246
  <div class="col span-6">
@@ -334,10 +352,14 @@ export default {
334
352
  :label="t('workload.container.titles.securityContext')"
335
353
  name="securityContext"
336
354
  :weight="tabWeightMap['securityContext']"
355
+ :error="!!tab.error.localhostProfile"
337
356
  >
338
357
  <Security
358
+ ref="security"
339
359
  v-model:value="allContainers[i].securityContext"
340
360
  :mode="mode"
361
+ :seccomp-profile-types="seccompProfileTypes"
362
+ :form-type="FORM_TYPES.CONTAINER"
341
363
  />
342
364
  </Tab>
343
365
  <Tab
@@ -406,8 +428,10 @@ export default {
406
428
  :label="t('workload.tabs.labels.pod')"
407
429
  :name="'pod'"
408
430
  :weight="98"
431
+ :error="tabErrors.podSecurityContext"
409
432
  >
410
433
  <Tabbed
434
+ name="podTabs"
411
435
  data-testid="workload-pod-tabs"
412
436
  :side-tabs="true"
413
437
  :use-hash="useTabbedHash"
@@ -434,7 +458,7 @@ export default {
434
458
  </Tab>
435
459
  <Tab
436
460
  :label="t('workload.container.titles.resources')"
437
- name="resources"
461
+ name="resources-pod"
438
462
  :weight="tabWeightMap['resources']"
439
463
  >
440
464
  <div>
@@ -476,7 +500,7 @@ export default {
476
500
  </Tab>
477
501
  <Tab
478
502
  :label="t('workload.container.titles.podScheduling')"
479
- name="podScheduling"
503
+ name="podScheduling-pod"
480
504
  :weight="tabWeightMap['podScheduling']"
481
505
  >
482
506
  <PodAffinity
@@ -488,19 +512,19 @@ export default {
488
512
  </Tab>
489
513
  <Tab
490
514
  :label="t('workload.container.titles.nodeScheduling')"
491
- name="nodeScheduling"
515
+ name="nodeScheduling-pod"
492
516
  :weight="tabWeightMap['nodeScheduling']"
493
517
  >
494
518
  <NodeScheduling
495
519
  :mode="mode"
496
520
  :value="podTemplateSpec"
497
- :nodes="allNodes"
521
+ :nodes="workerNodes"
498
522
  :loading="isLoadingSecondaryResources"
499
523
  />
500
524
  </Tab>
501
525
  <Tab
502
526
  :label="t('workload.container.titles.upgrading')"
503
- name="upgrading"
527
+ name="upgrading-pod"
504
528
  :weight="tabWeightMap['upgrading']"
505
529
  >
506
530
  <Job
@@ -519,26 +543,21 @@ export default {
519
543
  </Tab>
520
544
  <Tab
521
545
  :label="t('workload.container.titles.securityContext')"
522
- name="securityContext"
546
+ name="securityContext-pod"
523
547
  :weight="tabWeightMap['securityContext']"
548
+ :error="tabErrors.podSecurityContext"
524
549
  >
525
- <div>
526
- <h3>{{ t('workload.container.security.podFsGroup') }}</h3>
527
- <div class="row">
528
- <div class="col span-6">
529
- <LabeledInput
530
- v-model:value.number="podFsGroup"
531
- type="number"
532
- :mode="mode"
533
- :label="t('workload.container.security.fsGroup')"
534
- />
535
- </div>
536
- </div>
537
- </div>
550
+ <Security
551
+ ref="security"
552
+ v-model:value="podTemplateSpec.securityContext"
553
+ :mode="mode"
554
+ :seccomp-profile-types="seccompProfileTypes"
555
+ :form-type="FORM_TYPES.POD"
556
+ />
538
557
  </Tab>
539
558
  <Tab
540
559
  :label="t('workload.container.titles.networking')"
541
- name="networking"
560
+ name="networking-pod"
542
561
  :weight="tabWeightMap['networking']"
543
562
  >
544
563
  <Networking
@@ -549,7 +568,7 @@ export default {
549
568
  <Tab
550
569
  v-if="isStatefulSet"
551
570
  :label="t('workload.container.titles.volumeClaimTemplates')"
552
- name="volumeClaimTemplates"
571
+ name="volumeClaimTemplates-pod"
553
572
  :weight="tabWeightMap['volumeClaimTemplates']"
554
573
  >
555
574
  <VolumeClaimTemplate
@@ -558,7 +577,7 @@ export default {
558
577
  />
559
578
  </Tab>
560
579
  <Tab
561
- name="labels"
580
+ name="labels-pod"
562
581
  label-key="generic.labelsAndAnnotations"
563
582
  :weight="tabWeightMap['labels']"
564
583
  >