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

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 (97) hide show
  1. package/assets/styles/base/_variables.scss +17 -11
  2. package/assets/styles/themes/_dark.scss +2 -0
  3. package/assets/styles/themes/_light.scss +8 -2
  4. package/assets/translations/en-us.yaml +26 -4
  5. package/components/CodeMirror.vue +1 -1
  6. package/components/Drawer/Chrome.vue +0 -1
  7. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +26 -2
  8. package/components/Drawer/ResourceDetailDrawer/composables.ts +4 -1
  9. package/components/Drawer/ResourceDetailDrawer/index.vue +1 -0
  10. package/components/Loading.vue +1 -1
  11. package/components/PaginatedResourceTable.vue +46 -1
  12. package/components/PromptRestore.vue +22 -44
  13. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +10 -2
  14. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +21 -2
  15. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +8 -1
  16. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -10
  17. package/components/Resource/Detail/Metadata/Rectangle.vue +3 -1
  18. package/components/Resource/Detail/SpacedRow.vue +1 -1
  19. package/components/Resource/Detail/TitleBar/composables.ts +4 -3
  20. package/components/Resource/Detail/TitleBar/index.vue +2 -2
  21. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  22. package/components/ResourceDetail/index.vue +5 -3
  23. package/components/ResourceList/index.vue +1 -0
  24. package/components/ResourceTable.vue +6 -1
  25. package/components/ResourceYaml.vue +1 -1
  26. package/components/RichTranslation.vue +106 -0
  27. package/components/SlideInPanelManager.vue +3 -7
  28. package/components/SortableTable/index.vue +1 -1
  29. package/components/SortableTable/selection.js +0 -1
  30. package/components/Tabbed/index.vue +6 -1
  31. package/components/__tests__/PromptRestore.test.ts +1 -65
  32. package/components/__tests__/RichTranslation.test.ts +115 -0
  33. package/components/fleet/dashboard/ResourcePanel.vue +2 -1
  34. package/components/form/FileImageSelector.vue +1 -1
  35. package/components/form/NameNsDescription.vue +1 -0
  36. package/components/form/Networking.vue +24 -19
  37. package/components/form/ResourceLabeledSelect.vue +4 -3
  38. package/components/form/SelectOrCreateAuthSecret.vue +6 -3
  39. package/components/form/__tests__/Networking.test.ts +116 -0
  40. package/components/formatter/PodImages.vue +1 -1
  41. package/components/formatter/__tests__/LiveDate.test.ts +10 -2
  42. package/components/google/AccountAccess.vue +44 -46
  43. package/components/nav/Group.vue +4 -1
  44. package/composables/resources.ts +2 -2
  45. package/config/labels-annotations.js +2 -0
  46. package/config/pagination-table-headers.js +8 -1
  47. package/config/product/explorer.js +27 -2
  48. package/config/product/manager.js +0 -1
  49. package/config/query-params.js +10 -0
  50. package/config/router/routes.js +21 -1
  51. package/config/system-namespaces.js +1 -1
  52. package/config/table-headers.js +30 -1
  53. package/config/types.js +1 -1
  54. package/config/version.js +1 -1
  55. package/detail/provisioning.cattle.io.cluster.vue +3 -47
  56. package/dialog/RotateEncryptionKeyDialog.vue +10 -30
  57. package/edit/auth/ldap/__tests__/config.test.ts +14 -0
  58. package/edit/auth/ldap/config.vue +24 -0
  59. package/edit/compliance.cattle.io.clusterscan.vue +1 -1
  60. package/edit/configmap.vue +4 -1
  61. package/edit/networking.k8s.io.ingress/Certificate.vue +12 -12
  62. package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
  63. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
  64. package/edit/provisioning.cattle.io.cluster/rke2.vue +102 -48
  65. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +22 -13
  66. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +2 -2
  67. package/edit/secret/basic.vue +1 -0
  68. package/edit/secret/index.vue +126 -15
  69. package/list/projectsecret.vue +345 -0
  70. package/list/secret.vue +109 -0
  71. package/mixins/__tests__/brand.spec.ts +2 -2
  72. package/mixins/create-edit-view/impl.js +10 -1
  73. package/mixins/resource-fetch-api-pagination.js +9 -9
  74. package/mixins/resource-fetch.js +3 -1
  75. package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
  76. package/models/fleet.cattle.io.cluster.js +2 -2
  77. package/models/provisioning.cattle.io.cluster.js +24 -28
  78. package/models/secret.js +157 -2
  79. package/package.json +2 -2
  80. package/pages/c/_cluster/apps/charts/index.vue +46 -35
  81. package/pages/c/_cluster/explorer/projectsecret.vue +34 -0
  82. package/pages/c/_cluster/fleet/index.vue +0 -1
  83. package/pages/explorer/resource/detail/projectsecret.vue +9 -0
  84. package/pages/explorer/resource/detail/secret.vue +18 -5
  85. package/plugins/dashboard-store/__tests__/normalize.test.ts +223 -0
  86. package/plugins/dashboard-store/__tests__/resource-class.test.ts +191 -0
  87. package/plugins/dashboard-store/__tests__/utils/normalize-usecases.ts +1526 -0
  88. package/plugins/dashboard-store/normalize.js +29 -17
  89. package/plugins/dashboard-store/resource-class.js +52 -17
  90. package/plugins/steve/steve-pagination-utils.ts +14 -3
  91. package/types/kube/kube-api.ts +12 -0
  92. package/types/shell/index.d.ts +616 -558
  93. package/types/store/pagination.types.ts +16 -6
  94. package/utils/__tests__/create-yaml.test.ts +235 -0
  95. package/utils/create-yaml.js +103 -9
  96. package/utils/pagination-utils.ts +18 -0
  97. package/models/etcdbackup.js +0 -45
@@ -42,25 +42,31 @@ $z-indexes: (
42
42
 
43
43
  cruFooter: 19,
44
44
 
45
- modalOverlay: 20,
46
- modalContent: 21,
45
+ loadingMain: 51,
47
46
 
48
- tooltip: 30,
47
+ slide-in: 52,
49
48
 
50
- dropdownOverlay: 40,
51
- dropdownContent: 41,
49
+ modalOverlay: 53,
50
+ modalContent: 54,
52
51
 
53
- loadingMain: 51
52
+ dropdownOverlay: 55,
53
+ dropdownContent: 56,
54
+
55
+ tooltip: 57,
54
56
  );
55
57
 
56
58
  // Usage Example:
57
59
  // @media only screen and (min-width: map-get($breakpoints, '--viewport-*')) {
58
60
  // }
59
61
  $breakpoints: (
60
- '--viewport-4': 480px, // Phone
61
- '--viewport-7': 768px, // Tablet
62
- '--viewport-9': 992px, // Laptop/Desktop
63
- '--viewport-12': 1281px, // Desktop
62
+ '--viewport-4': 480px,
63
+ // Phone
64
+ '--viewport-7': 768px,
65
+ // Tablet
66
+ '--viewport-9': 992px,
67
+ // Laptop/Desktop
68
+ '--viewport-12': 1281px,
69
+ // Desktop
64
70
  );
65
71
 
66
72
  $font-size-h2: 21px;
@@ -68,4 +74,4 @@ $font-size-h2: 21px;
68
74
  // Global spacing variables
69
75
  $space-s: 10px;
70
76
  $space-m: 20px;
71
- $space-l: 40px;
77
+ $space-l: 40px;
@@ -51,6 +51,7 @@
51
51
  --slider-light-bg-right : #{rgba($darker, 0)};
52
52
 
53
53
  --muted : #{$disabled};
54
+ --deemphasized : #{$disabled};
54
55
 
55
56
  --body-bg : #{$darker};
56
57
  --body-text : #{$lightest};
@@ -94,6 +95,7 @@
94
95
 
95
96
  --modal-bg : #{$dark};
96
97
  --modal-border : #{$medium};
98
+ --subtle-overlay-bg : #{rgba($darkest, 0.75)};
97
99
  --overlay-bg : #{rgba($darkest, 0.75)};
98
100
  --shadow : #{rgba($darkest, 0.9)};
99
101
 
@@ -156,11 +156,16 @@ BODY, .theme-light {
156
156
  }
157
157
 
158
158
  --muted : #{$dark};
159
+ --deemphasized : #717179;
159
160
 
160
161
  .text-muted {
161
162
  color: var(--muted) !important;
162
163
  }
163
164
 
165
+ .text-deemphasized {
166
+ color: var(--deemphasized);
167
+ }
168
+
164
169
  --darker : #{$darker};
165
170
  --darker-text : #{contrast-color($darker)};
166
171
  --darker-hover-bg : #{darken($darker, 10%)};
@@ -402,7 +407,8 @@ BODY, .theme-light {
402
407
 
403
408
  --modal-bg : #{$lightest};
404
409
  --modal-border : #{$dark};
405
- --overlay-bg : #{rgba($lighter, 0.75)};
410
+ --subtle-overlay-bg : #{rgba($lighter, 0.75)};
411
+ --overlay-bg : rgba(38, 42, 64, 0.35);
406
412
  --shadow : #{rgba($medium, 0.85)};
407
413
 
408
414
  --checkbox-tick : #{$lightest};
@@ -468,7 +474,7 @@ BODY, .theme-light {
468
474
  --sortable-table-group-label : #{$secondary};
469
475
 
470
476
  --tag-primary : #{$darkest};
471
- --tag-bg : #{$medium};
477
+ --tag-bg : #EEEFF5;
472
478
 
473
479
  --popover-bg : var(--body-bg);
474
480
  --popover-border : var(--border);
@@ -607,6 +607,9 @@ authConfig:
607
607
  starttls:
608
608
  label: Start TLS
609
609
  tip: Upgrades non-encrypted connections by wrapping with TLS during the connection process. Can not be used in conjunction with TLS.
610
+ searchUsingServiceAccount:
611
+ label: Enable Service Account Search
612
+ tip: When enabled, Rancher will use the service account instead of the user account to search for users and groups.
610
613
  tls: TLS
611
614
  userEnabledAttribute: User Enabled Attribute
612
615
  userMemberAttribute: User Member Attribute
@@ -1074,10 +1077,8 @@ catalog:
1074
1077
  header: Charts
1075
1078
  noCharts:
1076
1079
  title: No charts to show
1077
- messagePart1: <b>Tips:</b> undo the last filter you applied or
1078
- messagePart2: clear all filters
1079
- messagePart3: ', and ensure you have the right <a tabindex="0" href={repositoriesUrl} rel="noopener noreferrer nofollow">repositories</a> in place.'
1080
- messagePart4: 'Want to learn more about Helm Charts and Apps? Read our <a tabindex="0" href="{docsBase}/how-to-guides/new-user-guides/helm-charts-in-rancher" target="_blank" class="secondary-text-link">documentation <i class="icon icon-external-link"></i></a>.'
1080
+ message: '<b>Tips:</b> undo the last filter you applied or <resetAllFilters>clear all filters</resetAllFilters>, and ensure you have the right <repositoriesUrl>repositories</repositoriesUrl> in place.'
1081
+ docsMessage: 'Want to learn more about Helm Charts and Apps? Read our <docsUrl>documentation</docsUrl>.'
1081
1082
  noWindows: Your repos do not contain any charts capable of being deployed on a cluster with Windows nodes.
1082
1083
  noWindowsAndLinux: Your repos do not contain any charts capable of being deployed on a cluster with both Windows and Linux worker nodes.
1083
1084
  operatingSystems:
@@ -1654,6 +1655,9 @@ cluster:
1654
1655
  sshUser:
1655
1656
  placeholder: e.g. ubuntu
1656
1657
  toolTip: SSH user to login with the selected OS image.
1658
+ kubeconfigSecret:
1659
+ nameRequired: Cluster name is required.
1660
+ error: 'Error generating Harvester kubeconfig secret: {err}'
1657
1661
  haveOneOwner: There must be at least one member with the Owner role.
1658
1662
  import:
1659
1663
  warningBanner: 'You should not import a cluster which has already been connected to another instance of Rancher as it will lead to data corruption.'
@@ -2302,6 +2306,7 @@ cluster:
2302
2306
  server: Server compliance Profile
2303
2307
  agent: Compliance Profile
2304
2308
  override: Allow the default Pod Security Admission Configuration Template to be overridden when using a compliance profile
2309
+ warning: After saving changes, and if this cluster has already been provisioned, please restart the rke2-server service on all nodes to ensure etcd file permissions are correct
2305
2310
  defaultPodSecurityPolicyTemplateName:
2306
2311
  label: Default Pod Security Policy
2307
2312
  option: Default - RKE2 Embedded
@@ -5868,6 +5873,13 @@ secret:
5868
5873
  'kubernetes.io/tls':
5869
5874
  description: Store a certificate and key for TLS
5870
5875
  docLink: https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets
5876
+ tabs:
5877
+ namespaced:
5878
+ label: Namespaced
5879
+ create: Create Secret
5880
+ projectScoped:
5881
+ label: Project Scoped
5882
+ create: Create Project Secret
5871
5883
 
5872
5884
  selectOrCreateAuthSecret:
5873
5885
  label: Authentication
@@ -6570,6 +6582,11 @@ tableHeaders:
6570
6582
  scope: Scope
6571
6583
  selector: Selector
6572
6584
  secrets: Secrets
6585
+ secret:
6586
+ project-clone: Created by Project Secret
6587
+ project-clone-tooltip: Has a Project Scoped Secret created this Secret in this Namespace
6588
+ project-scoped: Project Secret
6589
+ project-scoped-tooltip: This Project Scoped Secret will create matching Secrets in the Namespaces it contains
6573
6590
  schedule: Schedule
6574
6591
  service: Service
6575
6592
  serviceAccounts: Service Accounts
@@ -7855,6 +7872,11 @@ typeLabel:
7855
7872
  one { Secret }
7856
7873
  other { Secrets }
7857
7874
  }
7875
+ projectsecret: |-
7876
+ {count, plural,
7877
+ one { Project Secret }
7878
+ other { Project Secrets }
7879
+ }
7858
7880
  apiregistration.k8s.io.apiservice: |-
7859
7881
  {count, plural,
7860
7882
  one { APIService }
@@ -444,7 +444,7 @@ export default {
444
444
  justify-content: center;
445
445
  border: 1px solid transparent;
446
446
  color: var(--darker);
447
- background-color: var(--overlay-bg);
447
+ background-color: var(--subtle-overlay-bg);
448
448
  font-size: 12px;
449
449
 
450
450
  .close-indicator {
@@ -61,7 +61,6 @@ const ariaLabel = computed(() => i18n.t('component.drawer.chrome.ariaLabel.close
61
61
  bottom: 0;
62
62
  left: 0;
63
63
  right: 0;
64
- z-index: 1000;
65
64
 
66
65
  display: flex;
67
66
  flex-direction: column;
@@ -26,11 +26,13 @@ describe('composables: ResourceDetailDrawer', () => {
26
26
  describe('useDefaultConfigTabProps', () => {
27
27
  const hasCustomEdit = jest.fn();
28
28
  const importEdit = jest.fn();
29
+ const hasCustomDetail = jest.fn();
29
30
  const editComponent = { component: 'EDIT_COMPONENT' };
30
31
  const store: any = {
31
32
  getters: {
32
- 'type-map/hasCustomEdit': hasCustomEdit,
33
- 'type-map/importEdit': importEdit
33
+ 'type-map/hasCustomEdit': hasCustomEdit,
34
+ 'type-map/importEdit': importEdit,
35
+ 'type-map/hasCustomDetail': hasCustomDetail,
34
36
  }
35
37
  };
36
38
 
@@ -43,13 +45,35 @@ describe('composables: ResourceDetailDrawer', () => {
43
45
  expect(props).toBeUndefined();
44
46
  });
45
47
 
48
+ it('should return undefined if it does not have a customDetail', async() => {
49
+ jest.spyOn(vuex, 'useStore').mockImplementation(() => store);
50
+ const hasCustomEditSpy = hasCustomEdit.mockImplementation(() => true);
51
+ const hasCustomDetailSpy = hasCustomDetail.mockImplementation(() => false);
52
+ const props = useDefaultConfigTabProps(resource);
53
+
54
+ expect(hasCustomEditSpy).toHaveBeenCalledWith(resource.type);
55
+ expect(hasCustomDetailSpy).toHaveBeenCalledWith(resource.type);
56
+ expect(props).toBeUndefined();
57
+ });
58
+
59
+ it('should return undefined if resource disableResourceDetailDrawerConfigTab is true', async() => {
60
+ jest.spyOn(vuex, 'useStore').mockImplementation(() => store);
61
+ const hasCustomDetailSpy = hasCustomDetail.mockImplementation(() => false);
62
+ const props = useDefaultConfigTabProps({ ...resource, disableResourceDetailDrawerConfigTab: true });
63
+
64
+ expect(hasCustomDetailSpy).toHaveBeenCalledWith(resource.type);
65
+ expect(props).toBeUndefined();
66
+ });
67
+
46
68
  it('should return props if it has a customEdit', async() => {
47
69
  jest.spyOn(vuex, 'useStore').mockImplementation(() => store);
48
70
  const hasCustomEditSpy = hasCustomEdit.mockImplementation(() => true);
71
+ const hasCustomDetailSpy = hasCustomDetail.mockImplementation(() => true);
49
72
  const importEditSpy = importEdit.mockImplementation(() => editComponent);
50
73
  const props = useDefaultConfigTabProps(resource);
51
74
 
52
75
  expect(hasCustomEditSpy).toHaveBeenCalledWith(resource.type);
76
+ expect(hasCustomDetailSpy).toHaveBeenCalledWith(resource.type);
53
77
  expect(importEditSpy).toHaveBeenCalledWith(resource.type);
54
78
  expect(props?.component).toStrictEqual(editComponent);
55
79
  expect(props?.resource).toStrictEqual(resource);
@@ -38,7 +38,10 @@ export async function useDefaultYamlTabProps(resource: any): Promise<YamlTabProp
38
38
  export function useDefaultConfigTabProps(resource: any): ConfigTabProps | undefined {
39
39
  const store = useStore();
40
40
 
41
- if (!store.getters['type-map/hasCustomEdit'](resource.type)) {
41
+ // You don't want to show the Config tab if there isn't a an edit page to show and you don't want to show it if there isn't
42
+ // a detail page because we default to showing the existing edit page if the detail page doesn't exist. Showing them again
43
+ // wouldn't be worth while.
44
+ if (!store.getters['type-map/hasCustomEdit'](resource.type) || !store.getters['type-map/hasCustomDetail'](resource.type) || resource.disableResourceDetailDrawerConfigTab) {
42
45
  return;
43
46
  }
44
47
 
@@ -68,6 +68,7 @@ const action = computed(() => {
68
68
  <Tabbed
69
69
  class="tabbed"
70
70
  :useHash="false"
71
+ :showExtensionTabs="false"
71
72
  @changed="({selectedName}) => {activeTab = selectedName;}"
72
73
  >
73
74
  <ConfigTab
@@ -59,7 +59,7 @@ export default {
59
59
  <style lang="scss" scoped>
60
60
  .overlay {
61
61
  align-items: center;
62
- background-color: var(--overlay-bg);
62
+ background-color: var(--subtle-overlay-bg);
63
63
  display: flex;
64
64
  justify-content: center;
65
65
  position: absolute;
@@ -36,6 +36,35 @@ export default defineComponent({
36
36
  default: null,
37
37
  },
38
38
 
39
+ groupTooltip: {
40
+ type: String,
41
+ default: 'resourceTable.groupBy.namespace',
42
+ },
43
+
44
+ /**
45
+ * Field to group rows by, row[groupBy] must be something that can be a map key (or also use groupSort)
46
+ */
47
+ groupBy: {
48
+ type: String,
49
+ default: null,
50
+ },
51
+
52
+ /**
53
+ * Field to order groups by, defaults to groupBy
54
+ */
55
+ groupSort: {
56
+ type: String,
57
+ default: null
58
+ },
59
+
60
+ /**
61
+ * Override any product based group options
62
+ */
63
+ groupOptions: {
64
+ type: Array,
65
+ default: null
66
+ },
67
+
39
68
  groupable: {
40
69
  type: Boolean,
41
70
  default: null, // Null: auto based on namespaced and type custom groupings
@@ -58,6 +87,14 @@ export default defineComponent({
58
87
  default: null,
59
88
  },
60
89
 
90
+ /**
91
+ * Use this store instead of the store `inStore` getters
92
+ */
93
+ overrideInStore: {
94
+ type: String,
95
+ default: undefined,
96
+ },
97
+
61
98
  /**
62
99
  * Information may be required from resources other than the primary one shown per row
63
100
  *
@@ -79,7 +116,7 @@ export default defineComponent({
79
116
 
80
117
  async fetch() {
81
118
  const promises = [
82
- this.$fetchType(this.resource, [], this.inStore),
119
+ this.$fetchType(this.resource, [], this.overrideInStore || this.inStore),
83
120
  ];
84
121
 
85
122
  if (this.fetchSecondaryResources) {
@@ -115,13 +152,21 @@ export default defineComponent({
115
152
  :rows="rows"
116
153
  :alt-loading="canPaginate && !isFirstLoad"
117
154
  :loading="loading"
155
+
156
+ :group-by="groupBy"
157
+ :group-sort="groupSort"
118
158
  :groupable="groupable"
159
+ :groupTooltip="groupTooltip"
160
+ :groupOptions="groupOptions"
161
+
162
+ :override-in-store="overrideInStore"
119
163
 
120
164
  :headers="safeHeaders"
121
165
  :namespaced="namespaced"
122
166
 
123
167
  :external-pagination-enabled="canPaginate"
124
168
  :external-pagination-result="paginationResult"
169
+
125
170
  @pagination-changed="paginationChanged"
126
171
  >
127
172
  <!-- Pass down templates provided by the caller -->
@@ -7,7 +7,7 @@ import Date from '@shell/components/formatter/Date.vue';
7
7
  import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
8
8
  import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
9
9
  import { exceptionToErrorsArray } from '@shell/utils/error';
10
- import { CAPI, NORMAN, SNAPSHOT } from '@shell/config/types';
10
+ import { CAPI, SNAPSHOT } from '@shell/config/types';
11
11
  import { set } from '@shell/utils/object';
12
12
  import ChildHook, { BEFORE_SAVE_HOOKS } from '@shell/mixins/child-hook';
13
13
  import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
@@ -47,14 +47,13 @@ export default {
47
47
  },
48
48
 
49
49
  computed: {
50
- // toRestore can be a provisioning.cattle.io.cluster or a rke.cattle.io.etcdsnapshot or an etcdBackup resource
50
+ // toRestore can be a provisioning.cattle.io.cluster or a rke.cattle.io.etcdsnapshot resource
51
51
  ...mapState('action-menu', ['showPromptRestore', 'toRestore']),
52
52
  ...mapGetters({ t: 'i18n/t' }),
53
53
 
54
54
  // Was the dialog opened to restore a specific snapshot, or opened on a cluster to choose
55
55
  isCluster() {
56
- const isSnapshot = this.toRestore[0]?.type.toLowerCase() === NORMAN.ETCD_BACKUP ||
57
- this.toRestore[0]?.type.toLowerCase() === SNAPSHOT;
56
+ const isSnapshot = this.toRestore[0]?.type.toLowerCase() === SNAPSHOT;
58
57
 
59
58
  return !isSnapshot;
60
59
  },
@@ -79,9 +78,7 @@ export default {
79
78
  }
80
79
  },
81
80
  restoreModeOptions() {
82
- const etcdOption = this.isRke2 ? 'none' : 'etcd';
83
-
84
- return [etcdOption, 'kubernetesVersion', 'all'];
81
+ return ['none', 'kubernetesVersion', 'all'];
85
82
  }
86
83
  },
87
84
 
@@ -112,20 +109,12 @@ export default {
112
109
  }
113
110
 
114
111
  const cluster = this.toRestore?.[0];
115
- let promise;
116
-
117
- if (!cluster?.isRke2) {
118
- promise = this.$store.dispatch('rancher/findAll', { type: NORMAN.ETCD_BACKUP }).then((snapshots) => {
119
- return snapshots.filter((s) => s.state === STATES_ENUM.ACTIVE && s.clusterId === cluster.metadata.name);
120
- });
121
- } else {
122
- promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
123
- const toRestoreClusterName = cluster?.clusterName || cluster?.metadata?.name;
112
+ const promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
113
+ const toRestoreClusterName = cluster?.clusterName || cluster?.metadata?.name;
124
114
 
125
- return snapshots.filter((s) => s?.snapshotFile?.status === STATES_ENUM.SUCCESSFUL && s.clusterName === toRestoreClusterName
126
- );
127
- });
128
- }
115
+ return snapshots.filter((s) => s?.snapshotFile?.status === STATES_ENUM.SUCCESSFUL && s.clusterName === toRestoreClusterName
116
+ );
117
+ });
129
118
 
130
119
  // Map of snapshots by name
131
120
  const allSnapshots = await promise.then((snapshots) => {
@@ -154,30 +143,19 @@ export default {
154
143
 
155
144
  async apply(buttonDone) {
156
145
  try {
157
- if ( this.isRke2 ) {
158
- const cluster = this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, this.snapshot.clusterId);
159
-
160
- await this.applyHooks(BEFORE_SAVE_HOOKS);
161
-
162
- const now = cluster.spec?.rkeConfig?.etcdSnapshotRestore?.generation || 0;
163
-
164
- set(cluster, 'spec.rkeConfig.etcdSnapshotRestore', {
165
- generation: now + 1,
166
- name: this.snapshot.name,
167
- restoreRKEConfig: this.restoreMode,
168
- });
169
-
170
- await cluster.save();
171
- } else {
172
- await this.$store.dispatch('rancher/request', {
173
- url: `/v3/clusters/${ escape(this.snapshot.clusterId) }?action=restoreFromEtcdBackup`,
174
- method: 'post',
175
- data: {
176
- etcdBackupId: this.snapshot.id,
177
- restoreRkeConfig: this.restoreMode,
178
- },
179
- });
180
- }
146
+ const cluster = this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, this.snapshot.clusterId);
147
+
148
+ await this.applyHooks(BEFORE_SAVE_HOOKS);
149
+
150
+ const now = cluster.spec?.rkeConfig?.etcdSnapshotRestore?.generation || 0;
151
+
152
+ set(cluster, 'spec.rkeConfig.etcdSnapshotRestore', {
153
+ generation: now + 1,
154
+ name: this.snapshot.name,
155
+ restoreRKEConfig: this.restoreMode,
156
+ });
157
+
158
+ await cluster.save();
181
159
 
182
160
  this.$store.dispatch('growl/success', {
183
161
  title: this.t('promptRestore.notification.title'),
@@ -3,7 +3,7 @@ import { Row } from '@shell/components/Resource/Detail/Metadata/IdentifyingInfor
3
3
  import {
4
4
  useCertificate,
5
5
  useExpires,
6
- useImage, useIssuer, useLiveDate, useNamespace, useReady, useSecretType,
6
+ useImage, useIssuer, useLiveDate, useNamespace, useProject, useReady, useSecretCluster, useSecretType,
7
7
  useServiceAccount
8
8
  } from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields';
9
9
  import { useStore } from 'vuex';
@@ -21,7 +21,11 @@ export const useDefaultIdentifyingInformation = (resource: any): ComputedRef<Row
21
21
  });
22
22
  };
23
23
 
24
- export const useSecretIdentifyingInformation = (resource: any): ComputedRef<Row[]> => {
24
+ export const useSecretIdentifyingInformation = (resource: any, isProjectSecret: boolean): ComputedRef<Row[]> => {
25
+ const namespace = isProjectSecret ? undefined : useNamespace(resource);
26
+ const project = isProjectSecret ? useProject(resource) : undefined;
27
+ const cluster = isProjectSecret ? useSecretCluster(resource) : undefined;
28
+ const age = useLiveDate(resource);
25
29
  const secretType = useSecretType(resource);
26
30
  const serviceAccount = useServiceAccount(resource);
27
31
  const certificate = useCertificate(resource);
@@ -30,6 +34,10 @@ export const useSecretIdentifyingInformation = (resource: any): ComputedRef<Row[
30
34
 
31
35
  return computed(() => {
32
36
  const rows = [
37
+ age?.value,
38
+ namespace?.value,
39
+ project?.value,
40
+ cluster?.value,
33
41
  secretType?.value,
34
42
  serviceAccount?.value,
35
43
  certificate?.value,
@@ -2,7 +2,9 @@ import { useI18n } from '@shell/composables/useI18n';
2
2
  import { computed, ComputedRef, markRaw, toValue } from 'vue';
3
3
  import Additional from '@shell/components/Resource/Detail/Additional.vue';
4
4
  import { useStore } from 'vuex';
5
- import { NAMESPACE, FLEET, SERVICE_ACCOUNT } from '@shell/config/types';
5
+ import {
6
+ NAMESPACE, FLEET, SERVICE_ACCOUNT, SECRET, CAPI
7
+ } from '@shell/config/types';
6
8
  import { Row } from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue';
7
9
  import { NAME as FLEET_NAME } from '@shell/config/product/fleet';
8
10
  import { useRoute } from 'vue-router';
@@ -113,7 +115,12 @@ export const useProject = (resource: any): ComputedRef<Row> | undefined => {
113
115
  const i18n = useI18n(store);
114
116
  const resourceValue = toValue(resource);
115
117
 
116
- if (resource.type !== NAMESPACE || !resourceValue.project) {
118
+ // Only show project if one of these types
119
+ if (resource.type !== NAMESPACE && resource.type !== SECRET) {
120
+ return;
121
+ }
122
+
123
+ if (!resourceValue.project) {
117
124
  return;
118
125
  }
119
126
 
@@ -126,6 +133,18 @@ export const useProject = (resource: any): ComputedRef<Row> | undefined => {
126
133
  });
127
134
  };
128
135
 
136
+ export const useSecretCluster = (resource: any): ComputedRef<Row> | undefined => {
137
+ const store = useStore();
138
+ const resourceValue = toValue(resource);
139
+
140
+ return computed(() => {
141
+ return {
142
+ label: store.getters['type-map/labelFor']({ id: CAPI.RANCHER_CLUSTER }),
143
+ value: resourceValue.projectCluster?.nameDisplay,
144
+ };
145
+ });
146
+ };
147
+
129
148
  export const useResourceDetails = (resource: any): undefined | ComputedRef<Row[]> => {
130
149
  const details = resource.details;
131
150
 
@@ -34,7 +34,7 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
34
34
  :data-testid="row.dataTestid"
35
35
  >
36
36
  <label
37
- class="label text-muted"
37
+ class="label text-deemphasized"
38
38
  :for="getRowValueId(row)"
39
39
  >
40
40
  {{ row.label }}
@@ -95,6 +95,13 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
95
95
  display: flex;
96
96
  flex-direction: row;
97
97
  align-items: center;
98
+
99
+ &, & * {
100
+ max-width: 100%;
101
+ overflow: hidden;
102
+ text-overflow: ellipsis;
103
+ white-space: nowrap;
104
+ }
98
105
  }
99
106
 
100
107
  .label {
@@ -54,12 +54,12 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
54
54
  <template>
55
55
  <div class="key-value">
56
56
  <div class="heading">
57
- <span class="title text-muted">{{ propertyName }}</span>
57
+ <span class="title text-deemphasized">{{ propertyName }}</span>
58
58
  <span class="count">{{ rows.length }}</span>
59
59
  </div>
60
60
  <div
61
61
  v-if="visibleRows.length === 0"
62
- class="empty mmt-2 text-muted"
62
+ class="empty mmt-2 text-deemphasized"
63
63
  >
64
64
  <div class="no-rows">
65
65
  {{ i18n.t('component.resource.detail.metadata.keyValue.noRows', {propertyName: lowercasePropertyName}) }}
@@ -67,7 +67,7 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
67
67
  <div class="show-configuration mmt-1">
68
68
  <a
69
69
  :data-testid="showConfigurationEmptyDataTestId"
70
- class="secondary text-muted"
70
+ class="secondary text-deemphasized"
71
71
  href="#"
72
72
  @click="(ev: MouseEvent) => {ev.preventDefault(); emit('show-configuration', showConfigurationEmptyFocusSelector);}"
73
73
  >
@@ -91,7 +91,7 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
91
91
  v-if="showShowAllButton"
92
92
  :data-testid="showConfigurationMoreDataTestId"
93
93
  href="#"
94
- class="show-all secondary"
94
+ class="show-all"
95
95
  @click="(ev: MouseEvent) => {ev.preventDefault(); emit('show-configuration', showConfigurationMoreFocusSelector);}"
96
96
  >
97
97
  {{ showAllLabel }}
@@ -110,29 +110,31 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
110
110
  }
111
111
 
112
112
  .heading {
113
- margin-bottom: 4px;
113
+ margin-bottom: 8px;
114
114
  }
115
115
 
116
116
  .row {
117
+ display: block;
117
118
  width: 100%;
118
119
 
119
- &:not(:first-of-type) {
120
+ &:not(:nth-child(2)) {
120
121
  margin-top: 4px;
121
122
  }
122
-
123
- & {
124
- margin-top: 8px;
125
- }
126
123
  }
127
124
  .show-all {
128
125
  margin-top: 8px;
129
126
  }
130
127
 
131
128
  .rectangle {
129
+ display: inline-block;
132
130
  max-width: 100%;
133
131
  overflow: hidden;
134
132
  text-overflow: ellipsis;
135
133
  white-space: nowrap;
136
134
  }
135
+
136
+ .no-rows {
137
+ line-height: 21px;
138
+ }
137
139
  }
138
140
  </style>
@@ -23,7 +23,9 @@ const props = withDefaults(
23
23
  .rectangle {
24
24
  border: 1px solid var(--tag-bg);
25
25
  border-radius: 4px;
26
- padding: 4px;
26
+ padding: 0 8px;
27
+ height: 23px;
28
+ line-height: 23px;
27
29
 
28
30
  &:not(.outline) {
29
31
  background-color: var(--tag-bg);
@@ -7,7 +7,7 @@
7
7
  <style lang="scss" scoped>
8
8
  .spaced-row {
9
9
  display: grid;
10
- grid-template-columns: 1fr 1fr 1fr;
10
+ grid-template-columns: repeat(3, minmax(0, 1fr));;
11
11
  grid-auto-flow: dense;
12
12
  grid-gap: 24px;
13
13
  }