@rancher/shell 3.0.5-rc.6 → 3.0.5-rc.8

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 (243) hide show
  1. package/assets/brand/classic/metadata.json +3 -0
  2. package/assets/styles/app.scss +1 -0
  3. package/assets/styles/base/_color.scss +16 -0
  4. package/assets/styles/base/_helpers.scss +10 -0
  5. package/assets/styles/base/_variables.scss +18 -12
  6. package/assets/styles/fonts/_icons.scss +1 -32
  7. package/assets/styles/global/_layout.scss +1 -1
  8. package/assets/styles/themes/_dark.scss +262 -258
  9. package/assets/styles/themes/_light.scss +538 -509
  10. package/assets/styles/themes/_modern.scss +914 -0
  11. package/assets/translations/en-us.yaml +110 -29
  12. package/chart/__tests__/S3.test.ts +2 -1
  13. package/cloud-credential/generic.vue +18 -10
  14. package/cloud-credential/harvester.vue +1 -9
  15. package/components/AdvancedSection.vue +8 -0
  16. package/components/ChartReadme.vue +17 -7
  17. package/components/CodeMirror.vue +1 -1
  18. package/components/Drawer/Chrome.vue +0 -1
  19. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +27 -28
  20. package/components/Drawer/ResourceDetailDrawer/composables.ts +4 -24
  21. package/components/Drawer/ResourceDetailDrawer/index.vue +18 -4
  22. package/components/InstallHelmCharts.vue +656 -0
  23. package/components/LazyImage.vue +60 -4
  24. package/components/Loading.vue +1 -1
  25. package/components/LocaleSelector.vue +7 -2
  26. package/components/Markdown.vue +4 -0
  27. package/components/PaginatedResourceTable.vue +46 -1
  28. package/components/PromptRestore.vue +22 -44
  29. package/components/Resource/Detail/Masthead/composable.ts +16 -0
  30. package/components/Resource/Detail/Masthead/index.vue +37 -0
  31. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +10 -2
  32. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +26 -7
  33. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +8 -1
  34. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -10
  35. package/components/Resource/Detail/Metadata/Rectangle.vue +3 -1
  36. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +10 -17
  37. package/components/Resource/Detail/Metadata/composables.ts +9 -7
  38. package/components/Resource/Detail/Metadata/index.vue +17 -2
  39. package/components/Resource/Detail/Page.vue +35 -21
  40. package/components/Resource/Detail/SpacedRow.vue +1 -1
  41. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +8 -9
  42. package/components/Resource/Detail/TitleBar/composables.ts +5 -5
  43. package/components/Resource/Detail/TitleBar/index.vue +12 -3
  44. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  45. package/components/ResourceDetail/index.vue +569 -72
  46. package/components/ResourceList/index.vue +1 -0
  47. package/components/ResourceTable.vue +6 -1
  48. package/components/ResourceYaml.vue +1 -1
  49. package/components/RichTranslation.vue +106 -0
  50. package/components/SlideInPanelManager.vue +13 -10
  51. package/components/SortableTable/index.vue +5 -5
  52. package/components/SortableTable/selection.js +0 -1
  53. package/components/Tabbed/index.vue +35 -4
  54. package/components/__tests__/LazyImage.spec.ts +121 -0
  55. package/components/__tests__/PromptRestore.test.ts +1 -65
  56. package/components/__tests__/RichTranslation.test.ts +115 -0
  57. package/components/fleet/FleetStatus.vue +4 -0
  58. package/components/fleet/dashboard/ResourcePanel.vue +2 -1
  59. package/components/form/ClusterAppearance.vue +5 -0
  60. package/components/form/FileImageSelector.vue +1 -1
  61. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  62. package/components/form/NameNsDescription.vue +1 -0
  63. package/components/form/Networking.vue +24 -19
  64. package/components/form/ProjectMemberEditor.vue +1 -1
  65. package/components/form/ResourceLabeledSelect.vue +22 -8
  66. package/components/form/ResourceTabs/index.vue +20 -0
  67. package/components/form/SecretSelector.vue +9 -0
  68. package/components/form/SelectOrCreateAuthSecret.vue +6 -3
  69. package/components/form/__tests__/Networking.test.ts +116 -0
  70. package/components/form/labeled-select-utils/labeled-select-pagination.ts +3 -38
  71. package/components/formatter/FleetApplicationSource.vue +25 -17
  72. package/components/formatter/PodImages.vue +1 -1
  73. package/components/formatter/__tests__/LiveDate.test.ts +10 -2
  74. package/components/google/AccountAccess.vue +44 -46
  75. package/components/nav/Favorite.vue +4 -0
  76. package/components/nav/Group.vue +4 -1
  77. package/components/nav/NotificationCenter/Notification.vue +1 -27
  78. package/components/nav/WindowManager/index.vue +3 -3
  79. package/composables/resources.ts +2 -2
  80. package/config/labels-annotations.js +3 -2
  81. package/config/pagination-table-headers.js +8 -1
  82. package/config/product/explorer.js +27 -2
  83. package/config/product/manager.js +0 -1
  84. package/config/query-params.js +10 -0
  85. package/config/router/routes.js +21 -1
  86. package/config/system-namespaces.js +1 -1
  87. package/config/table-headers.js +30 -1
  88. package/config/types.js +1 -1
  89. package/config/version.js +1 -1
  90. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +11 -0
  91. package/detail/__tests__/workload.test.ts +164 -0
  92. package/detail/configmap.vue +33 -75
  93. package/detail/projectsecret.vue +11 -0
  94. package/detail/provisioning.cattle.io.cluster.vue +351 -369
  95. package/detail/secret.vue +49 -308
  96. package/detail/workload/index.vue +38 -21
  97. package/dialog/InstallExtensionDialog.vue +8 -5
  98. package/dialog/RotateEncryptionKeyDialog.vue +10 -30
  99. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  100. package/edit/auth/ldap/__tests__/config.test.ts +14 -0
  101. package/edit/auth/ldap/config.vue +24 -0
  102. package/edit/compliance.cattle.io.clusterscan.vue +1 -1
  103. package/edit/configmap.vue +4 -1
  104. package/edit/fleet.cattle.io.gitrepo.vue +5 -6
  105. package/edit/fleet.cattle.io.helmop.vue +78 -56
  106. package/edit/logging.banzaicloud.io.output/index.vue +1 -1
  107. package/edit/logging.banzaicloud.io.output/providers/awsElasticsearch.vue +5 -6
  108. package/edit/networking.k8s.io.ingress/Certificate.vue +20 -22
  109. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +8 -3
  110. package/edit/networking.k8s.io.ingress/Rule.vue +2 -5
  111. package/edit/networking.k8s.io.ingress/RulePath.vue +17 -11
  112. package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
  113. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +11 -10
  114. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -3
  115. package/edit/networking.k8s.io.networkpolicy/index.vue +17 -17
  116. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
  117. package/edit/provisioning.cattle.io.cluster/rke2.vue +123 -61
  118. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +9 -7
  119. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +22 -13
  120. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +10 -12
  121. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +39 -38
  122. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +41 -19
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +16 -3
  124. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +32 -33
  125. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +9 -10
  126. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +1 -3
  127. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +16 -9
  128. package/edit/secret/basic.vue +1 -0
  129. package/edit/secret/index.vue +126 -15
  130. package/edit/workload/index.vue +5 -14
  131. package/list/projectsecret.vue +345 -0
  132. package/list/provisioning.cattle.io.cluster.vue +1 -69
  133. package/list/secret.vue +109 -0
  134. package/machine-config/__tests__/vmwarevsphere.test.ts +5 -7
  135. package/machine-config/google.vue +9 -1
  136. package/machine-config/vmwarevsphere.vue +7 -17
  137. package/mixins/__tests__/brand.spec.ts +2 -2
  138. package/mixins/chart.js +0 -2
  139. package/mixins/create-edit-view/impl.js +10 -1
  140. package/mixins/resource-fetch-api-pagination.js +11 -12
  141. package/mixins/resource-fetch.js +3 -1
  142. package/models/__tests__/chart.test.ts +111 -80
  143. package/models/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  144. package/models/__tests__/node.test.ts +7 -63
  145. package/models/catalog.cattle.io.app.js +1 -1
  146. package/models/catalog.cattle.io.operation.js +1 -1
  147. package/models/chart.js +36 -20
  148. package/models/cloudcredential.js +2 -163
  149. package/models/cluster/node.js +7 -7
  150. package/models/cluster.x-k8s.io.machine.js +3 -3
  151. package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
  152. package/models/compliance.cattle.io.clusterscan.js +2 -2
  153. package/models/configmap.js +4 -0
  154. package/models/constraints.gatekeeper.sh.constraint.js +1 -1
  155. package/models/fleet-application.js +0 -17
  156. package/models/fleet.cattle.io.cluster.js +2 -2
  157. package/models/fleet.cattle.io.gitrepo.js +15 -1
  158. package/models/fleet.cattle.io.helmop.js +26 -22
  159. package/models/management.cattle.io.setting.js +4 -0
  160. package/models/persistentvolumeclaim.js +1 -1
  161. package/models/pod.js +2 -2
  162. package/models/provisioning.cattle.io.cluster.js +39 -67
  163. package/models/rke.cattle.io.etcdsnapshot.js +1 -1
  164. package/models/secret.js +161 -2
  165. package/models/storage.k8s.io.storageclass.js +2 -2
  166. package/models/workload.js +3 -3
  167. package/package.json +11 -10
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +1 -0
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +4 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/AppChartCardFooter.spec.js +41 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +422 -174
  172. package/pages/c/_cluster/apps/charts/index.vue +46 -35
  173. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  174. package/pages/c/_cluster/explorer/projectsecret.vue +24 -0
  175. package/pages/c/_cluster/fleet/__tests__/index.test.ts +608 -314
  176. package/pages/c/_cluster/fleet/index.vue +103 -45
  177. package/pages/c/_cluster/manager/cloudCredential/index.vue +2 -59
  178. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +10 -3
  179. package/pages/c/_cluster/uiplugins/index.vue +36 -25
  180. package/plugins/dashboard-store/__tests__/normalize.test.ts +223 -0
  181. package/plugins/dashboard-store/__tests__/resource-class.test.ts +191 -0
  182. package/plugins/dashboard-store/__tests__/utils/normalize-usecases.ts +1526 -0
  183. package/plugins/dashboard-store/actions.js +42 -22
  184. package/plugins/dashboard-store/normalize.js +29 -17
  185. package/plugins/dashboard-store/resource-class.js +83 -17
  186. package/plugins/steve/__tests__/getters.test.ts +1 -1
  187. package/plugins/steve/__tests__/subscribe.spec.ts +259 -1
  188. package/plugins/steve/getters.js +8 -2
  189. package/plugins/steve/resourceWatcher.js +10 -3
  190. package/plugins/steve/steve-pagination-utils.ts +14 -3
  191. package/plugins/steve/subscribe.js +192 -19
  192. package/plugins/steve/worker/web-worker.advanced.js +2 -0
  193. package/rancher-components/Card/Card.vue +0 -18
  194. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.test.ts +15 -0
  195. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +65 -0
  196. package/rancher-components/Pill/RcStatusBadge/index.ts +2 -0
  197. package/rancher-components/Pill/RcStatusBadge/types.ts +5 -0
  198. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.test.ts +33 -0
  199. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +75 -0
  200. package/rancher-components/Pill/RcStatusIndicator/index.ts +2 -0
  201. package/rancher-components/Pill/RcStatusIndicator/types.ts +7 -0
  202. package/rancher-components/Pill/types.ts +2 -0
  203. package/rancher-components/RcButton/RcButton.vue +1 -1
  204. package/rancher-components/RcDropdown/RcDropdown.test.ts +98 -0
  205. package/rancher-components/RcDropdown/RcDropdown.vue +5 -0
  206. package/rancher-components/RcDropdown/RcDropdownItem.vue +7 -1
  207. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +2 -1
  208. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +2 -1
  209. package/rancher-components/RcDropdown/useDropdownContext.ts +21 -0
  210. package/rancher-components/RcDropdown/useDropdownItem.ts +30 -1
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +20 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +40 -6
  213. package/store/__tests__/catalog.test.ts +93 -1
  214. package/store/aws.js +19 -8
  215. package/store/catalog.js +8 -3
  216. package/types/kube/kube-api.ts +12 -0
  217. package/types/resources/settings.d.ts +1 -1
  218. package/types/shell/index.d.ts +643 -585
  219. package/types/store/pagination.types.ts +16 -6
  220. package/types/uiplugins.ts +73 -0
  221. package/utils/__tests__/back-off.test.ts +354 -0
  222. package/utils/__tests__/create-yaml.test.ts +235 -0
  223. package/utils/__tests__/kontainer.test.ts +19 -0
  224. package/utils/__tests__/uiplugins.test.ts +84 -0
  225. package/utils/back-off.ts +176 -0
  226. package/utils/create-yaml.js +103 -9
  227. package/utils/dynamic-importer.js +8 -0
  228. package/utils/kontainer.ts +3 -5
  229. package/utils/pagination-utils.ts +18 -0
  230. package/utils/style.ts +3 -0
  231. package/utils/uiplugins.ts +29 -2
  232. package/utils/validators/__tests__/setting.test.js +92 -0
  233. package/utils/validators/formRules/__tests__/index.test.ts +88 -7
  234. package/utils/validators/formRules/index.ts +83 -8
  235. package/utils/validators/setting.js +17 -0
  236. package/cloud-credential/__tests__/harvester.test.ts +0 -18
  237. package/components/ResourceDetail/__tests__/index.test.ts +0 -135
  238. package/components/ResourceDetail/legacy.vue +0 -562
  239. package/components/formatter/CloudCredExpired.vue +0 -69
  240. package/models/etcdbackup.js +0 -45
  241. package/pages/explorer/resource/detail/configmap.vue +0 -42
  242. package/pages/explorer/resource/detail/secret.vue +0 -50
  243. package/utils/aws.js +0 -0
@@ -106,57 +106,55 @@ export default defineComponent({
106
106
  </script>
107
107
 
108
108
  <template>
109
- <div>
109
+ <div
110
+ :class="{'showing-form': !credential}"
111
+ class="credential-project"
112
+ >
110
113
  <div
111
- :class="{'showing-form': !credential}"
112
- class="credential-project"
114
+ v-show="!!credential"
115
+ class="project mb-10"
113
116
  >
114
- <div
115
- v-show="!!credential"
116
- class="project mb-10"
117
- >
118
- <LabeledInput
119
- :disabled="isAuthenticated"
120
- :value="project"
121
- label-key="gke.project.label"
122
- required
123
- @update:value="(val) => $emit('update:project', val.trim())"
124
- />
125
- </div>
126
- <div
127
- :class="{'view': isView}"
128
- class="select-credential-container mb-10"
129
- >
130
- <SelectCredential
131
- :value="credential"
132
- data-testid="crugke-select-credential"
133
- :mode="(isView|| isAuthenticated) ? VIEW : CREATE"
134
- provider="gcp"
135
- :default-on-cancel="true"
136
- :showing-form="!credential"
137
- class="select-credential"
138
- :cancel="()=>$emit('cancel-credential')"
139
- @update:value="$emit('update:credential', $event)"
140
- @credential-created="parseNewCredential"
141
- />
142
- </div>
117
+ <LabeledInput
118
+ :disabled="isAuthenticated"
119
+ :value="project"
120
+ label-key="gke.project.label"
121
+ required
122
+ @update:value="(val) => $emit('update:project', val.trim())"
123
+ />
143
124
  </div>
144
125
  <div
145
- v-if="!isView && !isAuthenticated"
146
- class="row mt-10"
126
+ :class="{'view': isView}"
127
+ class="select-credential-container"
147
128
  >
148
- <div
149
- v-show="!!credential"
150
- class="auth-button-container mb-10"
151
- >
152
- <AsyncButton
153
- :disabled="!credential || !project || isAuthenticated"
154
- type="button"
155
- class="btn"
156
- mode="authenticate"
157
- @click="testProjectId"
158
- />
159
- </div>
129
+ <SelectCredential
130
+ :value="credential"
131
+ data-testid="crugke-select-credential"
132
+ :mode="(isView|| isAuthenticated) ? VIEW : CREATE"
133
+ provider="gcp"
134
+ :default-on-cancel="true"
135
+ :showing-form="!credential"
136
+ class="select-credential"
137
+ :cancel="()=>$emit('cancel-credential')"
138
+ @update:value="$emit('update:credential', $event)"
139
+ @credential-created="parseNewCredential"
140
+ />
141
+ </div>
142
+ </div>
143
+ <div
144
+ v-if="!isView && !isAuthenticated && !!credential"
145
+ class="row mt-10"
146
+ >
147
+ <div
148
+ v-show="!!credential"
149
+ class="auth-button-container mb-10"
150
+ >
151
+ <AsyncButton
152
+ :disabled="!credential || !project || isAuthenticated"
153
+ type="button"
154
+ class="btn"
155
+ mode="authenticate"
156
+ @click="testProjectId"
157
+ />
160
158
  </div>
161
159
  </div>
162
160
  </template>
@@ -56,5 +56,9 @@ export default {
56
56
  &.icon-star-closed {
57
57
  color: var(--body-text);
58
58
  }
59
+
60
+ &:focus-visible {
61
+ @include focus-outline;
62
+ }
59
63
  }
60
64
  </style>
@@ -226,7 +226,10 @@ export default {
226
226
  class="accordion"
227
227
  :class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': isGroupActive }"
228
228
  >
229
- <div class="accordion-item">
229
+ <div
230
+ v-if="showHeader || (!onlyHasOverview && canCollapse)"
231
+ class="accordion-item"
232
+ >
230
233
  <div
231
234
  v-if="showHeader"
232
235
  class="header"
@@ -31,7 +31,7 @@ const unreadCount = computed<number>(() => store.getters['notifications/unreadCo
31
31
  const dateFormat = escapeHtml( store.getters['prefs/get'](DATE_FORMAT));
32
32
  const timeFormat = escapeHtml( store.getters['prefs/get'](TIME_FORMAT));
33
33
 
34
- const { close } = useDropdownItem();
34
+ const { close, scrollIntoView } = useDropdownItem();
35
35
 
36
36
  // Outer element for the notification
37
37
  const dropdownMenuItem = ref<HTMLElement>();
@@ -217,32 +217,6 @@ const findNewIndex = (shouldAdvance: boolean, activeIndex: number, itemsArr: Ele
217
217
 
218
218
  return newIndex;
219
219
  };
220
-
221
- /**
222
- * Ensure we scroll the item into view smoothly
223
- * @param event FocusIn Event
224
- */
225
- const scrollIntoView = (event: Event) => {
226
- const target = event.target;
227
-
228
- if (target instanceof HTMLElement) {
229
- const t = target as HTMLElement;
230
-
231
- // If a button was clicked, then do not scroll into view, as this will scroll to make the button
232
- // visible and the click will be ignored - so just return, so that the click works as expected
233
- if (t.tagName === 'BUTTON') {
234
- return;
235
- }
236
- }
237
-
238
- if (target instanceof HTMLElement) {
239
- target?.scrollIntoView({
240
- behavior: 'smooth',
241
- block: 'center',
242
- inline: 'nearest',
243
- });
244
- }
245
- };
246
220
  </script>
247
221
 
248
222
  <template>
@@ -383,7 +383,7 @@ export default {
383
383
  <span class="tab-label"> {{ tab.label }}</span>
384
384
  <i
385
385
  data-testid="wm-tab-close-button"
386
- class="closer icon icon-fw icon-x wm-closer-button"
386
+ class="closer icon icon-x wm-closer-button"
387
387
  :alt="t('wm.containerShell.closeShellTab', { tab: tab.label })"
388
388
  tabindex="0"
389
389
  :aria-label="t('windowmanager.closeTab', { tabId: tab.id })"
@@ -428,9 +428,9 @@ export default {
428
428
  </div>
429
429
  </div>
430
430
  <div
431
- v-for="(tab, i) in tabs"
431
+ v-for="tab in tabs"
432
432
  :id="`panel-${tab.id}`"
433
- :key="i"
433
+ :key="tab.id"
434
434
  class="body"
435
435
  :class="{'active': tab.id === active}"
436
436
  draggable="false"
@@ -21,7 +21,7 @@ export const useResourceIdentifiers = (type: ResourceType) => {
21
21
  };
22
22
  };
23
23
 
24
- export const useFetchResourceWithId = async(type: ResourceType, id: IdType) => {
24
+ export const useFetchResourceWithId = async(type: ResourceType, id: IdType, inStore = 'cluster') => {
25
25
  const store = useStore();
26
26
  const i18n = useI18n(store);
27
27
 
@@ -29,7 +29,7 @@ export const useFetchResourceWithId = async(type: ResourceType, id: IdType) => {
29
29
  const idValue = toValue(id);
30
30
 
31
31
  try {
32
- return await store.dispatch('cluster/find', { type: typeValue, id: idValue });
32
+ return await store.dispatch(`${ inStore }/find`, { type: typeValue, id: idValue });
33
33
  } catch (ex: any) {
34
34
  if (ex.status === 404 || ex.status === 403) {
35
35
  store.dispatch('loadingError', new Error(i18n.t('nav.failWhale.resourceIdNotFound', { resource: type, fqid: id }, true)));
@@ -17,6 +17,8 @@ export const AZURE_MIGRATED = 'auth.cattle.io/azuread-endpoint-migrated';
17
17
  export const WORKSPACE_ANNOTATION = 'objectset.rio.cattle.io/id';
18
18
  export const NODE_ARCHITECTURE = 'kubernetes.io/arch';
19
19
  export const IMPORTED_CLUSTER_VERSION_MANAGEMENT = 'rancher.io/imported-cluster-version-management';
20
+ export const UI_PROJECT_SECRET = 'management.cattle.io/project-scoped-secret';
21
+ export const UI_PROJECT_SECRET_COPY = 'management.cattle.io/project-scoped-secret-copy';
20
22
 
21
23
  export const KUBERNETES = {
22
24
  SERVICE_ACCOUNT_UID: 'kubernetes.io/service-account.uid',
@@ -163,6 +165,7 @@ export const HCI = {
163
165
  NETWORK_TYPE: 'network.harvesterhci.io/type',
164
166
  CLUSTER_NETWORK: 'network.harvesterhci.io/clusternetwork',
165
167
  PRIMARY_SERVICE: 'cloudprovider.harvesterhci.io/primary-service',
168
+ CPU_MANAGER: 'cpumanager',
166
169
  };
167
170
 
168
171
  // Annotations that can be on management.cattle.io.cluster to configure a custom badge
@@ -192,8 +195,6 @@ export const SYSTEM_LABELS = [
192
195
  'egress.rke2.io'
193
196
  ];
194
197
 
195
- export const CLOUD_CREDENTIALS = { EXPIRATION: 'rancher.io/expiration-timestamp' };
196
-
197
198
  export const OIDC_CLIENT_SECRET_ANNOTATIONS = {
198
199
  CREATE: 'cattle.io/oidc-client-secret-create',
199
200
  REGEN: 'cattle.io/oidc-client-secret-regenerate',
@@ -1,7 +1,9 @@
1
+ import { UI_PROJECT_SECRET_COPY } from '@shell/config/labels-annotations';
1
2
  import {
2
3
  STATE, NAME as NAME_COL, NAMESPACE as NAMESPACE_COL, AGE, OBJECT,
3
4
  EVENT_LAST_SEEN_TIME,
4
- EVENT_TYPE
5
+ EVENT_TYPE,
6
+ SECRET_CLONE
5
7
  } from '@shell/config/table-headers';
6
8
 
7
9
  // This file contains table headers
@@ -78,3 +80,8 @@ export const STEVE_LIST_GROUPS = [{
78
80
  tooltipKey: 'resourceTable.groupBy.namespace',
79
81
  groupLabelKey: 'groupByLabel',
80
82
  }];
83
+
84
+ export const STEVE_SECRET_CLONE = {
85
+ ...SECRET_CLONE,
86
+ sort: `metadata.annotations[${ UI_PROJECT_SECRET_COPY }]`,
87
+ };
@@ -21,7 +21,7 @@ import {
21
21
  HPA_REFERENCE, MIN_REPLICA, MAX_REPLICA, CURRENT_REPLICA,
22
22
  ACCESS_KEY, DESCRIPTION, EXPIRES, EXPIRY_STATE, LAST_USED, SUB_TYPE, AGE_NORMAN, SCOPE_NORMAN, PERSISTENT_VOLUME_CLAIM, RECLAIM_POLICY, PV_REASON, WORKLOAD_HEALTH_SCALE, POD_RESTARTS,
23
23
  DURATION, MESSAGE, REASON, EVENT_TYPE, OBJECT, ROLE, ROLES, VERSION, INTERNAL_EXTERNAL_IP, KUBE_NODE_OS, CPU, RAM, SECRET_DATA,
24
- EVENT_LAST_SEEN_TIME
24
+ EVENT_LAST_SEEN_TIME,
25
25
  } from '@shell/config/table-headers';
26
26
 
27
27
  import { DSL } from '@shell/store/type-map';
@@ -94,6 +94,7 @@ export function init(store) {
94
94
  PVC,
95
95
  STORAGE_CLASS,
96
96
  SECRET,
97
+ VIRTUAL_TYPES.PROJECT_SECRETS,
97
98
  CONFIG_MAP
98
99
  ], 'storage');
99
100
  basicType([
@@ -187,7 +188,12 @@ export function init(store) {
187
188
  configureType(NORMAN.CLUSTER_ROLE_TEMPLATE_BINDING, { depaginate: dePaginateNormanBindings });
188
189
  configureType(NORMAN.PROJECT_ROLE_TEMPLATE_BINDING, { depaginate: dePaginateNormanBindings });
189
190
  configureType(SNAPSHOT, { depaginate: true });
190
- configureType(NORMAN.ETCD_BACKUP, { depaginate: true });
191
+
192
+ configureType(SECRET, { showListMasthead: false });
193
+ weightType(SECRET, 1, false);
194
+
195
+ configureType(VIRTUAL_TYPES.PROJECT_SECRETS, { showListMasthead: false });
196
+ weightType(VIRTUAL_TYPES.PROJECT_SECRETS, 2, false);
191
197
 
192
198
  configureType(EVENT, { limit: 500 });
193
199
  weightType(EVENT, -1, true);
@@ -624,6 +630,25 @@ export function init(store) {
624
630
  exact: true,
625
631
  });
626
632
 
633
+ virtualType({
634
+ label: store.getters['i18n/t'](`typeLabel.${ VIRTUAL_TYPES.PROJECT_SECRETS }`, { count: 2 }),
635
+ group: 'storage',
636
+ icon: 'globe',
637
+ namespaced: false,
638
+ ifRancherCluster: true,
639
+ name: VIRTUAL_TYPES.PROJECT_SECRETS,
640
+ weight: -1,
641
+ route: {
642
+ name: 'c-cluster-product-resource',
643
+ params: { resource: VIRTUAL_TYPES.PROJECT_SECRETS }
644
+ },
645
+ exact: true,
646
+ ifHaveType: [{
647
+ store: 'management',
648
+ type: SECRET
649
+ }],
650
+ });
651
+
627
652
  // Ignore these types as they are managed through the settings product
628
653
  ignoreType(MANAGEMENT.FEATURE);
629
654
  ignoreType(MANAGEMENT.SETTING);
@@ -67,7 +67,6 @@ export function init(store) {
67
67
  ]);
68
68
 
69
69
  configureType(SNAPSHOT, { depaginate: true });
70
- configureType(NORMAN.ETCD_BACKUP, { depaginate: true });
71
70
 
72
71
  configureType(CAPI.RANCHER_CLUSTER, {
73
72
  showListMasthead: false, namespaced: false, alias: [HCI.CLUSTER]
@@ -80,5 +80,15 @@ export const CLOUD_CREDENTIAL = 'cloud';
80
80
  export const PROJECT_ID = 'projectId';
81
81
  export const FLAT_VIEW = 'flatView';
82
82
 
83
+ /**
84
+ * Used on the secret create/edit page to determine
85
+ * 1. if the user is creating a normal secret, or a project scoped secret
86
+ * 2. where to return the user on cancel/save
87
+ */
88
+ export const SECRET_QUERY_PARAMS = {
89
+ NAMESPACED: 'namespaced',
90
+ PROJECT_SCOPED: 'project-scoped'
91
+ };
92
+ export const SECRET_SCOPE = 'scope';
83
93
  // RANCHER OIDC CLIENT
84
94
  export const RANCHER_AS_OIDC_QUERY_PARAMS = ['scope', 'client_id', 'redirect_uri', 'response_type'];
@@ -1,7 +1,9 @@
1
1
  import { NAME as APPS } from '@shell/config/product/apps';
2
2
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
3
3
  import { NAME as MANAGER } from '@shell/config/product/manager';
4
- import { CAPI, MANAGEMENT, BACKUP_RESTORE, COMPLIANCE } from '@shell/config/types';
4
+ import {
5
+ CAPI, MANAGEMENT, BACKUP_RESTORE, COMPLIANCE, VIRTUAL_TYPES
6
+ } from '@shell/config/types';
5
7
  import { NAME as AUTH } from '@shell/config/product/auth';
6
8
 
7
9
  // All these imports are related to the install-redirect.js navigation guard.
@@ -313,6 +315,20 @@ export default [
313
315
  component: () => interopDefault(import('@shell/pages/c/_cluster/apps/charts/install.vue')),
314
316
  name: 'c-cluster-apps-charts-install',
315
317
  },
318
+ {
319
+ path: '/c/:cluster/apps/catalog.cattle.io.clusterrepo',
320
+ name: 'c-cluster-apps-catalog-repo',
321
+ redirect(to) {
322
+ return {
323
+ name: 'c-cluster-product-resource',
324
+ params: {
325
+ ...to.params,
326
+ product: APPS,
327
+ resource: 'catalog.cattle.io.clusterrepo',
328
+ }
329
+ };
330
+ },
331
+ },
316
332
  ]
317
333
  },
318
334
  {
@@ -493,6 +509,10 @@ export default [
493
509
  component: () => interopDefault(import('@shell/pages/c/_cluster/_product/_resource/_id.vue')),
494
510
  name: 'c-cluster-product-resource-id',
495
511
  meta: { asyncSetup: true }
512
+ }, {
513
+ path: `/c/:cluster/:product/${ VIRTUAL_TYPES.PROJECT_SECRETS }/:namespace/:id`,
514
+ component: () => interopDefault(import(`@shell/pages/c/_cluster/explorer/${ VIRTUAL_TYPES.PROJECT_SECRETS }.vue`)),
515
+ name: `c-cluster-product-${ VIRTUAL_TYPES.PROJECT_SECRETS }-namespace-id`,
496
516
  }, {
497
517
  path: '/c/:cluster/:product/:resource/:namespace/:id',
498
518
  component: () => interopDefault(import('@shell/pages/c/_cluster/_product/_resource/_namespace/_id.vue')),
@@ -12,7 +12,7 @@ export default [
12
12
  'linkerd',
13
13
  'security-scan',
14
14
  'tekton-pipelines',
15
- 'rancher-compliance-system',
15
+ 'compliance-operator-system',
16
16
  'istio-system',
17
17
  'longhorn-system',
18
18
  ];
@@ -1,4 +1,4 @@
1
- import { CATTLE_PUBLIC_ENDPOINTS } from '@shell/config/labels-annotations';
1
+ import { CATTLE_PUBLIC_ENDPOINTS, UI_PROJECT_SECRET_COPY } from '@shell/config/labels-annotations';
2
2
  import { NODE as NODE_TYPE } from '@shell/config/types';
3
3
  import { COLUMN_BREAKPOINTS } from '@shell/types/store/type-map';
4
4
 
@@ -383,6 +383,29 @@ export const SECRET_DATA = {
383
383
  formatter: 'SecretData'
384
384
  };
385
385
 
386
+ export const SECRET_CLONE = {
387
+ name: 'secret-clone',
388
+ labelKey: 'tableHeaders.secret.project-clone',
389
+ tooltip: 'tableHeaders.secret.project-clone-tooltip',
390
+ value: 'isProjectSecretCopy',
391
+ sort: `metadata.annotations."${ UI_PROJECT_SECRET_COPY }"`,
392
+ search: false,
393
+ formatter: 'Checked',
394
+ };
395
+
396
+ export const SECRET_PROJECT_SCOPED = {
397
+ name: 'secret-project-scoped',
398
+ labelKey: 'tableHeaders.secret.project-scoped',
399
+ tooltip: 'tableHeaders.secret.project-scoped-tooltip',
400
+ value: 'clusterAndProjectLabel',
401
+ // Cannot _sort_ upstream secrets by if they are cluster scoped
402
+ // https://github.com/rancher/rancher/issues/51001
403
+ // metadata.labels[management.cattle.io/project-scoped-secret] - covers both cluster scoped AND clones
404
+ // metadata.annotations[management.cattle.io/project-scoped-secret-copy]
405
+ // sort: [`metadata.labels[${ UI_PROJECT_SECRET }]`, `metadata.annotations[${ UI_PROJECT_SECRET_COPY }]`],
406
+ search: false,
407
+ };
408
+
386
409
  export const TARGET_KIND = {
387
410
  name: 'target-kind',
388
411
  labelKey: 'tableHeaders.targetKind',
@@ -1145,3 +1168,9 @@ export const UI_PLUGIN_CATALOG = [
1145
1168
  value: 'repo.metadata.name'
1146
1169
  }
1147
1170
  ];
1171
+
1172
+ // SECRETS
1173
+ export const PROJECT = {
1174
+ name: 'project',
1175
+ labelKey: 'tableHeaders.project',
1176
+ };
package/config/types.js CHANGED
@@ -14,7 +14,6 @@ export const STEVE = {
14
14
  export const NORMAN = {
15
15
  APP: 'app',
16
16
  AUTH_CONFIG: 'authconfig',
17
- ETCD_BACKUP: 'etcdbackup',
18
17
  CLUSTER: 'cluster',
19
18
  CLUSTER_TOKEN: 'clusterregistrationtoken',
20
19
  CLUSTER_ROLE_TEMPLATE_BINDING: 'clusterroletemplatebinding',
@@ -316,6 +315,7 @@ export const VIRTUAL_TYPES = {
316
315
  CLUSTER_MEMBERS: 'cluster-members',
317
316
  PROJECT_NAMESPACES: 'projects-namespaces',
318
317
  NAMESPACES: 'namespaces',
318
+ PROJECT_SECRETS: 'projectsecret',
319
319
  JWT_AUTHENTICATION: 'jwt.authentication'
320
320
  };
321
321
 
package/config/version.js CHANGED
@@ -30,4 +30,4 @@ export function setKubeVersionData(v) {
30
30
  _kubeVersionData = JSON.parse(JSON.stringify(v));
31
31
  }
32
32
 
33
- export const CURRENT_RANCHER_VERSION = '2.11';
33
+ export const CURRENT_RANCHER_VERSION = '2.12';
@@ -1,11 +1,22 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
2
  import ProvisioningCattleIoCluster from '@shell/detail/provisioning.cattle.io.cluster.vue';
3
+ import * as MastheadComposable from '@shell/components/Resource/Detail/Masthead/composable';
3
4
 
4
5
  jest.mock('@shell/utils/clipboard', () => {
5
6
  return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
6
7
  });
7
8
 
9
+ jest.mock('@shell/components/Resource/Detail/Masthead/composable');
10
+
8
11
  describe('view: provisioning.cattle.io.cluster', () => {
12
+ const useDefaultMastheadPropsSpy = jest.spyOn(MastheadComposable, 'useDefaultMastheadProps');
13
+
14
+ beforeEach(() => {
15
+ jest.clearAllMocks();
16
+
17
+ useDefaultMastheadPropsSpy.mockReturnValue({} as any);
18
+ });
19
+
9
20
  const mockStore = {
10
21
  getters: {
11
22
  'management/canList': () => true,
@@ -0,0 +1,164 @@
1
+ import Workload from '@shell/detail/workload/index.vue';
2
+
3
+ const { findMatchingIngresses } = Workload.methods;
4
+
5
+ describe('findMatchingIngresses', () => {
6
+ it('should do nothing if ingress schema is not present', () => {
7
+ const mockThis = {
8
+ ingressSchema: undefined,
9
+ matchingIngresses: [],
10
+ };
11
+
12
+ findMatchingIngresses.call(mockThis);
13
+ expect(mockThis.matchingIngresses).toStrictEqual([]);
14
+ });
15
+
16
+ it('should not find any ingresses if there are none', () => {
17
+ const mockThis = {
18
+ ingressSchema: true,
19
+ allIngresses: [],
20
+ matchingServices: [],
21
+ matchingIngresses: [],
22
+ value: { metadata: { namespace: 'test' } }
23
+ };
24
+
25
+ findMatchingIngresses.call(mockThis);
26
+ expect(mockThis.matchingIngresses).toStrictEqual([]);
27
+ });
28
+
29
+ it('should find matching ingresses', () => {
30
+ const mockThis = {
31
+ ingressSchema: true,
32
+ allIngresses: [
33
+ { // matching
34
+ metadata: { namespace: 'test' },
35
+ spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service1' } } }] } }] }
36
+ }
37
+ ],
38
+ matchingServices: [{ metadata: { name: 'service1' } }],
39
+ matchingIngresses: [],
40
+ value: { metadata: { namespace: 'test' } }
41
+ };
42
+
43
+ findMatchingIngresses.call(mockThis);
44
+ expect(mockThis.matchingIngresses).toHaveLength(1);
45
+ expect(mockThis.matchingIngresses[0]).toStrictEqual(mockThis.allIngresses[0]);
46
+ });
47
+
48
+ it('should not match ingresses from other namespaces', () => {
49
+ const mockThis = {
50
+ ingressSchema: true,
51
+ allIngresses: [
52
+ { // not matching
53
+ metadata: { namespace: 'other' },
54
+ spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service1' } } }] } }] }
55
+ }
56
+ ],
57
+ matchingServices: [{ metadata: { name: 'service1' } }],
58
+ matchingIngresses: [],
59
+ value: { metadata: { namespace: 'test' } }
60
+ };
61
+
62
+ findMatchingIngresses.call(mockThis);
63
+ expect(mockThis.matchingIngresses).toHaveLength(0);
64
+ });
65
+
66
+ it('should not match ingresses pointing to other services', () => {
67
+ const mockThis = {
68
+ ingressSchema: true,
69
+ allIngresses: [
70
+ { // not matching
71
+ metadata: { namespace: 'test' },
72
+ spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service2' } } }] } }] }
73
+ }
74
+ ],
75
+ matchingServices: [{ metadata: { name: 'service1' } }],
76
+ matchingIngresses: [],
77
+ value: { metadata: { namespace: 'test' } }
78
+ };
79
+
80
+ findMatchingIngresses.call(mockThis);
81
+ expect(mockThis.matchingIngresses).toHaveLength(0);
82
+ });
83
+
84
+ it('should handle ingresses with no rules', () => {
85
+ const mockThis = {
86
+ ingressSchema: true,
87
+ allIngresses: [
88
+ {
89
+ metadata: { namespace: 'test' },
90
+ spec: { }
91
+ }
92
+ ],
93
+ matchingServices: [{ metadata: { name: 'service1' } }],
94
+ matchingIngresses: [],
95
+ value: { metadata: { namespace: 'test' } }
96
+ };
97
+
98
+ findMatchingIngresses.call(mockThis);
99
+ expect(mockThis.matchingIngresses).toHaveLength(0);
100
+ });
101
+
102
+ it('should handle ingress rules with no paths', () => {
103
+ const mockThis = {
104
+ ingressSchema: true,
105
+ allIngresses: [
106
+ {
107
+ metadata: { namespace: 'test' },
108
+ spec: { rules: [{ http: {} }] }
109
+ }
110
+ ],
111
+ matchingServices: [{ metadata: { name: 'service1' } }],
112
+ matchingIngresses: [],
113
+ value: { metadata: { namespace: 'test' } }
114
+ };
115
+
116
+ findMatchingIngresses.call(mockThis);
117
+ expect(mockThis.matchingIngresses).toHaveLength(0);
118
+ });
119
+
120
+ it('should handle ingress paths with no backend service', () => {
121
+ const mockThis = {
122
+ ingressSchema: true,
123
+ allIngresses: [
124
+ {
125
+ metadata: { namespace: 'test' },
126
+ spec: { rules: [{ http: { paths: [{ backend: {} }] } }] }
127
+ }
128
+ ],
129
+ matchingServices: [{ metadata: { name: 'service1' } }],
130
+ matchingIngresses: [],
131
+ value: { metadata: { namespace: 'test' } }
132
+ };
133
+
134
+ findMatchingIngresses.call(mockThis);
135
+ expect(mockThis.matchingIngresses).toHaveLength(0);
136
+ });
137
+
138
+ it('should find one of many ingresses', () => {
139
+ const mockThis = {
140
+ ingressSchema: true,
141
+ allIngresses: [
142
+ { // not matching
143
+ metadata: { namespace: 'other' },
144
+ spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service1' } } }] } }] }
145
+ },
146
+ { // matching
147
+ metadata: { namespace: 'test' },
148
+ spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service1' } } }] } }] }
149
+ },
150
+ { // not matching
151
+ metadata: { namespace: 'test' },
152
+ spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'service2' } } }] } }] }
153
+ }
154
+ ],
155
+ matchingServices: [{ metadata: { name: 'service1' } }],
156
+ matchingIngresses: [],
157
+ value: { metadata: { namespace: 'test' } }
158
+ };
159
+
160
+ findMatchingIngresses.call(mockThis);
161
+ expect(mockThis.matchingIngresses).toHaveLength(1);
162
+ expect(mockThis.matchingIngresses[0]).toStrictEqual(mockThis.allIngresses[1]);
163
+ });
164
+ });