@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.
- package/assets/styles/global/_button.scss +1 -1
- package/assets/translations/en-us.yaml +39 -10
- package/components/ActionDropdownShell.vue +5 -3
- package/components/ButtonGroup.vue +26 -1
- package/components/CruResource.vue +51 -2
- package/components/PromptRestore.vue +93 -32
- package/components/Questions/index.vue +1 -0
- package/components/ResourceTable.vue +1 -0
- package/components/SortableTable/index.vue +4 -3
- package/components/Wizard.vue +14 -1
- package/components/__tests__/ButtonGroup.test.ts +56 -0
- package/components/__tests__/PromptRestore.test.ts +169 -19
- package/components/fleet/GitRepoAdvancedTab.vue +1 -0
- package/components/fleet/GitRepoMetadataTab.vue +5 -0
- package/components/fleet/HelmOpAppCoConfigTab.vue +4 -0
- package/components/fleet/HelmOpMetadataTab.vue +5 -0
- package/components/form/FileSelector.vue +39 -1
- package/components/form/PrivateRegistry.constants.ts +7 -0
- package/components/form/PrivateRegistry.vue +253 -18
- package/components/form/SelectOrCreateAuthSecret.vue +140 -17
- package/components/form/__tests__/FileSelector.test.ts +23 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
- package/components/formatter/EtcdSnapshotName.vue +73 -0
- package/components/nav/Header.vue +8 -1
- package/components/templates/default.vue +7 -0
- package/config/features.js +1 -0
- package/config/labels-annotations.js +2 -0
- package/config/product/manager.js +6 -0
- package/config/secret.ts +10 -0
- package/config/settings.ts +6 -2
- package/config/types.js +7 -0
- package/detail/provisioning.cattle.io.cluster.vue +79 -3
- package/dialog/RotateEncryptionKeyDialog.vue +33 -9
- package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +101 -0
- package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
- package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
- package/edit/fleet.cattle.io.gitrepo.vue +70 -16
- package/edit/fleet.cattle.io.helmop.vue +51 -5
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
- package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +5 -1
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +156 -0
- package/models/__tests__/secret.test.ts +68 -1
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/pod.js +13 -2
- package/models/provisioning.cattle.io.cluster.js +59 -9
- package/models/rke.cattle.io.etcdsnapshot.js +17 -9
- package/models/secret.js +19 -0
- package/models/workload.js +12 -7
- package/package.json +1 -1
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
- package/pages/c/_cluster/apps/charts/install.vue +114 -28
- package/pkg/require-asset.lib.js +25 -0
- package/pkg/vue.config.js +7 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +84 -0
- package/plugins/dashboard-store/getters.js +0 -1
- package/plugins/dashboard-store/resource-class.js +52 -12
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
- package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
- package/rancher-components/RcButton/index.ts +1 -1
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
- package/store/__tests__/features.test.ts +131 -0
- package/store/__tests__/growl.test.ts +374 -0
- package/store/__tests__/modal.test.ts +131 -0
- package/store/__tests__/slideInPanel.test.ts +88 -0
- package/store/__tests__/type-map.utils.test.ts +433 -0
- package/store/features.js +4 -0
- package/types/shell/index.d.ts +62 -0
- package/utils/__tests__/operation-cr.test.ts +34 -0
- package/utils/operation-cr.js +19 -0
- package/utils/require-asset.ts +7 -0
- package/utils/validators/__tests__/private-registry.test.ts +27 -15
- 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 {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
18
|
-
enabled,
|
|
19
|
-
icon:
|
|
20
|
-
label:
|
|
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
|
|
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
|
-
|
|
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);
|
package/models/workload.js
CHANGED
|
@@ -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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|