@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,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
  });
@@ -1,12 +1,17 @@
1
1
  <script lang="ts">
2
2
  import { defineComponent } from 'vue';
3
3
  import { Banner } from '@components/Banner';
4
+ import { HIDE_LOCAL_AUTH_PROVIDER } from '@shell/store/features';
4
5
 
5
6
  export default defineComponent({
6
7
  name: 'AuthProviderWarningBanners',
7
8
 
8
9
  components: { Banner },
9
10
 
11
+ data() {
12
+ return { disableLocalAuth: this.$store.getters['features/get'](HIDE_LOCAL_AUTH_PROVIDER) };
13
+ },
14
+
10
15
  props: {
11
16
  tArgs: {
12
17
  type: Object,
@@ -24,6 +29,13 @@ export default defineComponent({
24
29
  color="warning"
25
30
  data-testid="auth-provider-disabled-warning-banner"
26
31
  />
32
+ <Banner
33
+ v-if="disableLocalAuth"
34
+ color="warning"
35
+ data-testid="auth-provider-disable-local-auth-banner"
36
+ >
37
+ <span v-clean-html="t('authConfig.bannerEnableAuthProvider', {}, true)" />
38
+ </Banner>
27
39
  <Banner
28
40
  color="warning"
29
41
  data-testid="auth-provider-admin-permissions-warning-banner"
@@ -4,7 +4,16 @@ import { mount } from '@vue/test-utils';
4
4
  describe('component: AuthProviderWarningBanners.vue', () => {
5
5
  const wrapper = mount(AuthProviderWarningBanners, {
6
6
  props: { tArgs: { provider: 'Any Provider', username: 'username' } },
7
- global: { mocks: { $store: { getters: { 'i18n/t': (text: string) => text } } } }
7
+ global: {
8
+ mocks: {
9
+ $store: {
10
+ getters: {
11
+ 'i18n/t': (text: string) => text,
12
+ 'features/get': () => false,
13
+ }
14
+ }
15
+ }
16
+ }
8
17
  });
9
18
 
10
19
  it('should render properly', () => {
@@ -52,6 +52,7 @@ const requiredSetup = (modelOverrides = {}) => ({
52
52
  'current_store/all': jest.fn(),
53
53
  'i18n/t': (val: string) => val,
54
54
  'i18n/exists': jest.fn(),
55
+ 'features/get': () => false,
55
56
  },
56
57
  dispatch: jest.fn()
57
58
  },
@@ -56,6 +56,7 @@ const requiredSetup = (modelOverrides = {}, dataOverrides = {}) => ({
56
56
  'current_store/all': jest.fn(),
57
57
  'i18n/t': (val: string) => val,
58
58
  'i18n/exists': jest.fn(),
59
+ 'features/get': () => false,
59
60
  },
60
61
  dispatch: jest.fn(),
61
62
  },
@@ -69,6 +69,7 @@ describe('oidc.vue', () => {
69
69
  'current_store/all': jest.fn(),
70
70
  'i18n/t': (val: string) => val,
71
71
  'i18n/exists': jest.fn(),
72
+ 'features/get': () => false,
72
73
  },
73
74
  dispatch: jest.fn()
74
75
  },
@@ -63,6 +63,7 @@ const mountOptions = (model: object) => ({
63
63
  'current_store/all': jest.fn(),
64
64
  'i18n/t': (val: string) => val,
65
65
  'i18n/exists': jest.fn(),
66
+ 'features/get': () => false,
66
67
  },
67
68
  dispatch: jest.fn(),
68
69
  },
@@ -59,6 +59,7 @@ const buildSetup = (type = 'openldap', modelOverride = {}, localDataOverride = {
59
59
  'current_store/all': jest.fn(),
60
60
  'i18n/t': (val: string) => val,
61
61
  'i18n/exists': jest.fn(),
62
+ 'features/get': () => false,
62
63
  },
63
64
  dispatch: jest.fn()
64
65
  },
@@ -103,48 +103,46 @@ export default {
103
103
  </script>
104
104
 
105
105
  <template>
106
- <div>
107
- <CruResource
108
- :validation-passed="!!name && !!value.spec.benchmarkVersion"
109
- :done-route="doneRoute"
110
- :resource="value"
111
- :mode="mode"
112
- :errors="errors"
113
- @finish="save"
114
- @error="e=>errors = e"
115
- >
116
- <div class="spacer" />
117
- <div class="row">
118
- <div class="col span-12">
119
- <NameNsDescription
120
- :mode="mode"
121
- :value="value"
122
- :namespaced="false"
123
- @change="name=value.metadata.name"
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
- <div class="row">
128
- <div class="col span-6">
129
- <LabeledSelect
130
- v-model:value="value.spec.benchmarkVersion"
131
- :mode="mode"
132
- :label="t('compliance.benchmarkVersion')"
133
- :options="compatibleBenchmarkNames"
134
- />
135
- </div>
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
- <div class="spacer" />
138
- <div class="row">
139
- <div class="col span-6">
140
- <ArrayList
141
- v-model:value="value.spec.skipTests"
142
- :title="t('compliance.testsToSkip')"
143
- :value-placeholder="t('compliance.testID')"
144
- :mode="mode"
145
- />
146
- </div>
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
- </CruResource>
149
- </div>
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
- path: 'spec.repo',
102
- rules: ['urlRepository'],
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
- secret.data = {
332
- [publicField]: base64Encode(publicKey),
333
- [privateField]: base64Encode(privateKey),
334
- };
335
-
336
- // Add ssh known hosts
337
- if (selected === AUTH_TYPE._SSH && sshKnownHosts) {
338
- secret.data.known_hosts = base64Encode(sshKnownHosts);
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>