@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,6 +1,7 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
|
|
3
3
|
import { AUTH_TYPE } from '@shell/config/types';
|
|
4
|
+
import { base64Encode } from '@shell/utils/crypto';
|
|
4
5
|
import GitRepo from '@shell/models/fleet.cattle.io.gitrepo';
|
|
5
6
|
import GitRepoComponent from '@shell/edit/fleet.cattle.io.gitrepo.vue';
|
|
6
7
|
|
|
@@ -327,3 +328,94 @@ describe('view: fleet.cattle.io.gitrepo, GitHub password banner - should', () =>
|
|
|
327
328
|
expect(githubBanner.exists()).toBe(shouldShowBanner);
|
|
328
329
|
});
|
|
329
330
|
});
|
|
331
|
+
|
|
332
|
+
describe('view: fleet.cattle.io.gitrepo, GitHub App auth - should', () => {
|
|
333
|
+
const originalDispatch = mockStore.dispatch;
|
|
334
|
+
|
|
335
|
+
afterEach(() => {
|
|
336
|
+
mockStore.dispatch = originalDispatch;
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('create an Opaque secret with the GitHub App data keys on doCreate', async() => {
|
|
340
|
+
const fakeSecret: any = {
|
|
341
|
+
metadata: { name: 'gitrepo-auth-abc' },
|
|
342
|
+
save: jest.fn().mockResolvedValue(undefined),
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
mockStore.dispatch = jest.fn().mockResolvedValue(fakeSecret);
|
|
346
|
+
|
|
347
|
+
const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _CREATE }));
|
|
348
|
+
|
|
349
|
+
await (wrapper.vm as any).doCreate('clientSecretName', {
|
|
350
|
+
selected: AUTH_TYPE._GITHUB_APP,
|
|
351
|
+
githubAppId: 'app-id',
|
|
352
|
+
githubAppInstallationId: 'install-id',
|
|
353
|
+
githubAppPrivateKey: 'private-key',
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
expect(fakeSecret._type).toBe('Opaque');
|
|
357
|
+
expect(fakeSecret.data).toStrictEqual({
|
|
358
|
+
github_app_id: base64Encode('app-id'),
|
|
359
|
+
github_app_installation_id: base64Encode('install-id'),
|
|
360
|
+
github_app_private_key: base64Encode('private-key'),
|
|
361
|
+
});
|
|
362
|
+
expect(fakeSecret.save).toHaveBeenCalledWith();
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe('view: fleet.cattle.io.gitrepo, beforeNext dryRun validation', () => {
|
|
367
|
+
it('should call dryRunCreate when leaving stepMetadata in create mode', async() => {
|
|
368
|
+
const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _CREATE }));
|
|
369
|
+
const vm = wrapper.vm as any;
|
|
370
|
+
|
|
371
|
+
vm.value.dryRunCreate = jest.fn().mockResolvedValue({});
|
|
372
|
+
|
|
373
|
+
await vm.beforeNext({ name: 'stepMetadata' });
|
|
374
|
+
|
|
375
|
+
expect(vm.value.dryRunCreate).toHaveBeenCalledWith(expect.objectContaining({
|
|
376
|
+
type: 'fleet.cattle.io.gitrepo',
|
|
377
|
+
metadata: expect.objectContaining({
|
|
378
|
+
name: 'test',
|
|
379
|
+
namespace: 'test',
|
|
380
|
+
}),
|
|
381
|
+
spec: expect.objectContaining({ repo: 'https://example.com/placeholder' }),
|
|
382
|
+
}));
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should not call dryRunCreate for non-metadata steps', async() => {
|
|
386
|
+
const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _CREATE }));
|
|
387
|
+
const vm = wrapper.vm as any;
|
|
388
|
+
|
|
389
|
+
vm.value.dryRunCreate = jest.fn();
|
|
390
|
+
|
|
391
|
+
await vm.beforeNext({ name: 'stepRepo' });
|
|
392
|
+
|
|
393
|
+
expect(vm.value.dryRunCreate).not.toHaveBeenCalled();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should not call dryRunCreate in edit mode', async() => {
|
|
397
|
+
const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _EDIT }));
|
|
398
|
+
const vm = wrapper.vm as any;
|
|
399
|
+
|
|
400
|
+
vm.value.dryRunCreate = jest.fn();
|
|
401
|
+
|
|
402
|
+
await vm.beforeNext({ name: 'stepMetadata' });
|
|
403
|
+
|
|
404
|
+
expect(vm.value.dryRunCreate).not.toHaveBeenCalled();
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('should reject with API errors when dryRunCreate fails', async() => {
|
|
408
|
+
const apiError = {
|
|
409
|
+
_status: 409,
|
|
410
|
+
message: 'gitrepos.fleet.cattle.io "test" already exists',
|
|
411
|
+
statusText: 'Conflict',
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _CREATE }));
|
|
415
|
+
const vm = wrapper.vm as any;
|
|
416
|
+
|
|
417
|
+
vm.value.dryRunCreate = jest.fn().mockRejectedValue(apiError);
|
|
418
|
+
|
|
419
|
+
await expect(vm.beforeNext({ name: 'stepMetadata' })).rejects.toStrictEqual(apiError);
|
|
420
|
+
});
|
|
421
|
+
});
|
|
@@ -421,6 +421,12 @@ describe.each([
|
|
|
421
421
|
expect(wrapper.vm.value.spec.downstreamResources).toStrictEqual([{ name: 'configMap2', kind: 'ConfigMap' }, { name: 'configMap3', kind: 'ConfigMap' }]);
|
|
422
422
|
});
|
|
423
423
|
|
|
424
|
+
it('should have a beforeNext method', () => {
|
|
425
|
+
const wrapper = mount(HelmOpComponent, initHelmOp({ mode }));
|
|
426
|
+
|
|
427
|
+
expect(typeof wrapper.vm.beforeNext).toBe('function');
|
|
428
|
+
});
|
|
429
|
+
|
|
424
430
|
if (mode === _CREATE) {
|
|
425
431
|
it('should set created-by-user-id label when updateBeforeSave is called in CREATE mode', () => {
|
|
426
432
|
const mockCurrentUser = { id: 'user-123' };
|
|
@@ -475,3 +481,98 @@ describe.each([
|
|
|
475
481
|
});
|
|
476
482
|
}
|
|
477
483
|
});
|
|
484
|
+
|
|
485
|
+
describe('view: fleet.cattle.io.helmop, beforeNext dryRun validation', () => {
|
|
486
|
+
const initHelmOpWithMetadata = (props: any) => {
|
|
487
|
+
const value = new HelmOp({
|
|
488
|
+
...mockHelmOp,
|
|
489
|
+
metadata: {
|
|
490
|
+
name: 'test-helmop',
|
|
491
|
+
namespace: 'fleet-default',
|
|
492
|
+
},
|
|
493
|
+
}, {
|
|
494
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
495
|
+
dispatch: jest.fn(),
|
|
496
|
+
rootGetters: { 'i18n/t': jest.fn() },
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
value.applyDefaults = () => {};
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
props: {
|
|
503
|
+
value,
|
|
504
|
+
...props
|
|
505
|
+
},
|
|
506
|
+
provide: {
|
|
507
|
+
store: createStore({
|
|
508
|
+
getters: {
|
|
509
|
+
currentStore: () => 'current_store',
|
|
510
|
+
'management/paginationEnabled': () => () => false
|
|
511
|
+
}
|
|
512
|
+
})
|
|
513
|
+
},
|
|
514
|
+
computed: mockComputed,
|
|
515
|
+
global: { mocks },
|
|
516
|
+
};
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
it('should call dryRunCreate when leaving basics step in create mode', async() => {
|
|
520
|
+
const wrapper = mount(HelmOpComponent, initHelmOpWithMetadata({ mode: _CREATE }));
|
|
521
|
+
const vm = wrapper.vm as any;
|
|
522
|
+
|
|
523
|
+
vm.value.dryRunCreate = jest.fn().mockResolvedValue({});
|
|
524
|
+
|
|
525
|
+
await vm.beforeNext({ name: 'basics' });
|
|
526
|
+
|
|
527
|
+
expect(vm.value.dryRunCreate).toHaveBeenCalledWith(expect.objectContaining({
|
|
528
|
+
type: 'fleet.cattle.io.helmop',
|
|
529
|
+
metadata: expect.objectContaining({
|
|
530
|
+
name: 'test-helmop',
|
|
531
|
+
namespace: 'fleet-default',
|
|
532
|
+
}),
|
|
533
|
+
spec: expect.objectContaining({
|
|
534
|
+
helm: expect.objectContaining({
|
|
535
|
+
chart: 'placeholder',
|
|
536
|
+
repo: 'https://example.com',
|
|
537
|
+
}),
|
|
538
|
+
}),
|
|
539
|
+
}));
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('should not call dryRunCreate for non-basics steps', async() => {
|
|
543
|
+
const wrapper = mount(HelmOpComponent, initHelmOpWithMetadata({ mode: _CREATE }));
|
|
544
|
+
const vm = wrapper.vm as any;
|
|
545
|
+
|
|
546
|
+
vm.value.dryRunCreate = jest.fn();
|
|
547
|
+
|
|
548
|
+
await vm.beforeNext({ name: 'chart' });
|
|
549
|
+
|
|
550
|
+
expect(vm.value.dryRunCreate).not.toHaveBeenCalled();
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it('should not call dryRunCreate in edit mode', async() => {
|
|
554
|
+
const wrapper = mount(HelmOpComponent, initHelmOpWithMetadata({ mode: _EDIT }));
|
|
555
|
+
const vm = wrapper.vm as any;
|
|
556
|
+
|
|
557
|
+
vm.value.dryRunCreate = jest.fn();
|
|
558
|
+
|
|
559
|
+
await vm.beforeNext({ name: 'basics' });
|
|
560
|
+
|
|
561
|
+
expect(vm.value.dryRunCreate).not.toHaveBeenCalled();
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it('should reject with API errors when dryRunCreate fails', async() => {
|
|
565
|
+
const apiError = {
|
|
566
|
+
_status: 409,
|
|
567
|
+
message: 'helmops.fleet.cattle.io "test-helmop" already exists',
|
|
568
|
+
statusText: 'Conflict',
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
const wrapper = mount(HelmOpComponent, initHelmOpWithMetadata({ mode: _CREATE }));
|
|
572
|
+
const vm = wrapper.vm as any;
|
|
573
|
+
|
|
574
|
+
vm.value.dryRunCreate = jest.fn().mockRejectedValue(apiError);
|
|
575
|
+
|
|
576
|
+
await expect(vm.beforeNext({ name: 'basics' })).rejects.toStrictEqual(apiError);
|
|
577
|
+
});
|
|
578
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
|
-
import Settings from '@shell/edit/management.cattle.io.setting.vue';
|
|
2
|
+
import Settings from '@shell/edit/management.cattle.io.setting/index.vue';
|
|
3
3
|
import { SETTING } from '@shell/config/settings';
|
|
4
4
|
|
|
5
5
|
const requiredSetup = () => ({
|
|
@@ -18,6 +18,7 @@ const requiredSetup = () => ({
|
|
|
18
18
|
},
|
|
19
19
|
$route: { query: { AS: '' } },
|
|
20
20
|
$router: { applyQuery: jest.fn() },
|
|
21
|
+
t: (key: string) => key,
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
});
|
|
@@ -103,48 +103,46 @@ export default {
|
|
|
103
103
|
</script>
|
|
104
104
|
|
|
105
105
|
<template>
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
<div class="
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/>
|
|
125
|
-
</div>
|
|
106
|
+
<CruResource
|
|
107
|
+
:validation-passed="!!name && !!value.spec.benchmarkVersion"
|
|
108
|
+
:done-route="doneRoute"
|
|
109
|
+
:resource="value"
|
|
110
|
+
:mode="mode"
|
|
111
|
+
:errors="errors"
|
|
112
|
+
@finish="save"
|
|
113
|
+
@error="e=>errors = e"
|
|
114
|
+
>
|
|
115
|
+
<div class="spacer" />
|
|
116
|
+
<div class="row">
|
|
117
|
+
<div class="col span-12">
|
|
118
|
+
<NameNsDescription
|
|
119
|
+
:mode="mode"
|
|
120
|
+
:value="value"
|
|
121
|
+
:namespaced="false"
|
|
122
|
+
@change="name=value.metadata.name"
|
|
123
|
+
/>
|
|
126
124
|
</div>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
125
|
+
</div>
|
|
126
|
+
<div class="row">
|
|
127
|
+
<div class="col span-6">
|
|
128
|
+
<LabeledSelect
|
|
129
|
+
v-model:value="value.spec.benchmarkVersion"
|
|
130
|
+
:mode="mode"
|
|
131
|
+
:label="t('compliance.benchmarkVersion')"
|
|
132
|
+
:options="compatibleBenchmarkNames"
|
|
133
|
+
/>
|
|
136
134
|
</div>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
135
|
+
</div>
|
|
136
|
+
<div class="spacer" />
|
|
137
|
+
<div class="row">
|
|
138
|
+
<div class="col span-6">
|
|
139
|
+
<ArrayList
|
|
140
|
+
v-model:value="value.spec.skipTests"
|
|
141
|
+
:title="t('compliance.testsToSkip')"
|
|
142
|
+
:value-placeholder="t('compliance.testID')"
|
|
143
|
+
:mode="mode"
|
|
144
|
+
/>
|
|
147
145
|
</div>
|
|
148
|
-
</
|
|
149
|
-
</
|
|
146
|
+
</div>
|
|
147
|
+
</CruResource>
|
|
150
148
|
</template>
|
|
@@ -8,7 +8,7 @@ import Loading from '@shell/components/Loading';
|
|
|
8
8
|
import { base64Decode, base64Encode } from '@shell/utils/crypto';
|
|
9
9
|
import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
|
|
10
10
|
import { CATALOG, FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
|
|
11
|
-
import { SECRET_TYPES } from '@shell/config/secret';
|
|
11
|
+
import { SECRET_TYPES, GITHUB_APP_SECRET_KEYS } from '@shell/config/secret';
|
|
12
12
|
import FormValidation from '@shell/mixins/form-validation';
|
|
13
13
|
import { toSeconds } from '@shell/utils/duration';
|
|
14
14
|
import Tab from '@shell/components/Tabbed/Tab.vue';
|
|
@@ -97,10 +97,19 @@ export default {
|
|
|
97
97
|
refValue,
|
|
98
98
|
displayHelmRepoUrlRegex: false,
|
|
99
99
|
targetsCreated: '',
|
|
100
|
-
fvFormRuleSets: [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
fvFormRuleSets: [
|
|
101
|
+
{
|
|
102
|
+
step: 'stepMetadata',
|
|
103
|
+
path: 'metadata.name',
|
|
104
|
+
rules: ['subDomain'],
|
|
105
|
+
translationKey: 'nameNsDescription.name.label'
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
step: 'stepRepo',
|
|
109
|
+
path: 'spec.repo',
|
|
110
|
+
rules: ['urlRepository'],
|
|
111
|
+
},
|
|
112
|
+
],
|
|
104
113
|
touched: null,
|
|
105
114
|
};
|
|
106
115
|
},
|
|
@@ -129,7 +138,7 @@ export default {
|
|
|
129
138
|
label: this.t('fleet.gitRepo.add.steps.metadata.label'),
|
|
130
139
|
subtext: this.t('fleet.gitRepo.add.steps.metadata.subtext'),
|
|
131
140
|
descriptionKey: 'fleet.gitRepo.add.steps.metadata.description',
|
|
132
|
-
ready: this.isView || !!this.value.metadata.name,
|
|
141
|
+
ready: this.isView || (!!this.value.metadata.name && this.stepPathErrors('stepMetadata').length === 0),
|
|
133
142
|
weight: 1
|
|
134
143
|
},
|
|
135
144
|
{
|
|
@@ -206,6 +215,15 @@ export default {
|
|
|
206
215
|
},
|
|
207
216
|
|
|
208
217
|
methods: {
|
|
218
|
+
stepPathErrors(stepName) {
|
|
219
|
+
// Helper is used to check which validations is for each step
|
|
220
|
+
const paths = this.fvFormRuleSets
|
|
221
|
+
.filter((rule) => rule.step === stepName)
|
|
222
|
+
.map((rule) => rule.path);
|
|
223
|
+
|
|
224
|
+
return this.fvGetPathErrors(paths);
|
|
225
|
+
},
|
|
226
|
+
|
|
209
227
|
updatePaths(value) {
|
|
210
228
|
const { paths, bundles } = value;
|
|
211
229
|
|
|
@@ -283,10 +301,13 @@ export default {
|
|
|
283
301
|
selected,
|
|
284
302
|
publicKey,
|
|
285
303
|
privateKey,
|
|
286
|
-
sshKnownHosts
|
|
304
|
+
sshKnownHosts,
|
|
305
|
+
githubAppId,
|
|
306
|
+
githubAppInstallationId,
|
|
307
|
+
githubAppPrivateKey,
|
|
287
308
|
} = credentials;
|
|
288
309
|
|
|
289
|
-
if ( ![AUTH_TYPE._SSH, AUTH_TYPE._BASIC, AUTH_TYPE._S3].includes(selected) ) {
|
|
310
|
+
if ( ![AUTH_TYPE._SSH, AUTH_TYPE._BASIC, AUTH_TYPE._S3, AUTH_TYPE._GITHUB_APP].includes(selected) ) {
|
|
290
311
|
return;
|
|
291
312
|
}
|
|
292
313
|
|
|
@@ -323,19 +344,31 @@ export default {
|
|
|
323
344
|
publicField = 'username';
|
|
324
345
|
privateField = 'password';
|
|
325
346
|
break;
|
|
347
|
+
case AUTH_TYPE._GITHUB_APP:
|
|
348
|
+
type = SECRET_TYPES.OPAQUE;
|
|
349
|
+
break;
|
|
326
350
|
default:
|
|
327
351
|
throw new Error('Unknown type');
|
|
328
352
|
}
|
|
329
353
|
|
|
330
354
|
secret._type = type;
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
355
|
+
|
|
356
|
+
if (selected === AUTH_TYPE._GITHUB_APP) {
|
|
357
|
+
secret.data = {
|
|
358
|
+
[GITHUB_APP_SECRET_KEYS.APP_ID]: base64Encode(githubAppId),
|
|
359
|
+
[GITHUB_APP_SECRET_KEYS.INSTALLATION_ID]: base64Encode(githubAppInstallationId),
|
|
360
|
+
[GITHUB_APP_SECRET_KEYS.PRIVATE_KEY]: base64Encode(githubAppPrivateKey),
|
|
361
|
+
};
|
|
362
|
+
} else {
|
|
363
|
+
secret.data = {
|
|
364
|
+
[publicField]: base64Encode(publicKey),
|
|
365
|
+
[privateField]: base64Encode(privateKey),
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// Add ssh known hosts
|
|
369
|
+
if (selected === AUTH_TYPE._SSH && sshKnownHosts) {
|
|
370
|
+
secret.data.known_hosts = base64Encode(sshKnownHosts);
|
|
371
|
+
}
|
|
339
372
|
}
|
|
340
373
|
}
|
|
341
374
|
|
|
@@ -413,6 +446,25 @@ export default {
|
|
|
413
446
|
}
|
|
414
447
|
},
|
|
415
448
|
|
|
449
|
+
async beforeNext(activeStep) {
|
|
450
|
+
if (activeStep.name !== 'stepMetadata' || !this.isCreate) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
await this.value.dryRunCreate({
|
|
455
|
+
type: this.value.type,
|
|
456
|
+
metadata: {
|
|
457
|
+
name: this.value.metadata.name,
|
|
458
|
+
namespace: this.value.metadata.namespace,
|
|
459
|
+
},
|
|
460
|
+
spec: {
|
|
461
|
+
repo: 'https://example.com/placeholder',
|
|
462
|
+
branch: 'master',
|
|
463
|
+
paths: [],
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
},
|
|
467
|
+
|
|
416
468
|
durationSeconds(value) {
|
|
417
469
|
return `${ value }s`;
|
|
418
470
|
}
|
|
@@ -432,6 +484,7 @@ export default {
|
|
|
432
484
|
:errors="errors"
|
|
433
485
|
:steps="!isView ? steps : undefined"
|
|
434
486
|
:finish-mode="'finish'"
|
|
487
|
+
:before-next="beforeNext"
|
|
435
488
|
class="wizard"
|
|
436
489
|
@cancel="done"
|
|
437
490
|
@error="e=>errors = e"
|
|
@@ -442,6 +495,7 @@ export default {
|
|
|
442
495
|
:value="value"
|
|
443
496
|
:mode="mode"
|
|
444
497
|
:is-view="isView"
|
|
498
|
+
:name-rules="fvGetAndReportPathRules('metadata.name')"
|
|
445
499
|
@input="$emit('input', $event)"
|
|
446
500
|
/>
|
|
447
501
|
</template>
|
|
@@ -196,7 +196,7 @@ export default {
|
|
|
196
196
|
label: this.t('fleet.helmOp.add.steps.metadata.label'),
|
|
197
197
|
subtext: this.t('fleet.helmOp.add.steps.metadata.subtext'),
|
|
198
198
|
descriptionKey: 'fleet.helmOp.add.steps.metadata.description',
|
|
199
|
-
ready: this.isView || !!this.value.metadata.name,
|
|
199
|
+
ready: this.isView || (!!this.value.metadata.name && this.stepPathErrors('basics').length === 0),
|
|
200
200
|
weight: 1
|
|
201
201
|
},
|
|
202
202
|
{
|
|
@@ -443,6 +443,15 @@ export default {
|
|
|
443
443
|
this.done();
|
|
444
444
|
},
|
|
445
445
|
|
|
446
|
+
stepPathErrors(stepName) {
|
|
447
|
+
// Helper is used to check which validations is for each step
|
|
448
|
+
const paths = this.fvFormRuleSets
|
|
449
|
+
.filter((rule) => rule.step === stepName)
|
|
450
|
+
.map((rule) => rule.path);
|
|
451
|
+
|
|
452
|
+
return this.fvGetPathErrors(paths);
|
|
453
|
+
},
|
|
454
|
+
|
|
446
455
|
onSourceTypeSelect(type) {
|
|
447
456
|
if (this.isSuseAppCollection) {
|
|
448
457
|
return;
|
|
@@ -659,35 +668,49 @@ export default {
|
|
|
659
668
|
},
|
|
660
669
|
|
|
661
670
|
updateValidationRules(sourceType) {
|
|
671
|
+
const nameRule = {
|
|
672
|
+
step: 'basics',
|
|
673
|
+
path: 'metadata.name',
|
|
674
|
+
rules: ['subDomain'],
|
|
675
|
+
translationKey: 'nameNsDescription.name.label'
|
|
676
|
+
};
|
|
677
|
+
|
|
662
678
|
switch (sourceType) {
|
|
663
679
|
case SOURCE_TYPE.REPO:
|
|
664
|
-
this.fvFormRuleSets = [{
|
|
680
|
+
this.fvFormRuleSets = [nameRule, {
|
|
681
|
+
step: 'chart',
|
|
665
682
|
path: 'spec.helm.repo',
|
|
666
683
|
rules: ['urlRepository'],
|
|
667
684
|
}, {
|
|
685
|
+
step: 'chart',
|
|
668
686
|
path: 'spec.helm.chart',
|
|
669
687
|
rules: ['required'],
|
|
670
688
|
}, {
|
|
689
|
+
step: 'chart',
|
|
671
690
|
path: 'spec.helm.version',
|
|
672
691
|
rules: ['semanticVersion'],
|
|
673
692
|
}];
|
|
674
693
|
break;
|
|
675
694
|
case SOURCE_TYPE.OCI:
|
|
676
|
-
this.fvFormRuleSets = [{
|
|
695
|
+
this.fvFormRuleSets = [nameRule, {
|
|
696
|
+
step: 'chart',
|
|
677
697
|
path: 'spec.helm.repo',
|
|
678
698
|
rules: ['ociRegistry'],
|
|
679
699
|
},
|
|
680
700
|
...(this.isSuseAppCollection ? [{
|
|
701
|
+
step: 'chart',
|
|
681
702
|
path: 'spec.helm.chart',
|
|
682
703
|
rules: ['required'],
|
|
683
704
|
}] : []),
|
|
684
705
|
{
|
|
706
|
+
step: 'chart',
|
|
685
707
|
path: 'spec.helm.version',
|
|
686
708
|
rules: this.isSuseAppCollection ? ['required', 'semanticVersion'] : ['semanticVersion'],
|
|
687
709
|
}];
|
|
688
710
|
break;
|
|
689
711
|
case SOURCE_TYPE.TARBALL:
|
|
690
|
-
this.fvFormRuleSets = [{
|
|
712
|
+
this.fvFormRuleSets = [nameRule, {
|
|
713
|
+
step: 'chart',
|
|
691
714
|
path: 'spec.helm.chart',
|
|
692
715
|
rules: ['urlRepository'],
|
|
693
716
|
}];
|
|
@@ -739,6 +762,26 @@ export default {
|
|
|
739
762
|
break;
|
|
740
763
|
}
|
|
741
764
|
},
|
|
765
|
+
|
|
766
|
+
async beforeNext(activeStep) {
|
|
767
|
+
if (activeStep.name !== 'basics' || !this.isCreate) {
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
await this.value.dryRunCreate({
|
|
772
|
+
type: this.value.type,
|
|
773
|
+
metadata: {
|
|
774
|
+
name: this.value.metadata.name,
|
|
775
|
+
namespace: this.value.metadata.namespace,
|
|
776
|
+
},
|
|
777
|
+
spec: {
|
|
778
|
+
helm: {
|
|
779
|
+
chart: 'placeholder',
|
|
780
|
+
repo: 'https://example.com',
|
|
781
|
+
},
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
},
|
|
742
785
|
},
|
|
743
786
|
};
|
|
744
787
|
</script>
|
|
@@ -753,11 +796,12 @@ export default {
|
|
|
753
796
|
:mode="mode"
|
|
754
797
|
:resource="value"
|
|
755
798
|
:subtypes="[]"
|
|
756
|
-
:validation-passed="
|
|
799
|
+
:validation-passed="fvFormIsValid"
|
|
757
800
|
:errors="errors"
|
|
758
801
|
:steps="!isView ? steps : undefined"
|
|
759
802
|
:finish-mode="'finish'"
|
|
760
803
|
:cancel-event="true"
|
|
804
|
+
:before-next="beforeNext"
|
|
761
805
|
class="wizard"
|
|
762
806
|
data-testid="helmop-cru-resource"
|
|
763
807
|
@cancel="onCancel"
|
|
@@ -773,6 +817,7 @@ export default {
|
|
|
773
817
|
:mode="mode"
|
|
774
818
|
:is-view="isView"
|
|
775
819
|
data-testid="helmop-metadata-tab"
|
|
820
|
+
:name-rules="fvGetAndReportPathRules('metadata.name')"
|
|
776
821
|
@update:value="$emit('input', $event)"
|
|
777
822
|
/>
|
|
778
823
|
</template>
|
|
@@ -1049,6 +1094,7 @@ export default {
|
|
|
1049
1094
|
>
|
|
1050
1095
|
<HelmOpAppCoConfigTab
|
|
1051
1096
|
v-bind="appCoConfigProps"
|
|
1097
|
+
:name-rules="fvGetAndReportPathRules('metadata.name')"
|
|
1052
1098
|
data-testid="helmop-appco-edit-config-tab"
|
|
1053
1099
|
v-on="appCoConfigListeners"
|
|
1054
1100
|
/>
|
|
@@ -32,13 +32,14 @@ export default {
|
|
|
32
32
|
const t = this.$store.getters['i18n/t'];
|
|
33
33
|
|
|
34
34
|
return {
|
|
35
|
-
setting:
|
|
36
|
-
description:
|
|
37
|
-
editHelp:
|
|
38
|
-
enumOptions:
|
|
39
|
-
canReset:
|
|
40
|
-
errors:
|
|
41
|
-
fvFormRuleSets:
|
|
35
|
+
setting: ALLOWED_SETTINGS[this.value.id],
|
|
36
|
+
description: t(`advancedSettings.descriptions.${ this.value.id }`),
|
|
37
|
+
editHelp: t(`advancedSettings.editHelp.${ this.value.id }`),
|
|
38
|
+
enumOptions: [],
|
|
39
|
+
canReset: false,
|
|
40
|
+
errors: [],
|
|
41
|
+
fvFormRuleSets: [],
|
|
42
|
+
customEditComponent: null
|
|
42
43
|
};
|
|
43
44
|
},
|
|
44
45
|
|
|
@@ -60,6 +61,14 @@ export default {
|
|
|
60
61
|
if (isServerUrl(this.value.id) && !this.value.default) {
|
|
61
62
|
this.canReset = false;
|
|
62
63
|
}
|
|
64
|
+
|
|
65
|
+
let component;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
component = require(`@shell/edit/management.cattle.io.setting/${ this.value.id }.vue`).default;
|
|
69
|
+
} catch {}
|
|
70
|
+
|
|
71
|
+
this.customEditComponent = component;
|
|
63
72
|
},
|
|
64
73
|
|
|
65
74
|
computed: {
|
|
@@ -195,8 +204,22 @@ export default {
|
|
|
195
204
|
:label="err"
|
|
196
205
|
data-testid="setting-error-banner"
|
|
197
206
|
/>
|
|
198
|
-
|
|
199
|
-
|
|
207
|
+
<div
|
|
208
|
+
v-if="customEditComponent"
|
|
209
|
+
class="mt-20"
|
|
210
|
+
>
|
|
211
|
+
<component
|
|
212
|
+
:is="customEditComponent"
|
|
213
|
+
:setting-value="value.value"
|
|
214
|
+
:default-value="value.default"
|
|
215
|
+
:rules="fvGetAndReportPathRules('value')"
|
|
216
|
+
@update:setting-value="value.value=$event"
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
<div
|
|
220
|
+
v-else
|
|
221
|
+
class="mt-20"
|
|
222
|
+
>
|
|
200
223
|
<div v-if="setting.kind === 'enum'">
|
|
201
224
|
<LabeledSelect
|
|
202
225
|
v-model:value="value.value"
|