@rancher/shell 3.0.5-rc.1 → 3.0.5-rc.3

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 (280) hide show
  1. package/assets/data/aws-regions.json +2 -0
  2. package/assets/images/providers/sks.svg +1 -0
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +1 -0
  5. package/assets/styles/global/_layout.scss +0 -1
  6. package/assets/translations/en-us.yaml +92 -34
  7. package/assets/translations/zh-hans.yaml +4 -13
  8. package/chart/monitoring/index.vue +4 -2
  9. package/components/ActionDropdownShell.vue +71 -0
  10. package/components/AppModal.vue +18 -4
  11. package/components/AsyncButton.vue +2 -0
  12. package/components/CodeMirror.vue +3 -3
  13. package/components/CommunityLinks.vue +3 -58
  14. package/components/CruResource.vue +109 -16
  15. package/components/ExplorerProjectsNamespaces.vue +19 -6
  16. package/components/FixedBanner.vue +19 -5
  17. package/components/GlobalRoleBindings.vue +5 -1
  18. package/components/GrowlManager.vue +1 -0
  19. package/components/LandingPagePreference.vue +2 -0
  20. package/components/LocaleSelector.vue +1 -1
  21. package/components/ModalManager.vue +55 -0
  22. package/components/PaginatedResourceTable.vue +7 -0
  23. package/components/PromptModal.vue +47 -8
  24. package/components/ResourceDetail/Masthead.vue +38 -13
  25. package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
  26. package/components/ResourceDetail/index.vue +47 -12
  27. package/components/ResourceList/index.vue +2 -1
  28. package/components/ResourceTable.vue +54 -19
  29. package/components/SideNav.vue +5 -1
  30. package/components/SlideInPanelManager.vue +125 -0
  31. package/components/SortableTable/THead.vue +5 -2
  32. package/components/SortableTable/actions.js +1 -1
  33. package/components/SortableTable/index.vue +54 -40
  34. package/components/SortableTable/paging.js +16 -19
  35. package/components/SortableTable/selection.js +1 -12
  36. package/components/Tabbed/index.vue +6 -0
  37. package/components/Wizard.vue +2 -2
  38. package/components/__tests__/AsyncButton.test.ts +39 -0
  39. package/components/__tests__/CruResource.test.ts +63 -0
  40. package/components/__tests__/ModalManager.spec.ts +176 -0
  41. package/components/__tests__/PromptModal.test.ts +146 -0
  42. package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
  43. package/components/auth/AuthBanner.vue +13 -11
  44. package/components/auth/Principal.vue +1 -0
  45. package/components/auth/login/ldap.vue +1 -1
  46. package/components/fleet/FleetResources.vue +21 -6
  47. package/components/form/ArrayList.vue +138 -118
  48. package/components/form/BannerSettings.vue +149 -85
  49. package/components/form/ColorInput.vue +35 -6
  50. package/components/form/EnvVars.vue +1 -0
  51. package/components/form/KeyValue.vue +10 -7
  52. package/components/form/LabeledSelect.vue +25 -23
  53. package/components/form/MatchExpressions.vue +9 -2
  54. package/components/form/NameNsDescription.vue +6 -2
  55. package/components/form/NotificationSettings.vue +15 -1
  56. package/components/form/Password.vue +1 -0
  57. package/components/form/Probe.vue +1 -0
  58. package/components/form/ResourceSelector.vue +26 -23
  59. package/components/form/ResourceTabs/index.vue +2 -1
  60. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +15 -34
  61. package/components/form/SSHKnownHosts/index.vue +14 -11
  62. package/components/form/Select.vue +8 -15
  63. package/components/form/UnitInput.vue +13 -0
  64. package/components/form/ValueFromResource.vue +12 -12
  65. package/components/form/__tests__/ArrayList.test.ts +34 -2
  66. package/components/form/__tests__/ColorInput.test.ts +35 -0
  67. package/components/form/__tests__/KeyValue.test.ts +36 -0
  68. package/components/form/__tests__/LabeledSelect.test.ts +73 -0
  69. package/components/form/__tests__/SSHKnownHosts.test.ts +11 -2
  70. package/components/form/__tests__/Select.test.ts +34 -1
  71. package/components/form/__tests__/UnitInput.test.ts +23 -1
  72. package/components/formatter/ClusterLink.vue +5 -8
  73. package/components/formatter/Description.vue +30 -0
  74. package/components/formatter/__tests__/ClusterLink.test.ts +2 -32
  75. package/components/nav/Group.vue +12 -4
  76. package/components/nav/Header.vue +16 -43
  77. package/components/nav/NamespaceFilter.vue +134 -86
  78. package/components/nav/TopLevelMenu.vue +4 -5
  79. package/components/nav/WindowManager/ContainerLogs.vue +87 -61
  80. package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
  81. package/components/nav/WindowManager/index.vue +1 -0
  82. package/components/templates/default.vue +6 -3
  83. package/components/templates/home.vue +6 -0
  84. package/components/templates/plain.vue +6 -3
  85. package/composables/focusTrap.ts +12 -4
  86. package/config/product/explorer.js +16 -13
  87. package/config/product/manager.js +1 -28
  88. package/config/settings.ts +11 -13
  89. package/config/store.js +4 -0
  90. package/config/table-headers.js +7 -5
  91. package/config/uiplugins.js +5 -1
  92. package/core/types.ts +7 -6
  93. package/detail/catalog.cattle.io.app.vue +5 -1
  94. package/detail/fleet.cattle.io.bundle.vue +70 -6
  95. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  96. package/detail/namespace.vue +0 -3
  97. package/detail/node.vue +17 -13
  98. package/detail/provisioning.cattle.io.cluster.vue +85 -9
  99. package/detail/service.vue +0 -1
  100. package/detail/workload/index.vue +21 -34
  101. package/dialog/AddCustomBadgeDialog.vue +0 -1
  102. package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
  103. package/dialog/AssignToDialog.vue +176 -0
  104. package/dialog/ChangePasswordDialog.vue +106 -0
  105. package/{pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue → dialog/DeveloperLoadExtensionDialog.vue} +74 -71
  106. package/dialog/DisableAuthProviderDialog.vue +101 -0
  107. package/dialog/DrainNode.vue +1 -1
  108. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue → dialog/ExtensionCatalogInstallDialog.vue} +100 -88
  109. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue → dialog/ExtensionCatalogUninstallDialog.vue} +83 -65
  110. package/dialog/FeatureFlagListDialog.vue +288 -0
  111. package/dialog/ForceMachineRemoveDialog.vue +1 -1
  112. package/{components/Import.vue → dialog/ImportDialog.vue} +0 -5
  113. package/{pages/c/_cluster/uiplugins/InstallDialog.vue → dialog/InstallExtensionDialog.vue} +124 -106
  114. package/{components/form/SSHKnownHosts → dialog}/KnownHostsEditDialog.vue +52 -62
  115. package/dialog/MoveNamespaceDialog.vue +157 -0
  116. package/dialog/ScalePoolDownDialog.vue +1 -1
  117. package/{components/nav/Jump.vue → dialog/SearchDialog.vue} +34 -14
  118. package/{pages/c/_cluster/uiplugins/UninstallDialog.vue → dialog/UninstallExtensionDialog.vue} +67 -58
  119. package/dialog/WechatDialog.vue +57 -0
  120. package/edit/__tests__/service.test.ts +2 -1
  121. package/edit/auth/azuread.vue +1 -1
  122. package/edit/auth/github.vue +1 -1
  123. package/edit/auth/googleoauth.vue +1 -1
  124. package/edit/auth/ldap/index.vue +1 -1
  125. package/edit/auth/oidc.vue +1 -1
  126. package/edit/auth/saml.vue +1 -1
  127. package/edit/cloudcredential.vue +24 -10
  128. package/edit/management.cattle.io.user.vue +28 -3
  129. package/edit/namespace.vue +1 -4
  130. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +3 -14
  131. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +57 -62
  132. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +3 -14
  133. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.test.ts +72 -41
  134. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +17 -1
  135. package/edit/networking.k8s.io.networkpolicy/index.vue +18 -30
  136. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
  137. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -10
  138. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -8
  139. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +26 -12
  140. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +66 -0
  141. package/edit/provisioning.cattle.io.cluster/__tests__/utils/rke2-test-data.ts +58 -0
  142. package/edit/provisioning.cattle.io.cluster/index.vue +21 -73
  143. package/edit/provisioning.cattle.io.cluster/rke2.vue +24 -7
  144. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
  145. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +4 -1
  146. package/edit/service.vue +13 -28
  147. package/initialize/install-plugins.js +2 -1
  148. package/list/harvesterhci.io.management.cluster.vue +4 -1
  149. package/list/management.cattle.io.feature.vue +4 -288
  150. package/list/workload.vue +6 -1
  151. package/machine-config/azure.vue +16 -4
  152. package/mixins/resource-fetch-api-pagination.js +55 -43
  153. package/mixins/resource-fetch.js +14 -5
  154. package/mixins/vue-select-overrides.js +0 -4
  155. package/models/__tests__/workload.test.ts +1 -0
  156. package/models/cluster/node.js +1 -0
  157. package/models/cluster.js +32 -2
  158. package/models/fleet.cattle.io.cluster.js +8 -2
  159. package/models/fleet.cattle.io.gitrepo.js +8 -34
  160. package/models/management.cattle.io.cluster.js +0 -20
  161. package/models/management.cattle.io.feature.js +7 -1
  162. package/models/management.cattle.io.node.js +7 -22
  163. package/models/management.cattle.io.nodepool.js +12 -0
  164. package/models/namespace.js +12 -1
  165. package/models/provisioning.cattle.io.cluster.js +18 -64
  166. package/models/service.js +24 -9
  167. package/models/workload.js +70 -31
  168. package/package.json +1 -1
  169. package/pages/about.vue +13 -3
  170. package/pages/account/index.vue +12 -5
  171. package/pages/auth/login.vue +7 -4
  172. package/pages/auth/setup.vue +1 -0
  173. package/pages/auth/verify.vue +9 -7
  174. package/pages/c/_cluster/apps/charts/install.vue +25 -26
  175. package/pages/c/_cluster/auth/config/index.vue +10 -12
  176. package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
  177. package/pages/c/_cluster/explorer/index.vue +28 -15
  178. package/pages/c/_cluster/istio/index.vue +2 -2
  179. package/pages/c/_cluster/longhorn/index.vue +3 -3
  180. package/pages/c/_cluster/monitoring/index.vue +1 -1
  181. package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
  182. package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
  183. package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
  184. package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
  185. package/pages/c/_cluster/neuvector/index.vue +1 -1
  186. package/pages/c/_cluster/settings/banners.vue +60 -5
  187. package/pages/c/_cluster/settings/performance.vue +7 -26
  188. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
  189. package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +4 -7
  190. package/pages/c/_cluster/uiplugins/index.vue +98 -55
  191. package/pages/diagnostic.vue +12 -9
  192. package/pages/fail-whale.vue +8 -5
  193. package/pages/home.vue +11 -52
  194. package/pages/prefs.vue +7 -6
  195. package/plugins/clean-html.js +2 -0
  196. package/plugins/dashboard-store/__tests__/actions.test.ts +4 -1
  197. package/plugins/dashboard-store/actions.js +122 -21
  198. package/plugins/dashboard-store/getters.js +74 -3
  199. package/plugins/dashboard-store/mutations.js +10 -5
  200. package/plugins/dashboard-store/resource-class.js +23 -3
  201. package/plugins/internal-api/index.ts +37 -0
  202. package/plugins/internal-api/shared/base-api.ts +13 -0
  203. package/plugins/internal-api/shell/shell.api.ts +108 -0
  204. package/plugins/steve/__tests__/getters.test.ts +18 -11
  205. package/plugins/steve/__tests__/steve-class.test.ts +1 -0
  206. package/plugins/steve/actions.js +34 -24
  207. package/plugins/steve/getters.js +39 -10
  208. package/plugins/steve/steve-class.js +5 -0
  209. package/plugins/steve/steve-pagination-utils.ts +199 -37
  210. package/plugins/steve/worker/web-worker.advanced.js +3 -1
  211. package/public/index.html +1 -0
  212. package/rancher-components/Banner/Banner.test.ts +51 -3
  213. package/rancher-components/Banner/Banner.vue +28 -6
  214. package/rancher-components/Card/Card.vue +1 -1
  215. package/rancher-components/Form/Checkbox/Checkbox.test.ts +59 -1
  216. package/rancher-components/Form/Checkbox/Checkbox.vue +27 -3
  217. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +51 -0
  218. package/rancher-components/Form/LabeledInput/LabeledInput.vue +20 -2
  219. package/rancher-components/Form/Radio/RadioButton.test.ts +36 -1
  220. package/rancher-components/Form/Radio/RadioButton.vue +20 -4
  221. package/rancher-components/Form/Radio/RadioGroup.test.ts +60 -0
  222. package/rancher-components/Form/Radio/RadioGroup.vue +75 -35
  223. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
  224. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +26 -1
  225. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +10 -1
  226. package/rancher-components/RcButton/RcButton.vue +2 -1
  227. package/rancher-components/RcButton/types.ts +1 -0
  228. package/rancher-components/RcDropdown/RcDropdown.vue +17 -6
  229. package/rancher-components/RcDropdown/RcDropdownItem.vue +3 -56
  230. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +68 -0
  231. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +92 -0
  232. package/rancher-components/RcDropdown/index.ts +2 -0
  233. package/rancher-components/RcDropdown/useDropdownItem.ts +63 -0
  234. package/scripts/extension/bundle +20 -0
  235. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
  236. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
  237. package/scripts/extension/helmpatch +44 -31
  238. package/scripts/extension/publish +12 -13
  239. package/scripts/typegen.sh +2 -4
  240. package/store/action-menu.js +26 -56
  241. package/store/features.js +0 -1
  242. package/store/index.js +5 -0
  243. package/store/modal.ts +71 -0
  244. package/store/slideInPanel.ts +47 -0
  245. package/store/type-map.js +8 -1
  246. package/store/type-map.utils.ts +49 -6
  247. package/types/fleet.d.ts +1 -1
  248. package/types/global-vue.d.ts +5 -0
  249. package/types/internal-api/shell/growl.d.ts +25 -0
  250. package/types/internal-api/shell/modal.d.ts +77 -0
  251. package/types/internal-api/shell/slideIn.d.ts +15 -0
  252. package/types/kube/kube-api.ts +22 -0
  253. package/types/resources/fleet.d.ts +0 -14
  254. package/types/resources/settings.d.ts +0 -4
  255. package/types/shell/index.d.ts +375 -306
  256. package/types/store/dashboard-store.types.ts +24 -1
  257. package/types/store/pagination.types.ts +19 -2
  258. package/types/vue-shim.d.ts +4 -1
  259. package/utils/__mocks__/tabbable.js +13 -0
  260. package/utils/__tests__/object.test.ts +38 -4
  261. package/utils/cluster.js +24 -20
  262. package/utils/fleet.ts +15 -73
  263. package/utils/grafana.js +1 -0
  264. package/utils/object.js +36 -5
  265. package/utils/pagination-utils.ts +6 -2
  266. package/utils/perf-setting.utils.ts +28 -0
  267. package/utils/selector-typed.ts +205 -0
  268. package/utils/selector.js +29 -6
  269. package/utils/uiplugins.ts +10 -6
  270. package/utils/v-sphere.ts +5 -1
  271. package/utils/validators/formRules/__tests__/index.test.ts +10 -1
  272. package/utils/validators/formRules/index.ts +27 -3
  273. package/components/AssignTo.vue +0 -199
  274. package/components/DisableAuthProviderModal.vue +0 -115
  275. package/components/MoveModal.vue +0 -167
  276. package/components/PromptChangePassword.vue +0 -123
  277. package/components/fleet/FleetBundleResources.vue +0 -86
  278. package/components/formatter/RKETemplateName.vue +0 -37
  279. package/dialog/SaveAsRKETemplateDialog.vue +0 -139
  280. package/types/vue-shim.d +0 -20
@@ -99,7 +99,7 @@ export default {
99
99
  </template>
100
100
  </AuthBanner>
101
101
 
102
- <hr>
102
+ <hr role="none">
103
103
 
104
104
  <AllowedPrincipals
105
105
  :provider="NAME"
@@ -209,7 +209,7 @@ export default {
209
209
  </template>
210
210
  </AuthBanner>
211
211
 
212
- <hr>
212
+ <hr role="none">
213
213
 
214
214
  <AllowedPrincipals
215
215
  :provider="NAME"
@@ -223,7 +223,7 @@ export default {
223
223
  </template>
224
224
  </AuthBanner>
225
225
 
226
- <hr>
226
+ <hr role="none">
227
227
 
228
228
  <AllowedPrincipals
229
229
  :provider="NAME"
@@ -100,6 +100,30 @@ export default {
100
100
  computed: {
101
101
  rke2Enabled: mapFeature(RKE2_FEATURE),
102
102
 
103
+ hasCustomCloudCredentialComponent() {
104
+ const driverName = this.driverName;
105
+
106
+ return this.$store.getters['type-map/hasCustomCloudCredentialComponent'](driverName);
107
+ },
108
+
109
+ cloudCredentialComponent() {
110
+ const driverName = this.driverName;
111
+
112
+ return this.$store.getters['type-map/importCloudCredential'](driverName);
113
+ },
114
+
115
+ genericCloudCredentialComponent() {
116
+ return this.$store.getters['type-map/importCloudCredential']('generic');
117
+ },
118
+
119
+ cloudComponent() {
120
+ if (this.hasCustomCloudCredentialComponent) {
121
+ return this.cloudCredentialComponent;
122
+ }
123
+
124
+ return this.genericCloudCredentialComponent;
125
+ },
126
+
103
127
  validationPassed() {
104
128
  return this.credCustomComponentValidation && this.nameRequiredValidation;
105
129
  },
@@ -112,14 +136,6 @@ export default {
112
136
  return this.value?.provider;
113
137
  },
114
138
 
115
- cloudComponent() {
116
- if (this.$store.getters['type-map/hasCustomCloudCredentialComponent'](this.driverName)) {
117
- return this.$store.getters['type-map/importCloudCredential'](this.driverName);
118
- }
119
-
120
- return this.$store.getters['type-map/importCloudCredential']('generic');
121
- },
122
-
123
139
  // array of id, label, description, initials for type selection step
124
140
  secretSubTypes() {
125
141
  const out = [];
@@ -194,7 +210,6 @@ export default {
194
210
  },
195
211
 
196
212
  methods: {
197
-
198
213
  createValidationChanged(passed) {
199
214
  this.credCustomComponentValidation = passed;
200
215
  },
@@ -276,7 +291,6 @@ export default {
276
291
  <Loading v-if="$fetchState.pending" />
277
292
  <CruResource
278
293
  v-else
279
- :done-params="$attrs['done-params'] /* Without this, changes to the validationPassed prop end up propagating all the way to the root of the app and force a re-render when the input becomes valid. I haven't found a reasonable explanation for why this happens. */"
280
294
  :mode="mode"
281
295
  :validation-passed="validationPassed"
282
296
  :selected-subtype="value._type"
@@ -15,6 +15,8 @@ export default {
15
15
  ChangePassword, GlobalRoleBindings, CruResource, LabeledInput, Loading
16
16
  },
17
17
 
18
+ emits: ['update:mode'],
19
+
18
20
  mixins: [
19
21
  CreateEditView
20
22
  ],
@@ -40,6 +42,7 @@ export default {
40
42
  roles: !showGlobalRoles,
41
43
  rolesChanged: false,
42
44
  },
45
+ watchOverride: false,
43
46
  };
44
47
  },
45
48
 
@@ -115,7 +118,11 @@ export default {
115
118
  this.$router.replace({ name: this.doneRoute });
116
119
  buttonDone(true);
117
120
  } catch (err) {
118
- this.errors = exceptionToErrorsArray(err);
121
+ if (err?.message?.includes('errors due to escalation')) {
122
+ this.errors = [this.t('rbac.errors.escalation')];
123
+ } else {
124
+ this.errors = exceptionToErrorsArray(err);
125
+ }
119
126
  buttonDone(false);
120
127
  }
121
128
  },
@@ -150,7 +157,7 @@ export default {
150
157
 
151
158
  const normanUser = await this.$store.dispatch('rancher/find', {
152
159
  type: NORMAN.USER,
153
- id: this.value.id,
160
+ id: this.value.id || this.user?.id,
154
161
  });
155
162
 
156
163
  // Save change of password
@@ -181,8 +188,25 @@ export default {
181
188
  },
182
189
 
183
190
  async updateRoles(userId) {
184
- if (this.$refs.grb) {
191
+ if (!this.$refs.grb) {
192
+ return;
193
+ }
194
+
195
+ try {
185
196
  await this.$refs.grb.save(userId);
197
+ } catch (err) {
198
+ if (this.isCreate) {
199
+ this.watchOverride = true;
200
+ this.$emit(
201
+ 'update:mode',
202
+ {
203
+ userId,
204
+ mode: _EDIT,
205
+ resource: 'management.cattle.io.user',
206
+ }
207
+ );
208
+ }
209
+ throw err;
186
210
  }
187
211
  }
188
212
  }
@@ -255,6 +279,7 @@ export default {
255
279
  :user-id="value.id || liveValue.id"
256
280
  :mode="mode"
257
281
  :real-mode="realMode"
282
+ :watch-override="watchOverride"
258
283
  type="user"
259
284
  @hasChanges="validation.rolesChanged = $event"
260
285
  @canLogIn="validation.roles = $event"
@@ -11,7 +11,6 @@ import Tab from '@shell/components/Tabbed/Tab';
11
11
  import ResourceTabs from '@shell/components/form/ResourceTabs/index.vue';
12
12
  import CruResource from '@shell/components/CruResource';
13
13
  import { PROJECT_ID, _VIEW, FLAT_VIEW, _CREATE } from '@shell/config/query-params';
14
- import MoveModal from '@shell/components/MoveModal';
15
14
  import ResourceQuota from '@shell/components/form/ResourceQuota/Namespace';
16
15
  import Loading from '@shell/components/Loading';
17
16
  import { HARVESTER_TYPES, RANCHER_TYPES } from '@shell/components/form/ResourceQuota/shared';
@@ -31,8 +30,7 @@ export default {
31
30
  PodSecurityAdmission,
32
31
  ResourceQuota,
33
32
  Tab,
34
- ResourceTabs,
35
- MoveModal
33
+ ResourceTabs
36
34
  },
37
35
 
38
36
  mixins: [CreateEditView],
@@ -277,6 +275,5 @@ export default {
277
275
  />
278
276
  </Tab>
279
277
  </ResourceTabs>
280
- <MoveModal v-if="projects" />
281
278
  </CruResource>
282
279
  </template>
@@ -5,6 +5,9 @@ import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
5
5
  import { _EDIT } from '@shell/config/query-params';
6
6
  import PolicyRuleTarget from '@shell/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget';
7
7
 
8
+ // Components shown for Network Policy --> Ingress/Egress Rules --> Rule Type are...
9
+ // Edit Network Policy --> `PolicyRules` 1 --> M `PolicyRule` 1 --> M `PolicyRuleTarget`
10
+
8
11
  export default {
9
12
  components: {
10
13
  ArrayListGrouped, PolicyRulePort, PolicyRuleTarget
@@ -28,18 +31,6 @@ export default {
28
31
  type: String,
29
32
  default: ''
30
33
  },
31
- allPods: {
32
- type: Array,
33
- default: () => {
34
- return [];
35
- },
36
- },
37
- allNamespaces: {
38
- type: Array,
39
- default: () => {
40
- return [];
41
- },
42
- },
43
34
  },
44
35
  data() {
45
36
  return { targetKey: this.type === 'ingress' ? 'from' : 'to' };
@@ -71,8 +62,6 @@ export default {
71
62
  :mode="mode"
72
63
  :type="type"
73
64
  :namespace="namespace"
74
- :all-namespaces="allNamespaces"
75
- :all-pods="allPods"
76
65
  :data-testid="`policy-rule-target-${props.i}`"
77
66
  />
78
67
  </template>
@@ -4,12 +4,13 @@ import { LabeledInput } from '@components/Form/LabeledInput';
4
4
  import LabeledSelect from '@shell/components/form/LabeledSelect';
5
5
  import { _EDIT } from '@shell/config/query-params';
6
6
  import MatchExpressions from '@shell/components/form/MatchExpressions';
7
- import { convert, matching, simplify } from '@shell/utils/selector';
8
- import { POD } from '@shell/config/types';
7
+ import { convert, simplify } from '@shell/utils/selector';
8
+ import { NAMESPACE, POD } from '@shell/config/types';
9
9
  import ArrayList from '@shell/components/form/ArrayList';
10
10
  import { Banner } from '@components/Banner';
11
- import throttle from 'lodash/throttle';
11
+ import debounce from 'lodash/debounce';
12
12
  import { isValidCIDR } from '@shell/utils/validators/cidr';
13
+ import { matching } from '@shell/utils/selector-typed';
13
14
 
14
15
  const TARGET_OPTIONS = {
15
16
  IP_BLOCK: 'ipBlock',
@@ -18,6 +19,9 @@ const TARGET_OPTIONS = {
18
19
  NAMESPACE_AND_POD_SELECTOR: 'namespaceAndPodSelector',
19
20
  };
20
21
 
22
+ // Components shown for Network Policy --> Ingress/Egress Rules --> Rule Type are...
23
+ // Edit Network Policy --> `PolicyRules` 1 --> M `PolicyRule` 1 --> M `PolicyRuleTarget`
24
+
21
25
  export default {
22
26
  components: {
23
27
  ArrayList, Banner, LabeledInput, LabeledSelect, MatchExpressions
@@ -41,18 +45,6 @@ export default {
41
45
  type: String,
42
46
  default: ''
43
47
  },
44
- allPods: {
45
- type: Array,
46
- default: () => {
47
- return [];
48
- },
49
- },
50
- allNamespaces: {
51
- type: Array,
52
- default: () => {
53
- return [];
54
- },
55
- },
56
48
  },
57
49
  data() {
58
50
  if (!this.value[TARGET_OPTIONS.IP_BLOCK] &&
@@ -66,18 +58,26 @@ export default {
66
58
  }
67
59
 
68
60
  return {
69
- portOptions: ['TCP', 'UDP'],
70
- matchingPods: {},
71
- matchingNamespaces: {},
72
- invalidCidr: null,
73
- invalidCidrs: [],
61
+ portOptions: ['TCP', 'UDP'],
62
+ matchingPods: {
63
+ matches: [], matched: 0, total: 0
64
+ },
65
+ matchingNamespaces: {
66
+ matches: [], matched: 0, total: 0
67
+ },
68
+ invalidCidr: null,
69
+ invalidCidrs: [],
74
70
  POD,
75
71
  TARGET_OPTIONS,
76
- targetOptions: Object.values(TARGET_OPTIONS),
77
- throttleTime: 250,
72
+ targetOptions: Object.values(TARGET_OPTIONS),
73
+ inStore: this.$store.getters['currentProduct'].inStore,
74
+ debouncedUpdateMatches: debounce(this.updateMatches, 500)
78
75
  };
79
76
  },
80
77
  computed: {
78
+ /**
79
+ * of type matchExpression aka `KubeLabelSelectorExpression[]`
80
+ */
81
81
  podSelectorExpressions: {
82
82
  get() {
83
83
  return convert(
@@ -89,6 +89,9 @@ export default {
89
89
  this.value[TARGET_OPTIONS.POD_SELECTOR] = simplify(podSelectorExpressions);
90
90
  }
91
91
  },
92
+ /**
93
+ * of type matchExpression aka `KubeLabelSelectorExpression[]`
94
+ */
92
95
  namespaceSelectorExpressions: {
93
96
  get() {
94
97
  return convert(
@@ -145,39 +148,40 @@ export default {
145
148
  },
146
149
  watch: {
147
150
  namespace: {
148
- handler: 'updateMatches',
149
- immediate: true
150
- },
151
- allNamespaces: {
152
- handler: 'updateMatches',
151
+ handler: 'debouncedUpdateMatches',
153
152
  immediate: true
154
153
  },
155
154
  'value.podSelector': {
156
- handler: 'updateMatches',
155
+ handler: 'debouncedUpdateMatches',
157
156
  immediate: true
158
157
  },
159
158
  'value.namespaceSelector': {
160
- handler: 'updateMatches',
159
+ handler: 'debouncedUpdateMatches',
161
160
  immediate: true
162
161
  },
163
162
  'value.ipBlock.cidr': 'validateCIDR',
164
163
  'value.ipBlock.except': 'validateCIDR',
165
164
  podSelectorExpressions: {
166
- handler: 'updateMatches',
165
+ handler: 'debouncedUpdateMatches',
167
166
  immediate: true
168
167
  },
169
168
  namespaceSelectorExpressions: {
170
- handler: 'updateMatches',
169
+ handler: 'debouncedUpdateMatches',
171
170
  immediate: true
172
171
  }
173
172
  },
173
+
174
+ fetch() {
175
+ this.debouncedUpdateMatches();
176
+ },
177
+
174
178
  methods: {
175
- updateMatches() {
176
- throttle(() => {
177
- this.matchingNamespaces = this.getMatchingNamespaces();
178
- this.matchingPods = this.getMatchingPods();
179
- }, this.throttle, { leading: true })();
179
+ async updateMatches() {
180
+ // Note - needs to be sequential as getMatchingPods requires matchingNamespaces to be up-to-date
181
+ this.matchingNamespaces = await this.getMatchingNamespaces();
182
+ this.matchingPods = await this.getMatchingPods();
180
183
  },
184
+
181
185
  validateCIDR() {
182
186
  const exceptCidrs = this.value[TARGET_OPTIONS.IP_BLOCK]?.except || [];
183
187
 
@@ -191,34 +195,25 @@ export default {
191
195
  this.invalidCidr = null;
192
196
  }
193
197
  },
194
- getMatchingPods() {
195
- const namespaces = this.targetType === TARGET_OPTIONS.NAMESPACE_AND_POD_SELECTOR ? this.matchingNamespaces.matches : [{ id: this.namespace }];
196
- const allInNamespace = this.allPods.filter((pod) => namespaces.some((ns) => ns.id === pod.metadata.namespace));
197
- const match = matching(allInNamespace, this.podSelectorExpressions);
198
- const matched = match.length || 0;
199
- const sample = match[0]?.nameDisplay;
200
198
 
201
- return {
202
- matched,
203
- matches: match,
204
- none: matched === 0,
205
- sample,
206
- total: allInNamespace.length,
207
- };
199
+ async getMatchingPods() {
200
+ return await matching({
201
+ labelSelector: { matchExpressions: this.podSelectorExpressions },
202
+ type: POD,
203
+ $store: this.$store,
204
+ inStore: this.inStore,
205
+ namespace: this.targetType === TARGET_OPTIONS.NAMESPACE_AND_POD_SELECTOR ? this.matchingNamespaces.matches.map((ns) => ns.id) : this.namespace,
206
+ transient: true,
207
+ });
208
208
  },
209
- getMatchingNamespaces() {
210
- const allNamespaces = this.allNamespaces;
211
- const match = matching(allNamespaces, this.namespaceSelectorExpressions);
212
- const matched = match.length || 0;
213
- const sample = match[0]?.nameDisplay;
214
-
215
- return {
216
- matched,
217
- matches: match,
218
- none: matched === 0,
219
- sample,
220
- total: allNamespaces.length,
221
- };
209
+ async getMatchingNamespaces() {
210
+ return await matching({
211
+ labelSelector: { matchExpressions: this.namespaceSelectorExpressions },
212
+ type: NAMESPACE,
213
+ $store: this.$store,
214
+ inStore: this.inStore,
215
+ transient: true,
216
+ });
222
217
  },
223
218
  }
224
219
  };
@@ -6,6 +6,9 @@ import Tab from '@shell/components/Tabbed/Tab';
6
6
  import Tabbed from '@shell/components/Tabbed';
7
7
  import { removeAt } from '@shell/utils/array';
8
8
 
9
+ // Components shown for Network Policy --> Ingress/Egress Rules --> Rule Type are...
10
+ // Edit Network Policy --> `PolicyRules` 1 --> M `PolicyRule` 1 --> M `PolicyRuleTarget`
11
+
9
12
  export default {
10
13
  components: {
11
14
  PolicyRule, Tabbed, Tab
@@ -24,18 +27,6 @@ export default {
24
27
  type: String,
25
28
  default: 'ingress'
26
29
  },
27
- allPods: {
28
- type: Array,
29
- default: () => {
30
- return [];
31
- },
32
- },
33
- allNamespaces: {
34
- type: Array,
35
- default: () => {
36
- return [];
37
- },
38
- },
39
30
  },
40
31
  data() {
41
32
  if (!this.value.spec[this.type]) {
@@ -82,8 +73,6 @@ export default {
82
73
  :mode="mode"
83
74
  :type="type"
84
75
  :namespace="value.metadata.namespace"
85
- :all-namespaces="allNamespaces"
86
- :all-pods="allPods"
87
76
  />
88
77
  </Tab>
89
78
  </Tabbed>
@@ -2,6 +2,8 @@ import { mount } from '@vue/test-utils';
2
2
  import PolicyRuleTarget from '@shell/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget';
3
3
  import mock from '@shell/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json';
4
4
  import { PolicyRuleTargetSelectors } from '@shell/edit/networking.k8s.io.networkpolicy/__tests__/utils/selectors.test.ts';
5
+ import { nextTick } from 'vue';
6
+ import { COUNT, NAMESPACE, POD } from '@shell/config/types';
5
7
 
6
8
  type MatchData = {
7
9
  matched: number;
@@ -11,29 +13,6 @@ type MatchData = {
11
13
  sample?: string;
12
14
  }
13
15
 
14
- const newNamespace = {
15
- id: 'new-namespace',
16
- type: 'namespace',
17
- kind: 'Namespace',
18
- spec: { finalizers: ['kubernetes'] },
19
- status: { phase: 'Active' },
20
- metadata: {
21
- annotations: { user: 'john' },
22
- name: 'default',
23
- creationTimestamp: '2024-01-31T10:24:03Z',
24
- fields: ['default', 'Active', '1d'],
25
- labels: { user: 'john' },
26
- relationships: null,
27
- resourceVersion: '1',
28
- state: {
29
- error: false,
30
- message: '',
31
- name: 'active',
32
- transitioning: false
33
- }
34
- }
35
- };
36
-
37
16
  describe.each([
38
17
  'view',
39
18
  'edit',
@@ -45,18 +24,60 @@ describe.each([
45
24
  return { throttleTime: 0 };
46
25
  },
47
26
  props: {
48
- namespace: mock.defaultNamespace,
49
- allNamespaces: mock.allNamespaces,
50
- allPods: mock.allPods,
51
- type: 'ingress',
27
+ namespace: mock.defaultNamespace,
28
+ type: 'ingress',
52
29
  mode
53
30
  },
54
31
  global: {
55
32
  mocks: {
56
33
  $store: {
34
+ dispatch: (action: string, { type }: { type: string}) => {
35
+ switch (action) {
36
+ case 'cluster/findAll':
37
+ switch (type) {
38
+ case NAMESPACE:
39
+ return mock.allNamespaces;
40
+ case POD:
41
+ return mock.allPods;
42
+ default:
43
+ throw new Error(`unknown type ${ type }`);
44
+ }
45
+ default:
46
+ throw new Error(`unknown action ${ action }`);
47
+ }
48
+ },
57
49
  getters: {
58
- 'i18n/exists': mockExists,
59
- 'i18n/t': (key: string, matchData: MatchData) => matchData ? `${ key }-${ matchData.total }` : key,
50
+ 'i18n/exists': mockExists,
51
+ 'i18n/t': (key: string, matchData: MatchData) => matchData ? `${ key }-${ matchData.total }` : key,
52
+ currentProduct: { inStore: 'cluster' },
53
+ 'cluster/all': (type: string) => {
54
+ switch (type) {
55
+ case COUNT:
56
+ return mock.counts;
57
+ default:
58
+ throw new Error(`unknown type ${ type }`);
59
+ }
60
+ },
61
+ 'cluster/findAll': ({ type }: {type: string}) => {
62
+ switch (type) {
63
+ case NAMESPACE:
64
+ return mock.allNamespaces;
65
+ case POD:
66
+ return mock.allPods;
67
+ default:
68
+ throw new Error(`unknown type ${ type }`);
69
+ }
70
+ },
71
+ 'cluster/schemaFor': (type: string) => {
72
+ switch (type) {
73
+ case NAMESPACE:
74
+ return { attributes: { namespaced: false } };
75
+ case POD:
76
+ return { attributes: { namespaced: true } };
77
+ default:
78
+ throw new Error(`unknown type ${ type }`);
79
+ }
80
+ }
60
81
  }
61
82
  }
62
83
  },
@@ -64,6 +85,20 @@ describe.each([
64
85
  });
65
86
 
66
87
  describe(`${ mode } mode`, () => {
88
+ beforeEach(() => {
89
+ jest.useFakeTimers();
90
+ });
91
+
92
+ afterEach(() => {
93
+ jest.runOnlyPendingTimers();
94
+ jest.useRealTimers();
95
+ });
96
+
97
+ const waitForUpdatedMatched = async() => {
98
+ jest.advanceTimersByTime(1000); // Wait for debounced call to fetch updated cluster list
99
+ await nextTick(); // Wait for changes to cluster list to trigger changes
100
+ };
101
+
67
102
  it('should display ip-block selector rule', async() => {
68
103
  const ipBlock = mock.selectors.ipBlock;
69
104
 
@@ -83,6 +118,7 @@ describe.each([
83
118
  });
84
119
 
85
120
  it('should display namespace selector rule', async() => {
121
+ // This test needs improving - mock data needs to contain more than applicable to selector
86
122
  const namespaceSelector = mock.selectors.namespace;
87
123
 
88
124
  await wrapper.setProps({ value: { namespaceSelector } });
@@ -92,6 +128,12 @@ describe.each([
92
128
  // Check rule type selector
93
129
  expect(wrapper.vm.targetType).toBe('namespaceSelector');
94
130
 
131
+ // Check the matching namespaces displayed by the banner
132
+ expect(wrapper.vm.$data.matchingNamespaces.matched).toBe(0);
133
+
134
+ await wrapper.vm.updateMatches();
135
+ await waitForUpdatedMatched();
136
+
95
137
  // Check the matching namespaces displayed by the banner
96
138
  expect(wrapper.vm.$data.matchingNamespaces.matched).toBe(1);
97
139
 
@@ -105,21 +147,10 @@ describe.each([
105
147
  expect(selectors.namespaceAndPod.podRule.exists()).toBe(false);
106
148
 
107
149
  expect(selectors.namespace.element).toBeDefined();
108
-
109
- // Updating allNamespace should update the matching namespaces message too
110
- await wrapper.setProps({
111
- allNamespaces: [
112
- ...wrapper.vm.$props.allNamespaces,
113
- newNamespace
114
- ]
115
- });
116
-
117
- const expectedTotal = 3;
118
-
119
- expect(wrapper.vm.$data.matchingNamespaces.total).toBe(expectedTotal);
120
150
  });
121
151
 
122
152
  it('should display pod selector rule', async() => {
153
+ // This test needs improving - mock data needs to contain more than applicable to selector
123
154
  const podSelector = mock.selectors.pod;
124
155
 
125
156
  await wrapper.setProps({ value: { podSelector } });
@@ -154,5 +154,21 @@
154
154
  }
155
155
  }
156
156
  }
157
- ]
157
+ ],
158
+ "counts": [{
159
+ "counts": {
160
+ "namespace": {
161
+ "summary": {
162
+ "count": 1
163
+ }
164
+ },
165
+ "pod": {
166
+ "namespaces": {
167
+ "default": {
168
+ "count": 1
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }]
158
174
  }