@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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createOperationCR } from '@shell/utils/operation-cr';
|
|
2
|
+
|
|
3
|
+
describe('util: operation-cr', () => {
|
|
4
|
+
it('should dispatch create and save the operation resource', async() => {
|
|
5
|
+
const save = jest.fn().mockResolvedValue({ id: 'op-1' });
|
|
6
|
+
const dispatch = jest.fn().mockResolvedValue({ save });
|
|
7
|
+
const spec = {
|
|
8
|
+
clusterRef: {
|
|
9
|
+
apiVersion: 'management.cattle.io/v3',
|
|
10
|
+
kind: 'Cluster',
|
|
11
|
+
name: 'c-m-1',
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const out = await createOperationCR(dispatch, 'operation.test', spec, 'c-m-1', 'cluster-name');
|
|
16
|
+
|
|
17
|
+
expect(dispatch).toHaveBeenCalledWith('management/create', {
|
|
18
|
+
type: 'operation.test',
|
|
19
|
+
metadata: {
|
|
20
|
+
namespace: 'c-m-1',
|
|
21
|
+
generateName: 'cluster-name-'
|
|
22
|
+
},
|
|
23
|
+
spec,
|
|
24
|
+
}, { root: true });
|
|
25
|
+
expect(save).toHaveBeenCalledWith();
|
|
26
|
+
expect(out).toStrictEqual({ id: 'op-1' });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should surface create failures', async() => {
|
|
30
|
+
const dispatch = jest.fn().mockRejectedValue(new Error('forbidden'));
|
|
31
|
+
|
|
32
|
+
await expect(createOperationCR(dispatch, 'operation.test', {}, 'c-m-1', 'cluster-name')).rejects.toThrow('forbidden');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a day 2 operation CR for imported clusters.
|
|
3
|
+
*
|
|
4
|
+
* @param {Function} dispatch - The Vuex dispatch function
|
|
5
|
+
* @param {string} type - The operation CRD type
|
|
6
|
+
* @param {object} spec - The operation spec
|
|
7
|
+
* @param {string} namespace - The namespace for the operation CR
|
|
8
|
+
* @param {string} namePrefix - The name prefix for the generated name
|
|
9
|
+
* @returns {Promise} The saved resource
|
|
10
|
+
*/
|
|
11
|
+
export async function createOperationCR(dispatch, type, spec, namespace, namePrefix) {
|
|
12
|
+
const resource = await dispatch('management/create', {
|
|
13
|
+
type,
|
|
14
|
+
metadata: { namespace, generateName: `${ namePrefix }-` },
|
|
15
|
+
spec,
|
|
16
|
+
}, { root: true });
|
|
17
|
+
|
|
18
|
+
return resource.save();
|
|
19
|
+
}
|
package/utils/require-asset.ts
CHANGED
|
@@ -88,6 +88,13 @@ export function requireJson(path: string): object {
|
|
|
88
88
|
return mod.default || mod;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// Expose asset resolvers on window so extension builds (which use a stub
|
|
92
|
+
// instead of require.context) can delegate to the host dashboard at runtime.
|
|
93
|
+
if (typeof window !== 'undefined') {
|
|
94
|
+
(window as any).__shell_requireAsset = requireAsset;
|
|
95
|
+
(window as any).__shell_requireJson = requireJson;
|
|
96
|
+
}
|
|
97
|
+
|
|
91
98
|
// Exported for testing — allows injecting mock contexts
|
|
92
99
|
export function _setContexts(img: WebpackRequireContext | null, json: WebpackRequireContext | null): void {
|
|
93
100
|
imgCtx = img;
|
|
@@ -3,28 +3,22 @@ import { privateRegistryRequired } from '@shell/utils/validators/private-registr
|
|
|
3
3
|
const makeCtx = (overrides: any = {}) => ({
|
|
4
4
|
t: jest.fn((key: string, params?: any) => (params ? `${ key }:${ JSON.stringify(params) }` : key)),
|
|
5
5
|
privateRegistryEnabled: false,
|
|
6
|
-
normanCluster: { importedConfig: {
|
|
7
|
-
|
|
6
|
+
normanCluster: { importedConfig: {} },
|
|
7
|
+
$store: { getters: { 'management/byId': () => null } },
|
|
8
8
|
...overrides,
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
it('should return undefined when the cluster is not imported', () => {
|
|
13
|
-
const ctx = makeCtx({ isImportedCluster: false, privateRegistryEnabled: true });
|
|
14
|
-
const rule = privateRegistryRequired(ctx);
|
|
15
|
-
|
|
16
|
-
expect(rule()).toBeUndefined();
|
|
17
|
-
});
|
|
11
|
+
const storeWithGlobalRegistry = { getters: { 'management/byId': () => ({ value: 'registry.global.io' }) } };
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
describe('privateRegistryRequired', () => {
|
|
14
|
+
it('should return undefined when a global default registry is configured', () => {
|
|
15
|
+
const ctx = makeCtx({
|
|
22
16
|
privateRegistryEnabled: true,
|
|
23
|
-
|
|
24
|
-
};
|
|
17
|
+
$store: storeWithGlobalRegistry,
|
|
18
|
+
});
|
|
25
19
|
const rule = privateRegistryRequired(ctx);
|
|
26
20
|
|
|
27
|
-
expect(rule()).
|
|
21
|
+
expect(rule()).toBeUndefined();
|
|
28
22
|
});
|
|
29
23
|
|
|
30
24
|
it('should return undefined when the registry toggle is off', () => {
|
|
@@ -73,4 +67,22 @@ describe('privateRegistryRequired', () => {
|
|
|
73
67
|
|
|
74
68
|
expect(rule()).toBeUndefined();
|
|
75
69
|
});
|
|
70
|
+
|
|
71
|
+
it('should handle $store getter throwing gracefully', () => {
|
|
72
|
+
const ctx = makeCtx({
|
|
73
|
+
privateRegistryEnabled: true,
|
|
74
|
+
normanCluster: { importedConfig: {} },
|
|
75
|
+
$store: {
|
|
76
|
+
getters: {
|
|
77
|
+
get 'management/byId'() {
|
|
78
|
+
throw new Error('no store');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const rule = privateRegistryRequired(ctx);
|
|
84
|
+
|
|
85
|
+
// Falls through to validation since no global default detected
|
|
86
|
+
expect(rule()).toStrictEqual('validation.required:{"key":"cluster.privateRegistry.label"}');
|
|
87
|
+
});
|
|
76
88
|
});
|
|
@@ -1,19 +1,30 @@
|
|
|
1
|
+
import { VuexStore } from 'types/store/vuex';
|
|
1
2
|
import { Translation } from '@shell/types/t';
|
|
2
3
|
import formRulesGenerator from '@shell/utils/validators/formRules';
|
|
4
|
+
import { SETTING } from '@shell/config/settings';
|
|
5
|
+
import { MANAGEMENT } from '@shell/config/types';
|
|
3
6
|
|
|
4
7
|
interface PrivateRegistryRuleContext {
|
|
5
8
|
t: Translation;
|
|
6
9
|
privateRegistryEnabled: boolean;
|
|
7
10
|
normanCluster: { importedConfig?: { privateRegistryURL?: string | null } } | null;
|
|
8
|
-
|
|
11
|
+
$store: VuexStore
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
export function privateRegistryRequired(ctx: PrivateRegistryRuleContext) {
|
|
12
15
|
return () => {
|
|
13
|
-
|
|
14
|
-
const isImported = 'isImportedCluster' in ctx ? !!ctx.isImportedCluster : true;
|
|
16
|
+
let hasGlobalDefault = false;
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
try {
|
|
19
|
+
// settings are loaded when the dashboard loads so using a synchronous getter is reliable here
|
|
20
|
+
const globalRegistrySetting = ctx.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.SYSTEM_DEFAULT_REGISTRY);
|
|
21
|
+
|
|
22
|
+
hasGlobalDefault = !!globalRegistrySetting?.value;
|
|
23
|
+
} catch {
|
|
24
|
+
// if no setting proceed like there's no global default
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!ctx.privateRegistryEnabled || hasGlobalDefault) {
|
|
17
28
|
return undefined;
|
|
18
29
|
}
|
|
19
30
|
const url = ctx.normanCluster?.importedConfig?.privateRegistryURL;
|