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

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 (81) hide show
  1. package/assets/styles/global/_button.scss +1 -1
  2. package/assets/translations/en-us.yaml +39 -10
  3. package/components/ActionDropdownShell.vue +5 -3
  4. package/components/ButtonGroup.vue +26 -1
  5. package/components/CruResource.vue +51 -2
  6. package/components/PromptRestore.vue +93 -32
  7. package/components/Questions/index.vue +1 -0
  8. package/components/ResourceTable.vue +1 -0
  9. package/components/SortableTable/index.vue +4 -3
  10. package/components/Wizard.vue +14 -1
  11. package/components/__tests__/ButtonGroup.test.ts +56 -0
  12. package/components/__tests__/PromptRestore.test.ts +169 -19
  13. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  14. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  15. package/components/fleet/HelmOpAppCoConfigTab.vue +4 -0
  16. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  17. package/components/form/FileSelector.vue +39 -1
  18. package/components/form/PrivateRegistry.constants.ts +7 -0
  19. package/components/form/PrivateRegistry.vue +253 -18
  20. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  21. package/components/form/__tests__/FileSelector.test.ts +23 -0
  22. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  23. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  24. package/components/formatter/EtcdSnapshotName.vue +73 -0
  25. package/components/nav/Header.vue +8 -1
  26. package/components/templates/default.vue +7 -0
  27. package/config/features.js +1 -0
  28. package/config/labels-annotations.js +2 -0
  29. package/config/product/manager.js +6 -0
  30. package/config/secret.ts +10 -0
  31. package/config/settings.ts +6 -2
  32. package/config/types.js +7 -0
  33. package/detail/provisioning.cattle.io.cluster.vue +79 -3
  34. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  35. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  36. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  37. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +101 -0
  38. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  39. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  40. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  41. package/edit/fleet.cattle.io.helmop.vue +51 -5
  42. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  43. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  44. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  45. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  46. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  47. package/edit/provisioning.cattle.io.cluster/rke2.vue +5 -1
  48. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  49. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  50. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +156 -0
  51. package/models/__tests__/secret.test.ts +68 -1
  52. package/models/management.cattle.io.cluster.js +21 -3
  53. package/models/pod.js +13 -2
  54. package/models/provisioning.cattle.io.cluster.js +59 -9
  55. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  56. package/models/secret.js +19 -0
  57. package/models/workload.js +12 -7
  58. package/package.json +1 -1
  59. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
  60. package/pages/c/_cluster/apps/charts/install.vue +114 -28
  61. package/pkg/require-asset.lib.js +25 -0
  62. package/pkg/vue.config.js +7 -0
  63. package/plugins/dashboard-store/__tests__/resource-class.test.ts +84 -0
  64. package/plugins/dashboard-store/getters.js +0 -1
  65. package/plugins/dashboard-store/resource-class.js +52 -12
  66. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  67. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  68. package/rancher-components/RcButton/index.ts +1 -1
  69. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  70. package/store/__tests__/features.test.ts +131 -0
  71. package/store/__tests__/growl.test.ts +374 -0
  72. package/store/__tests__/modal.test.ts +131 -0
  73. package/store/__tests__/slideInPanel.test.ts +88 -0
  74. package/store/__tests__/type-map.utils.test.ts +433 -0
  75. package/store/features.js +4 -0
  76. package/types/shell/index.d.ts +62 -0
  77. package/utils/__tests__/operation-cr.test.ts +34 -0
  78. package/utils/operation-cr.js +19 -0
  79. package/utils/require-asset.ts +7 -0
  80. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  81. package/utils/validators/private-registry.ts +15 -4
@@ -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,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.5",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancher/dashboard",
6
6
  "license": "Apache-2.0",