@rancher/shell 3.0.12-rc.4 → 3.0.12-rc.6

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 (159) hide show
  1. package/apis/intf/resources-api/resource-base.ts +42 -2
  2. package/apis/intf/resources-api/resource-instance.ts +101 -0
  3. package/apis/intf/resources-api/resources-api.ts +177 -23
  4. package/apis/intf/resources.ts +8 -6
  5. package/apis/resources/__tests__/resources-api-class.test.ts +309 -28
  6. package/apis/resources/resources-api-class.ts +232 -43
  7. package/assets/images/providers/traefik.png +0 -0
  8. package/assets/styles/global/_button.scss +1 -1
  9. package/assets/translations/en-us.yaml +49 -14
  10. package/chart/__tests__/rancher-monitoring-dashboards-index.test.ts +269 -0
  11. package/chart/rancher-monitoring-dashboards/index.vue +155 -0
  12. package/cloud-credential/__tests__/generic.test.ts +132 -0
  13. package/cloud-credential/generic.vue +33 -3
  14. package/components/ActionDropdownShell.vue +5 -3
  15. package/components/ButtonGroup.vue +26 -1
  16. package/components/CruResource.vue +51 -2
  17. package/components/EtcdInfoBanner.vue +2 -12
  18. package/components/GrafanaDashboard.vue +2 -8
  19. package/components/PromptRestore.vue +93 -32
  20. package/components/Questions/index.vue +1 -0
  21. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +68 -14
  22. package/components/Resource/Detail/Masthead/index.vue +8 -3
  23. package/components/ResourceTable.vue +1 -0
  24. package/components/SortableTable/index.vue +4 -3
  25. package/components/Wizard.vue +14 -1
  26. package/components/__tests__/ButtonGroup.test.ts +56 -0
  27. package/components/__tests__/PromptRestore.test.ts +169 -19
  28. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  29. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  30. package/components/fleet/HelmOpAppCoConfigTab.vue +4 -0
  31. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  32. package/components/form/FileSelector.vue +39 -1
  33. package/components/form/PrivateRegistry.constants.ts +7 -0
  34. package/components/form/PrivateRegistry.vue +253 -18
  35. package/components/form/Security.vue +1 -1
  36. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  37. package/components/form/__tests__/FileSelector.test.ts +23 -0
  38. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  39. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  40. package/components/formatter/EtcdSnapshotName.vue +73 -0
  41. package/components/formatter/Translate.vue +22 -1
  42. package/components/nav/Group.vue +5 -10
  43. package/components/nav/Header.vue +8 -1
  44. package/components/nav/TopLevelMenu.vue +1 -30
  45. package/components/nav/__tests__/Group.test.ts +61 -0
  46. package/components/templates/default.vue +7 -0
  47. package/config/features.js +1 -0
  48. package/config/labels-annotations.js +3 -0
  49. package/config/product/manager.js +6 -0
  50. package/config/secret.ts +10 -0
  51. package/config/settings.ts +6 -2
  52. package/config/table-headers.js +5 -2
  53. package/config/types.js +7 -0
  54. package/core/__tests__/plugin-products-apply.test.ts +787 -0
  55. package/core/__tests__/plugin-products-extend.test.ts +189 -0
  56. package/core/__tests__/plugin-products-helpers.test.ts +81 -78
  57. package/core/__tests__/plugin-products-new.test.ts +389 -0
  58. package/core/__tests__/plugin-products-scenarios.test.ts +980 -0
  59. package/core/__tests__/plugin-products-side-menu.test.ts +1935 -0
  60. package/core/plugin-products-base.ts +201 -93
  61. package/core/plugin-products-extending.ts +1 -1
  62. package/core/plugin-products-external.ts +380 -0
  63. package/core/plugin-products-helpers.ts +28 -24
  64. package/core/plugin-products-internal.ts +207 -0
  65. package/core/plugin-products-top-level.ts +10 -5
  66. package/core/plugin-products-type-guards.ts +17 -6
  67. package/core/plugin-products.ts +5 -7
  68. package/core/plugin-types.ts +1 -389
  69. package/core/plugin.ts +10 -11
  70. package/core/types.ts +6 -202
  71. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +52 -0
  72. package/detail/pod.vue +1 -1
  73. package/detail/provisioning.cattle.io.cluster.vue +79 -5
  74. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  75. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  76. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  77. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +101 -0
  78. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  79. package/edit/auth/AuthProviderWarningBanners.vue +12 -0
  80. package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +10 -1
  81. package/edit/auth/__tests__/azuread.test.ts +1 -0
  82. package/edit/auth/__tests__/github.test.ts +1 -0
  83. package/edit/auth/__tests__/oidc.test.ts +1 -0
  84. package/edit/auth/__tests__/saml.test.ts +1 -0
  85. package/edit/auth/ldap/__tests__/index.test.ts +1 -0
  86. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  87. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  88. package/edit/fleet.cattle.io.helmop.vue +51 -5
  89. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  90. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  91. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  92. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  93. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  94. package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
  95. package/edit/provisioning.cattle.io.cluster/rke2.vue +6 -2
  96. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  97. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +2 -2
  98. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  99. package/list/management.cattle.io.user.vue +24 -5
  100. package/list/utils/management.cattle.io.cluster.utils.ts +1 -1
  101. package/machine-config/__tests__/generic.test.ts +163 -0
  102. package/machine-config/components/EC2Networking.vue +31 -7
  103. package/machine-config/components/__tests__/EC2Networking.test.ts +38 -15
  104. package/machine-config/generic.vue +32 -5
  105. package/mixins/resource-fetch.js +1 -1
  106. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +156 -0
  107. package/models/__tests__/rke-machine.cattle.io.vmwarevspheremachinetemplate.test.ts +22 -0
  108. package/models/__tests__/secret.test.ts +68 -1
  109. package/models/management.cattle.io.cluster.js +21 -3
  110. package/models/pod.js +25 -12
  111. package/models/provisioning.cattle.io.cluster.js +59 -9
  112. package/models/rke-machine.cattle.io.vmwarevspheremachinetemplate.js +9 -0
  113. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  114. package/models/secret.js +19 -0
  115. package/models/workload.js +12 -7
  116. package/package.json +1 -1
  117. package/pages/auth/login.vue +1 -1
  118. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +569 -81
  119. package/pages/c/_cluster/apps/charts/install.vue +143 -28
  120. package/pages/c/_cluster/auth/config/index.vue +11 -4
  121. package/pages/c/_cluster/monitoring/index.vue +6 -1
  122. package/pkg/require-asset.lib.js +25 -0
  123. package/pkg/vue.config.js +7 -0
  124. package/plugins/dashboard-store/__tests__/resource-class.test.ts +84 -0
  125. package/plugins/dashboard-store/getters.js +0 -1
  126. package/plugins/dashboard-store/resource-class.js +62 -15
  127. package/plugins/steve/__tests__/steve-class-resource-api.test.ts +147 -0
  128. package/plugins/steve/steve-class.js +43 -0
  129. package/plugins/steve/subscribe.js +11 -0
  130. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +115 -0
  131. package/rancher-components/Form/LabeledInput/LabeledInput.vue +83 -3
  132. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  133. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  134. package/rancher-components/RcButton/index.ts +1 -1
  135. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  136. package/store/__tests__/auth.test.ts +18 -0
  137. package/store/__tests__/features.test.ts +131 -0
  138. package/store/__tests__/growl.test.ts +374 -0
  139. package/store/__tests__/modal.test.ts +131 -0
  140. package/store/__tests__/notifications.test.ts +466 -1
  141. package/store/__tests__/slideInPanel.test.ts +88 -0
  142. package/store/__tests__/type-map.utils.test.ts +433 -0
  143. package/store/__tests__/ui-context.test.ts +255 -0
  144. package/store/auth.js +11 -1
  145. package/store/features.js +5 -0
  146. package/store/plugins.js +6 -0
  147. package/types/rancher/steve.api.ts +2 -2
  148. package/types/shell/index.d.ts +120 -0
  149. package/types/store/dashboard-store.types.ts +2 -2
  150. package/types/store/type-map.ts +262 -1
  151. package/utils/__tests__/operation-cr.test.ts +34 -0
  152. package/utils/async.ts +2 -0
  153. package/utils/grafana.js +2 -17
  154. package/utils/monitoring.js +38 -1
  155. package/utils/operation-cr.js +19 -0
  156. package/utils/require-asset.ts +7 -0
  157. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  158. package/utils/validators/private-registry.ts +15 -4
  159. package/core/__tests__/plugin-products.test.ts +0 -4694
@@ -1,4 +1,4 @@
1
- import { CATALOG, CLUSTER_BADGE, NODE_ARCHITECTURE } from '@shell/config/labels-annotations';
1
+ import { CATALOG, CLUSTER_BADGE, NODE_ARCHITECTURE, OPERATION_ANNOTATIONS } from '@shell/config/labels-annotations';
2
2
  import {
3
3
  NODE, FLEET, MANAGEMENT, CAPI, EXT,
4
4
  NORMAN,
@@ -9,7 +9,7 @@ import { downloadFile } from '@shell/utils/download';
9
9
  import { parseSi } from '@shell/utils/units';
10
10
  import { parseColor, textColor } from '@shell/utils/color';
11
11
  import { isEmpty, isEqual } from '@shell/utils/object';
12
- import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
12
+ import { HARVESTER_NAME as HARVESTER, IMPORTED_DAY_2_OPS } from '@shell/config/features';
13
13
  import { isHarvesterCluster } from '@shell/utils/cluster';
14
14
  import SteveModel from '@shell/plugins/steve/steve-class';
15
15
  import { LINUX, WINDOWS } from '@shell/store/catalog';
@@ -20,7 +20,7 @@ import { copyTextToClipboard } from '@shell/utils/clipboard';
20
20
  import { isHostedProvider, isCAPIProvider } from '@shell/utils/provider';
21
21
  import { ucFirst } from '@shell/utils/string';
22
22
  import { sortBy } from '@shell/utils/sort';
23
-
23
+ import { SETTING } from '@shell/config/settings';
24
24
  const DEFAULT_BADGE_COLOR = '#707070';
25
25
 
26
26
  // See translation file cluster.providers for list of providers
@@ -389,6 +389,24 @@ export default class MgmtCluster extends SteveModel {
389
389
  return this.spec?.internal === true;
390
390
  }
391
391
 
392
+ get isDayTwoOpsFeatureEnabled() {
393
+ return this.$rootGetters['management/byId'](MANAGEMENT.FEATURE, IMPORTED_DAY_2_OPS)?.enabled || false;
394
+ }
395
+
396
+ /**
397
+ * Whether day 2 operations are enabled for this cluster.
398
+ * Reads the `operations.cattle.io/ops-enabled` annotation.
399
+ */
400
+ get isDayTwoOpsEnabled() {
401
+ const isImportedRke2K3s = !this.isLocal && (this.isImportedK3s || this.isImportedRke2);
402
+ const annotationExists = typeof this.metadata?.annotations?.[OPERATION_ANNOTATIONS.ENABLED] !== 'undefined';
403
+ const annotationEnabled = this.metadata?.annotations?.[OPERATION_ANNOTATIONS.ENABLED] === 'true';
404
+ const globalDefaultIsTrue = this.$rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.IMPORTED_CLUSTER_DAY2_OPS_DEFAULT)?.value === 'true';
405
+ const annotationEnabledOrDefault = annotationExists ? annotationEnabled : globalDefaultIsTrue;
406
+
407
+ return this.isDayTwoOpsFeatureEnabled && isImportedRke2K3s && annotationEnabledOrDefault;
408
+ }
409
+
392
410
  get isHarvester() {
393
411
  return isHarvesterCluster(this);
394
412
  }
package/models/pod.js CHANGED
@@ -6,6 +6,7 @@ import WorkloadService from '@shell/models/workload.service';
6
6
  import { deleteProperty } from '@shell/utils/object';
7
7
  import { POD_RESTARTS_REG_EX } from '@shell/types/resources/pod';
8
8
  import { useResourceCardRow } from '@shell/components/Resource/Detail/Card/StateCard/composables';
9
+ import { POD_SHELL } from '@shell/store/features';
9
10
 
10
11
  export const WORKLOAD_PRIORITY = {
11
12
  [WORKLOAD_TYPES.DEPLOYMENT]: 1,
@@ -64,11 +65,16 @@ export default class Pod extends WorkloadService {
64
65
 
65
66
  get _availableActions() {
66
67
  const out = super._availableActions;
68
+ const podShellFeatureEnabled = !!this.$rootGetters['features/get'](POD_SHELL);
67
69
 
68
70
  // Add backwards, each one to the top
69
71
  insertAt(out, 0, { divider: true });
70
72
  insertAt(out, 0, this.openLogsMenuItem);
71
- insertAt(out, 0, this.openShellMenuItem);
73
+
74
+ // Only add the menu item for the pod shell if the feature flag is enabled
75
+ if (podShellFeatureEnabled) {
76
+ insertAt(out, 0, this.openShellMenuItem);
77
+ }
72
78
 
73
79
  return out;
74
80
  }
@@ -95,9 +101,14 @@ export default class Pod extends WorkloadService {
95
101
 
96
102
  get containerActions() {
97
103
  const out = [];
104
+ const podShellFeatureEnabled = !!this.$rootGetters['features/get'](POD_SHELL);
98
105
 
99
106
  insertAt(out, 0, this.openLogsMenuItem);
100
- insertAt(out, 0, this.openShellMenuItem);
107
+
108
+ // Only add the menu item for the container shell if the feature flag is enabled
109
+ if (podShellFeatureEnabled) {
110
+ insertAt(out, 0, this.openShellMenuItem);
111
+ }
101
112
 
102
113
  return out;
103
114
  }
@@ -298,19 +309,21 @@ export default class Pod extends WorkloadService {
298
309
  save() {
299
310
  const prev = { ...this };
300
311
 
301
- const { metadata, spec } = this.spec.template;
312
+ if (this.spec?.template) {
313
+ const { metadata, spec } = this.spec.template;
302
314
 
303
- this.spec = {
304
- ...this.spec,
305
- ...spec
306
- };
315
+ this.spec = {
316
+ ...this.spec,
317
+ ...spec
318
+ };
307
319
 
308
- this.metadata = {
309
- ...this.metadata,
310
- ...metadata
311
- };
320
+ this.metadata = {
321
+ ...this.metadata,
322
+ ...metadata
323
+ };
312
324
 
313
- delete this.spec.template;
325
+ delete this.spec.template;
326
+ }
314
327
 
315
328
  // IF there is an error POD world model get overwritten
316
329
  // For the workloads this need be reset back
@@ -1,13 +1,16 @@
1
1
  import {
2
2
  CAPI, MANAGEMENT, NAMESPACE, NORMAN, SNAPSHOT, LOCAL_CLUSTER,
3
3
  CONFIG_MAP, AUTOSCALER_CONFIG_MAP_ID,
4
- EVENT
4
+ EVENT, OPERATION
5
5
  } from '@shell/config/types';
6
6
  import SteveModel from '@shell/plugins/steve/steve-class';
7
7
  import { findBy } from '@shell/utils/array';
8
8
  import { get, set } from '@shell/utils/object';
9
9
  import { compare } from '@shell/utils/version';
10
- import { CAPI as CAPI_ANNOTATIONS } from '@shell/config/labels-annotations';
10
+ import { IMPORTED_DAY_2_OPS } from '@shell/config/features';
11
+ import { CAPI as CAPI_ANNOTATIONS, OPERATION_ANNOTATIONS } from '@shell/config/labels-annotations';
12
+ import { SETTING } from '@shell/config/settings';
13
+ import { createOperationCR } from '@shell/utils/operation-cr';
11
14
  import jsyaml from 'js-yaml';
12
15
  import { defineAsyncComponent, markRaw } from 'vue';
13
16
  import stevePaginationUtils from '@shell/plugins/steve/steve-pagination-utils';
@@ -122,10 +125,9 @@ export default class ProvCluster extends SteveModel {
122
125
  }
123
126
  const ready = this.mgmt?.isReady;
124
127
 
128
+ const canDayTwoOps = ready && this.isDayTwoOpsEnabled && this.canUpdate;
125
129
  const canEditRKE2cluster = this.isRke2 && ready && this.canUpdate;
126
130
 
127
- const canSnapshot = ready && this.isRke2 && this.canUpdate;
128
-
129
131
  const actions = [
130
132
  // Note: Actions are not supported in the Steve API, so we check
131
133
  // available actions for RKE1 clusters, but not RKE2 clusters.
@@ -153,12 +155,12 @@ export default class ProvCluster extends SteveModel {
153
155
  icon: 'icon icon-snapshot',
154
156
  bulkAction: 'snapshotBulk',
155
157
  bulkable: true,
156
- enabled: canSnapshot,
158
+ enabled: canDayTwoOps,
157
159
  }, {
158
160
  action: 'restoreSnapshotAction',
159
161
  label: this.$rootGetters['i18n/t']('nav.restoreSnapshot'),
160
162
  icon: 'icon icon-backup-restore',
161
- enabled: canSnapshot,
163
+ enabled: canDayTwoOps,
162
164
  }, {
163
165
  action: 'rotateCertificates',
164
166
  label: this.$rootGetters['i18n/t']('nav.rotateCertificates'),
@@ -168,7 +170,7 @@ export default class ProvCluster extends SteveModel {
168
170
  action: 'rotateEncryptionKey',
169
171
  label: this.$rootGetters['i18n/t']('nav.rotateEncryptionKeys'),
170
172
  icon: 'icon icon-refresh',
171
- enabled: canEditRKE2cluster
173
+ enabled: canDayTwoOps
172
174
  },
173
175
  {
174
176
  action: 'toggleAutoscalerRunner',
@@ -301,6 +303,35 @@ export default class ProvCluster extends SteveModel {
301
303
  return this.mgmt?.isImportedRke2;
302
304
  }
303
305
 
306
+ get isImportedRke2K3s() {
307
+ return this.isImportedRke2 || this.isImportedK3s;
308
+ }
309
+
310
+ /**
311
+ * Whether day 2 operations (snapshot, restore, cert rotation, encryption key rotation)
312
+ * are enabled for this cluster.
313
+ */
314
+ get isDayTwoOpsEnabled() {
315
+ return !!(this.isRke2 || this.isImportedWithDayTwoOps);
316
+ }
317
+
318
+ get isDayTwoOpsFeatureEnabled() {
319
+ return this.$rootGetters['management/byId'](MANAGEMENT.FEATURE, IMPORTED_DAY_2_OPS)?.enabled || false;
320
+ }
321
+
322
+ /**
323
+ * Whether this is an imported RKE2/K3s cluster with day 2 operations enabled.
324
+ */
325
+ get isImportedWithDayTwoOps() {
326
+ const annotationExists = typeof this.metadata?.annotations?.[OPERATION_ANNOTATIONS.ENABLED] !== 'undefined';
327
+ const annotationEnabled = this.mgmt?.metadata?.annotations?.[OPERATION_ANNOTATIONS.ENABLED] === 'true';
328
+ const globalDefaultIsTrue = this.$rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.IMPORTED_CLUSTER_DAY2_OPS_DEFAULT)?.value === 'true';
329
+ const annotationOrGlobalEnabled = annotationEnabled || (!annotationExists && globalDefaultIsTrue);
330
+ const canGetOpSchema = this.$getters['schemaFor'](OPERATION.ETCD_SNAPSHOT);
331
+
332
+ return !this.isLocal && canGetOpSchema && this.isDayTwoOpsFeatureEnabled && this.isImportedRke2K3s && annotationOrGlobalEnabled;
333
+ }
334
+
304
335
  get isK3s() {
305
336
  return this.mgmt?.isK3s;
306
337
  }
@@ -565,6 +596,21 @@ export default class ProvCluster extends SteveModel {
565
596
  url: `/v3/clusters/${ escape(this.mgmt.id) }?action=backupEtcd`,
566
597
  method: 'post',
567
598
  }, { root: true });
599
+ } else if ( this.isImportedWithDayTwoOps ) {
600
+ // For imported clusters with day 2 ops, create an operation CRD
601
+ const namespace = this.mgmt?.id;
602
+ const safePrefix = this.mgmt?.metadata?.name || this.mgmt?.id;
603
+ const spec = {
604
+ clusterRef: {
605
+ apiVersion: 'management.cattle.io/v3',
606
+ kind: 'Cluster',
607
+ name: this.mgmt?.id,
608
+ }
609
+ };
610
+
611
+ return createOperationCR(this.$dispatch, OPERATION.ETCD_SNAPSHOT, spec, namespace, safePrefix);
612
+ } else if (this.isImportedRke2K3s && !this.isDayTwoOpsFeatureEnabled) {
613
+ throw new Error(this.$rootGetters['i18n/t']('cluster.snapshot.day2OpsNotEnabled'));
568
614
  } else {
569
615
  const now = this.spec?.rkeConfig?.etcdSnapshotCreate?.generation || 0;
570
616
  const args = { generation: now + 1 };
@@ -582,6 +628,11 @@ export default class ProvCluster extends SteveModel {
582
628
  get etcdSnapshots() {
583
629
  const allSnapshots = this.$rootGetters['management/all']({ type: SNAPSHOT });
584
630
 
631
+ if (this.isImportedWithDayTwoOps) {
632
+ return allSnapshots
633
+ .filter((s) => s.metadata.namespace === this.mgmt?.id && s.spec?.clusterName === this.mgmt?.metadata?.name );
634
+ }
635
+
585
636
  return allSnapshots
586
637
  .filter((s) => s.metadata.namespace === this.namespace && s.clusterName === this.name );
587
638
  }
@@ -593,8 +644,7 @@ export default class ProvCluster extends SteveModel {
593
644
  rotateCertificates(cluster = this) {
594
645
  this.$dispatch('promptModal', {
595
646
  componentProps: { cluster },
596
-
597
- component: 'RotateCertificatesDialog'
647
+ component: 'RotateCertificatesDialog'
598
648
  });
599
649
  }
600
650
 
@@ -1,11 +1,20 @@
1
1
  import { formatSi } from '@shell/utils/units';
2
2
  import MachineTemplate from './rke-machine.cattle.io.machinetemplate';
3
3
 
4
+ const POOL_LOCATION_MATCHER = /\/host\/([^/]+)\/Resources(?:\/|$)/;
5
+
4
6
  export default class VmwarevsphereMachineTemplate extends MachineTemplate {
5
7
  get provider() {
6
8
  return 'vmwarevsphere';
7
9
  }
8
10
 
11
+ get providerLocation() {
12
+ const pool = this.spec?.template?.spec?.pool;
13
+ const match = pool?.match(POOL_LOCATION_MATCHER);
14
+
15
+ return match?.[1] || null;
16
+ }
17
+
9
18
  get providerSize() {
10
19
  const size = formatSi(this.spec.template.spec.memorySize * 1048576, 1024, 'iB');
11
20
 
@@ -1,6 +1,6 @@
1
1
  import NormanModel from '@shell/plugins/steve/norman-class';
2
2
  import { SNAPSHOT } from '@shell/config/labels-annotations';
3
- import { CAPI } from '@shell/config/types';
3
+ import { CAPI, DEFAULT_WORKSPACE } from '@shell/config/types';
4
4
  import { get } from '@shell/utils/object';
5
5
  import { base64Decode } from '@shell/utils/crypto';
6
6
  import { ucFirst } from '@shell/utils/string';
@@ -11,13 +11,11 @@ export default class EtcdBackup extends NormanModel {
11
11
  * Restrict actions for snapshots to restore only
12
12
  */
13
13
  get _availableActions() {
14
- const enabled = this.snapshotFile?.status === STATES_ENUM.SUCCESSFUL;
15
-
16
14
  return [{
17
- action: 'promptRestore',
18
- enabled,
19
- icon: 'icon icon-backup-restore',
20
- label: 'Restore'
15
+ action: 'promptRestore',
16
+ enabled: this.restoreEnabled,
17
+ icon: 'icon icon-backup-restore',
18
+ label: 'Restore'
21
19
  }];
22
20
  }
23
21
 
@@ -30,7 +28,7 @@ export default class EtcdBackup extends NormanModel {
30
28
  }
31
29
 
32
30
  get clusterId() {
33
- return this.cluster.id;
31
+ return this.cluster?.id;
34
32
  }
35
33
 
36
34
  get name() {
@@ -38,7 +36,9 @@ export default class EtcdBackup extends NormanModel {
38
36
  }
39
37
 
40
38
  get cluster() {
41
- return this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, `${ this.metadata.namespace }/${ this.clusterName }`);
39
+ const ns = this.metadata?.namespace || DEFAULT_WORKSPACE;
40
+
41
+ return this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, `${ ns }/${ this.clusterName }`);
42
42
  }
43
43
 
44
44
  get rke2() {
@@ -49,6 +49,14 @@ export default class EtcdBackup extends NormanModel {
49
49
  return this.snapshotFile?.name || this.name;
50
50
  }
51
51
 
52
+ get restoreEnabled() {
53
+ return this.snapshotFile?.status === STATES_ENUM.SUCCESSFUL && !this.isSnapshotTooOld;
54
+ }
55
+
56
+ get isSnapshotTooOld() {
57
+ return this.snapshotFile?.createdAt <= this.cluster?.metadata?.creationTimestamp;
58
+ }
59
+
52
60
  get errorMessage() {
53
61
  const inError = get(this, 'snapshotFile.status') === STATES_ENUM.FAILED;
54
62
 
package/models/secret.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import r from 'jsrsasign';
2
2
  import { CERTMANAGER, KUBERNETES, UI_PROJECT_SECRET, UI_PROJECT_SECRET_COPY } from '@shell/config/labels-annotations';
3
+ import { GITHUB_APP_SECRET_KEYS } from '@shell/config/secret';
3
4
  import { base64Decode, base64Encode } from '@shell/utils/crypto';
4
5
  import { removeObjects } from '@shell/utils/array';
5
6
  import { MANAGEMENT, SERVICE_ACCOUNT, VIRTUAL_TYPES } from '@shell/config/types';
@@ -57,6 +58,14 @@ export default class Secret extends SteveModel {
57
58
  return this._type === TYPES.SSH && !!this.data && 'known_hosts' in this.data;
58
59
  }
59
60
 
61
+ // A GitHub App auth secret is an Opaque secret holding the GitHub App data keys
62
+ get isGithubApp() {
63
+ return this._type === TYPES.OPAQUE && !!this.data &&
64
+ GITHUB_APP_SECRET_KEYS.APP_ID in this.data &&
65
+ GITHUB_APP_SECRET_KEYS.INSTALLATION_ID in this.data &&
66
+ GITHUB_APP_SECRET_KEYS.PRIVATE_KEY in this.data;
67
+ }
68
+
60
69
  get issuer() {
61
70
  const { metadata:{ annotations = {} } } = this;
62
71
 
@@ -237,6 +246,11 @@ export default class Secret extends SteveModel {
237
246
  return this.sshUser;
238
247
  } else if ( this._type === TYPES.SERVICE_ACCT ) {
239
248
  return this.metadata?.annotations?.['kubernetes.io/service-account.name'];
249
+ } else if ( this.isGithubApp ) {
250
+ const appId = base64Decode(this.data[GITHUB_APP_SECRET_KEYS.APP_ID]);
251
+ const installationId = base64Decode(this.data[GITHUB_APP_SECRET_KEYS.INSTALLATION_ID]);
252
+
253
+ return `${ appId } / ${ installationId }`;
240
254
  }
241
255
 
242
256
  return this.keysDisplay;
@@ -274,6 +288,11 @@ export default class Secret extends SteveModel {
274
288
 
275
289
  get subTypeDisplay() {
276
290
  const type = this._type || '';
291
+
292
+ if ( this.isGithubApp ) {
293
+ return this.$rootGetters['i18n/withFallback'](`secret.githubApp.label`, null, 'GitHub App');
294
+ }
295
+
277
296
  const fallback = type.replace(/^kubernetes.io\//, '');
278
297
 
279
298
  return this.$rootGetters['i18n/withFallback'](`secret.types."${ type }"`, null, fallback);
@@ -10,6 +10,7 @@ import { matching } from '@shell/utils/selector-typed';
10
10
  import { defineAsyncComponent, markRaw } from 'vue';
11
11
  import { useResourceCardRow } from '@shell/components/Resource/Detail/Card/StateCard/composables';
12
12
  import { colorForState as colorForStateFn, stateDisplay as stateDisplayFn } from '@shell/plugins/dashboard-store/resource-class';
13
+ import { POD_SHELL } from '@shell/store/features';
13
14
 
14
15
  export const defaultContainer = {
15
16
  imagePullPolicy: 'Always',
@@ -28,6 +29,7 @@ export default class Workload extends WorkloadService {
28
29
  get _availableActions() {
29
30
  let out = super._availableActions;
30
31
  const type = this._type ? this._type : this.type;
32
+ const podShellFeatureEnabled = !!this.$rootGetters['features/get'](POD_SHELL);
31
33
 
32
34
  const editYaml = findBy(out, 'action', 'goToEditYaml');
33
35
  const index = editYaml ? out.indexOf(editYaml) : 0;
@@ -76,13 +78,16 @@ export default class Workload extends WorkloadService {
76
78
 
77
79
  insertAt(out, 0, { divider: true }) ;
78
80
 
79
- insertAt(out, 0, {
80
- action: 'openShell',
81
- enabled: !!this.links.view,
82
- icon: 'icon icon-chevron-right',
83
- label: this.t('action.openShell'),
84
- total: 1,
85
- });
81
+ // Only add the menu item for the pod shell if the feature flag is enabled
82
+ if (podShellFeatureEnabled) {
83
+ insertAt(out, 0, {
84
+ action: 'openShell',
85
+ enabled: !!this.links.view,
86
+ icon: 'icon icon-chevron-right',
87
+ label: this.t('action.openShell'),
88
+ total: 1,
89
+ });
90
+ }
86
91
 
87
92
  const toFilter = ['cloneYaml'];
88
93
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.12-rc.4",
3
+ "version": "3.0.12-rc.6",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancher/dashboard",
6
6
  "license": "Apache-2.0",
@@ -177,7 +177,7 @@ export default {
177
177
  this.vendor = getVendor();
178
178
  this.providers = providers;
179
179
  this.hasLocal = hasLocal;
180
- this.showLocal = !hasOthers || (this.$route.query[LOCAL] === _FLAGGED);
180
+ this.showLocal = hasLocal && (!hasOthers || (this.$route.query[LOCAL] === _FLAGGED));
181
181
  this.customLoginError = JSON.parse(value).loginError;
182
182
  this.firstLogin = firstLoginSetting?.value === 'true';
183
183
  this.username = this.firstLogin ? 'admin' : this.username;