@rancher/shell 3.0.7 → 3.0.8-rc.1

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 (83) hide show
  1. package/assets/images/vendor/githubapp.svg +13 -0
  2. package/assets/styles/base/_typography.scss +1 -1
  3. package/assets/styles/themes/_modern.scss +5 -5
  4. package/assets/translations/en-us.yaml +91 -11
  5. package/assets/translations/zh-hans.yaml +0 -4
  6. package/components/Inactivity.vue +222 -106
  7. package/components/InstallHelmCharts.vue +2 -2
  8. package/components/ResourceDetail/index.vue +1 -1
  9. package/components/SortableTable/index.vue +17 -2
  10. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  11. package/components/fleet/FleetSecretSelector.vue +127 -0
  12. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  13. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  14. package/components/form/FileImageSelector.vue +13 -4
  15. package/components/form/FileSelector.vue +11 -2
  16. package/components/form/ResourceLabeledSelect.vue +1 -0
  17. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  18. package/components/nav/Header.vue +1 -0
  19. package/config/product/auth.js +1 -0
  20. package/config/query-params.js +1 -0
  21. package/config/settings.ts +8 -1
  22. package/config/types.js +2 -0
  23. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  24. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  25. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  26. package/edit/auth/github-app-steps.vue +97 -0
  27. package/edit/auth/github-steps.vue +75 -0
  28. package/edit/auth/github.vue +94 -65
  29. package/edit/fleet.cattle.io.helmop.vue +51 -2
  30. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  31. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  32. package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
  33. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  34. package/list/projectsecret.vue +1 -1
  35. package/machine-config/azure.vue +1 -1
  36. package/mixins/chart.js +1 -1
  37. package/models/__tests__/chart.test.ts +17 -9
  38. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  39. package/models/catalog.cattle.io.app.js +1 -1
  40. package/models/chart.js +3 -1
  41. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  42. package/models/management.cattle.io.authconfig.js +1 -0
  43. package/package.json +2 -2
  44. package/pages/auth/login.vue +5 -2
  45. package/pages/auth/verify.vue +1 -1
  46. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  47. package/pages/c/_cluster/apps/charts/chart.vue +2 -2
  48. package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
  49. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  50. package/pages/c/_cluster/settings/performance.vue +12 -25
  51. package/pages/home.vue +313 -12
  52. package/plugins/axios.js +2 -1
  53. package/plugins/dashboard-store/actions.js +1 -1
  54. package/plugins/dashboard-store/resource-class.js +17 -2
  55. package/plugins/steve/steve-pagination-utils.ts +2 -2
  56. package/scripts/extension/publish +1 -1
  57. package/store/auth.js +8 -3
  58. package/store/aws.js +8 -6
  59. package/store/features.js +1 -0
  60. package/store/index.js +9 -3
  61. package/store/prefs.js +6 -0
  62. package/types/kube/kube-api.ts +2 -1
  63. package/types/rancher/index.d.ts +1 -0
  64. package/types/resources/settings.d.ts +29 -7
  65. package/types/shell/index.d.ts +59 -0
  66. package/utils/__tests__/cluster.test.ts +379 -1
  67. package/utils/cluster.js +157 -3
  68. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  69. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  70. package/utils/dynamic-content/__tests__/info.test.ts +263 -0
  71. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  72. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  73. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  74. package/utils/dynamic-content/config.ts +55 -0
  75. package/utils/dynamic-content/index.ts +273 -0
  76. package/utils/dynamic-content/info.ts +219 -0
  77. package/utils/dynamic-content/new-release.ts +126 -0
  78. package/utils/dynamic-content/support-notice.ts +169 -0
  79. package/utils/dynamic-content/types.d.ts +101 -0
  80. package/utils/dynamic-content/util.ts +122 -0
  81. package/utils/inactivity.ts +104 -0
  82. package/utils/pagination-utils.ts +19 -4
  83. package/utils/release-notes.ts +1 -1
@@ -2,18 +2,22 @@ import { mount } from '@vue/test-utils';
2
2
  import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
3
3
  import HelmOp from '@shell/models/fleet.cattle.io.helmop';
4
4
  import HelmOpComponent from '@shell/edit/fleet.cattle.io.helmop.vue';
5
+ import FleetSecretSelector from '@shell/components/fleet/FleetSecretSelector.vue';
6
+ import FleetConfigMapSelector from '@shell/components/fleet/FleetConfigMapSelector.vue';
5
7
 
6
8
  const mockStore = {
7
9
  dispatch: jest.fn(),
8
10
  commit: jest.fn(),
9
11
  getters: {
10
- 'i18n/t': (text: string) => text,
11
- 'i18n/exists': jest.fn(),
12
- t: (text: string) => text,
13
- currentStore: () => 'current_store',
14
- 'current_store/schemaFor': jest.fn(),
15
- 'current_store/all': jest.fn(),
16
- workspace: 'test',
12
+ 'i18n/t': (text: string) => text,
13
+ 'i18n/exists': jest.fn(),
14
+ t: (text: string) => text,
15
+ currentStore: () => 'current_store',
16
+ 'current_store/schemaFor': jest.fn(),
17
+ 'current_store/all': jest.fn(),
18
+ workspace: 'test',
19
+ 'management/paginationEnabled': () => false,
20
+ 'management/all': () => [],
17
21
  },
18
22
  rootGetters: { 'i18n/t': jest.fn() },
19
23
  };
@@ -63,19 +67,22 @@ const mockHelmOp = {
63
67
  currentRoute: () => {},
64
68
  };
65
69
 
66
- const initHelmOp = (props: any, value?: any) => {
67
- const initValue = new HelmOp({
70
+ const initHelmOp = (props: any, options = {}) => {
71
+ const value = new HelmOp({
68
72
  ...mockHelmOp,
69
- ...(value || {})
73
+ ...options
70
74
  }, {
71
75
  getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
72
76
  dispatch: jest.fn(),
73
77
  rootGetters: { 'i18n/t': jest.fn() },
74
78
  });
75
79
 
80
+ value.applyDefaults = () => {};
81
+ value.metadata = { namespace: '' };
82
+
76
83
  return {
77
84
  props: {
78
- value: initValue,
85
+ value,
79
86
  ...props
80
87
  },
81
88
  computed: mockComputed,
@@ -221,4 +228,38 @@ describe.each([
221
228
 
222
229
  expect(pollingIntervalInput.value).toBe(displayValue);
223
230
  });
231
+
232
+ it('should update downstreamResources with new Secrets when FleetSecretSelector emits update event', async() => {
233
+ const wrapper = mount(HelmOpComponent, initHelmOp({ realMode: mode }));
234
+
235
+ const fleetConfigMapSelector = wrapper.findComponent(FleetConfigMapSelector);
236
+ const fleetSecretSelector = wrapper.findComponent(FleetSecretSelector);
237
+
238
+ expect(fleetSecretSelector.exists()).toBe(true);
239
+ expect(fleetConfigMapSelector.exists()).toBe(true);
240
+
241
+ await fleetSecretSelector.vm.$emit('update:value', []);
242
+ await fleetConfigMapSelector.vm.$emit('update:value', []);
243
+
244
+ await fleetSecretSelector.vm.$emit('update:value', ['secret2', 'secret3']);
245
+
246
+ expect(wrapper.vm.value.spec.helm.downstreamResources).toStrictEqual([{ name: 'secret2', kind: 'Secret' }, { name: 'secret3', kind: 'Secret' }]);
247
+ });
248
+
249
+ it('should update downstreamResources with new ConfigMaps when FleetConfigMapSelector emits update event', async() => {
250
+ const wrapper = mount(HelmOpComponent, initHelmOp({ realMode: mode }));
251
+
252
+ const fleetConfigMapSelector = wrapper.findComponent(FleetConfigMapSelector);
253
+ const fleetSecretSelector = wrapper.findComponent(FleetSecretSelector);
254
+
255
+ expect(fleetSecretSelector.exists()).toBe(true);
256
+ expect(fleetConfigMapSelector.exists()).toBe(true);
257
+
258
+ await fleetSecretSelector.vm.$emit('update:value', []);
259
+ await fleetConfigMapSelector.vm.$emit('update:value', []);
260
+
261
+ await fleetConfigMapSelector.vm.$emit('update:value', ['configMap2', 'configMap3']);
262
+
263
+ expect(wrapper.vm.value.spec.helm.downstreamResources).toStrictEqual([{ name: 'configMap2', kind: 'ConfigMap' }, { name: 'configMap3', kind: 'ConfigMap' }]);
264
+ });
224
265
  });
@@ -28,7 +28,20 @@ export default defineComponent({
28
28
  color="warning"
29
29
  data-testid="auth-provider-admin-permissions-warning-banner"
30
30
  >
31
- <span v-clean-html="t('authConfig.associatedWarning', tArgs, true)" />
31
+ <span class="banner-content">
32
+ <span v-clean-html="t('authConfig.associatedWarning', tArgs, true)" />
33
+ <slot name="additional-warning">
34
+ <!--Empty slot content-->
35
+ </slot>
36
+ </span>
32
37
  </Banner>
33
38
  </div>
34
39
  </template>
40
+
41
+ <style lang="scss" scoped>
42
+ .banner-content {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: 1rem;
46
+ }
47
+ </style>
@@ -0,0 +1,97 @@
1
+ <script setup lang="ts">
2
+ import InfoBox from '@shell/components/InfoBox';
3
+ import CopyToClipboard from '@shell/components/CopyToClipboard';
4
+
5
+ type TArgs = {
6
+ baseUrl: string;
7
+ serverUrl: string;
8
+ provider: string;
9
+ username: string;
10
+ }
11
+
12
+ defineProps<{
13
+ name: string;
14
+ tArgs: TArgs;
15
+ }>();
16
+
17
+ </script>
18
+
19
+ <template>
20
+ <InfoBox
21
+ :step="1"
22
+ class="step-box"
23
+ >
24
+ <ul class="step-list">
25
+ <li v-clean-html="t(`authConfig.${name}.form.prefix.1`, tArgs, true)" />
26
+ <li v-clean-html="t(`authConfig.${name}.form.prefix.2`, tArgs, true)" />
27
+ <li v-clean-html="t(`authConfig.${name}.form.prefix.3`, tArgs, true)" />
28
+ <li v-clean-html="t(`authConfig.${name}.form.prefix.4`, tArgs, true)" />
29
+ </ul>
30
+ </InfoBox>
31
+ <InfoBox
32
+ :step="2"
33
+ class="step-box"
34
+ >
35
+ <ul class="step-list">
36
+ <li>
37
+ {{ t(`authConfig.${name}.form.instruction`, tArgs, true) }}
38
+ <ul class="mt-10">
39
+ <li>
40
+ <b>
41
+ {{ t(`authConfig.${name}.form.app.label`) }}
42
+ </b>:
43
+ <span v-clean-html="t(`authConfig.${name}.form.app.value`, tArgs, true)" />
44
+ </li>
45
+ <li>
46
+ <b>
47
+ {{ t(`authConfig.${name}.form.homepage.label`) }}
48
+ </b>: {{ tArgs.serverUrl }}
49
+ <CopyToClipboard
50
+ label-as="tooltip"
51
+ :text="tArgs.serverUrl"
52
+ class="icon-btn"
53
+ action-color="bg-transparent"
54
+ />
55
+ </li>
56
+ <li>
57
+ <b>
58
+ {{ t(`authConfig.${name}.form.description.label`) }}
59
+ </b>:
60
+ <span v-clean-html="t(`authConfig.${name}.form.description.value`, tArgs, true)" />
61
+ </li>
62
+ <li>
63
+ <b>
64
+ {{ t(`authConfig.${name}.form.callback.label`) }}
65
+ </b>:
66
+ {{ t(`authConfig.${name}.form.callback.value`, tArgs, true) }}
67
+ <CopyToClipboard
68
+ :text="t(`authConfig.${name}.form.callback.value`, tArgs, true)"
69
+ label-as="tooltip"
70
+ class="icon-btn"
71
+ action-color="bg-transparent"
72
+ />
73
+ </li>
74
+ </ul>
75
+ </li>
76
+ <li>
77
+ {{ t(`authConfig.${name}.form.create`, tArgs, true) }}
78
+ </li>
79
+ </ul>
80
+ </InfoBox>
81
+ <InfoBox
82
+ :step="3"
83
+ class="mb-20"
84
+ >
85
+ <ul class="step-list">
86
+ <li v-clean-html="t(`authConfig.${name}.form.suffix.1`, tArgs, true)" />
87
+ <li v-clean-html="t(`authConfig.${name}.form.suffix.2`, tArgs, true)" />
88
+ <li v-clean-html="t(`authConfig.${name}.form.suffix.3`, tArgs, true)" />
89
+ </ul>
90
+ </InfoBox>
91
+ </template>
92
+
93
+ <style lang="scss" scoped>
94
+ .step-list li:not(:last-child) {
95
+ margin-bottom: 8px;
96
+ }
97
+ </style>
@@ -0,0 +1,75 @@
1
+ <script setup lang="ts">
2
+ import InfoBox from '@shell/components/InfoBox';
3
+ import CopyToClipboard from '@shell/components/CopyToClipboard';
4
+
5
+ type TArgs = {
6
+ baseUrl: string;
7
+ serverUrl: string;
8
+ provider: string;
9
+ username: string;
10
+ }
11
+
12
+ defineProps<{
13
+ name: string;
14
+ tArgs: TArgs;
15
+ }>();
16
+
17
+ </script>
18
+
19
+ <template>
20
+ <InfoBox
21
+ :step="1"
22
+ class="step-box"
23
+ >
24
+ <ul class="step-list">
25
+ <li v-clean-html="t(`authConfig.${name}.form.prefix.1`, tArgs, true)" />
26
+ <li v-clean-html="t(`authConfig.${name}.form.prefix.2`, tArgs, true)" />
27
+ <li v-clean-html="t(`authConfig.${name}.form.prefix.3`, tArgs, true)" />
28
+ </ul>
29
+ </InfoBox>
30
+ <InfoBox
31
+ :step="2"
32
+ class="step-box"
33
+ >
34
+ <ul class="step-list">
35
+ <li>
36
+ {{ t(`authConfig.${name}.form.instruction`, tArgs, true) }}
37
+ <ul class="mt-10">
38
+ <li><b>{{ t(`authConfig.${name}.form.app.label`) }}</b>: <span v-clean-html="t(`authConfig.${name}.form.app.value`, tArgs, true)" /></li>
39
+ <li>
40
+ <b>{{ t(`authConfig.${name}.form.homepage.label`) }}</b>: {{ tArgs.serverUrl }} <CopyToClipboard
41
+ label-as="tooltip"
42
+ :text="tArgs.serverUrl"
43
+ class="icon-btn"
44
+ action-color="bg-transparent"
45
+ />
46
+ </li>
47
+ <li><b>{{ t(`authConfig.${name}.form.description.label`) }}</b>: <span v-clean-html="t(`authConfig.${name}.form.description.value`, tArgs, true)" /></li>
48
+ <li>
49
+ <b>{{ t(`authConfig.${name}.form.callback.label`) }}</b>: {{ tArgs.serverUrl }} <CopyToClipboard
50
+ :text="tArgs.serverUrl"
51
+ label-as="tooltip"
52
+ class="icon-btn"
53
+ action-color="bg-transparent"
54
+ />
55
+ </li>
56
+ </ul>
57
+ </li>
58
+ </ul>
59
+ </InfoBox>
60
+ <InfoBox
61
+ :step="3"
62
+ class="mb-20"
63
+ >
64
+ <ul class="step-list">
65
+ <li v-clean-html="t(`authConfig.${name}.form.suffix.1`, tArgs, true)" />
66
+ <li v-clean-html="t(`authConfig.${name}.form.suffix.2`, tArgs, true)" />
67
+ </ul>
68
+ </InfoBox>
69
+ </template>
70
+
71
+ <style lang="scss" scoped>
72
+ .step-list li:not(:last-child) {
73
+ margin-bottom: 8px;
74
+ }
75
+ </style>
@@ -4,16 +4,15 @@ import CreateEditView from '@shell/mixins/create-edit-view';
4
4
  import CruResource from '@shell/components/CruResource';
5
5
  import { RadioGroup } from '@components/Form/Radio';
6
6
  import { LabeledInput } from '@components/Form/LabeledInput';
7
- import CopyToClipboard from '@shell/components/CopyToClipboard';
8
7
  import AllowedPrincipals from '@shell/components/auth/AllowedPrincipals';
9
8
  import { MANAGEMENT } from '@shell/config/types';
10
9
  import { findBy } from '@shell/utils/array';
11
10
  import AuthConfig from '@shell/mixins/auth-config';
12
11
  import AuthBanner from '@shell/components/auth/AuthBanner';
13
- import InfoBox from '@shell/components/InfoBox';
14
12
  import AuthProviderWarningBanners from '@shell/edit/auth/AuthProviderWarningBanners';
15
-
16
- const NAME = 'github';
13
+ import FileSelector from '@shell/components/form/FileSelector';
14
+ import GithubSteps from '@shell/edit/auth/github-steps.vue';
15
+ import GithubAppSteps from '@shell/edit/auth/github-app-steps.vue';
17
16
 
18
17
  export default {
19
18
  components: {
@@ -21,11 +20,12 @@ export default {
21
20
  CruResource,
22
21
  RadioGroup,
23
22
  LabeledInput,
24
- CopyToClipboard,
25
23
  AllowedPrincipals,
26
24
  AuthBanner,
27
- InfoBox,
28
- AuthProviderWarningBanners
25
+ AuthProviderWarningBanners,
26
+ FileSelector,
27
+ GithubSteps,
28
+ GithubAppSteps,
29
29
  },
30
30
 
31
31
  mixins: [CreateEditView, AuthConfig],
@@ -60,7 +60,7 @@ export default {
60
60
  },
61
61
 
62
62
  displayName() {
63
- return this.t(`model.authConfig.provider.${ NAME }`);
63
+ return this.t(`model.authConfig.provider.${ this.NAME }`);
64
64
  },
65
65
 
66
66
  tArgs() {
@@ -73,7 +73,7 @@ export default {
73
73
  },
74
74
 
75
75
  NAME() {
76
- return NAME;
76
+ return this.isGithubApp ? 'githubapp' : 'github';
77
77
  },
78
78
 
79
79
  AUTH_CONFIG() {
@@ -86,7 +86,27 @@ export default {
86
86
  githubConfig: this.model,
87
87
  description: 'Enable GitHub',
88
88
  };
89
- }
89
+ },
90
+
91
+ isGithubApp() {
92
+ return this.model?.id === 'githubapp';
93
+ },
94
+
95
+ steps() {
96
+ return this.isGithubApp ? GithubAppSteps : GithubSteps;
97
+ },
98
+
99
+ validationPassed() {
100
+ if (!this.model.clientId || !this.model.clientSecret) {
101
+ return false;
102
+ }
103
+
104
+ if (this.isGithubApp && (!this.model.appId || !this.model.privateKey)) {
105
+ return false;
106
+ }
107
+
108
+ return true;
109
+ },
90
110
 
91
111
  },
92
112
 
@@ -109,6 +129,10 @@ export default {
109
129
  this.model.hostname = match[4] || 'github.com';
110
130
  }
111
131
  },
132
+
133
+ updatePrivateKey(content) {
134
+ this.model.privateKey = content;
135
+ },
112
136
  },
113
137
  };
114
138
  </script>
@@ -122,7 +146,7 @@ export default {
122
146
  :mode="mode"
123
147
  :resource="model"
124
148
  :subtypes="[]"
125
- :validation-passed="true"
149
+ :validation-passed="validationPassed"
126
150
  :finish-button-mode="model.enabled ? 'edit' : 'enable'"
127
151
  :can-yaml="false"
128
152
  :errors="errors"
@@ -139,7 +163,7 @@ export default {
139
163
  >
140
164
  <template #rows>
141
165
  <tr><td>{{ t(`authConfig.${ NAME }.table.server`) }}: </td><td>{{ baseUrl }}</td></tr>
142
- <tr><td>{{ t(`authConfig.${ NAME }.table.clientId`) }}: </td><td>{{ value.clientId }}</td></tr>
166
+ <tr><td>{{ t(`authConfig.${ NAME }.table.clientId`) }}: </td><td>{{ model.clientId }}</td></tr>
143
167
  </template>
144
168
  </AuthBanner>
145
169
 
@@ -156,7 +180,17 @@ export default {
156
180
  <AuthProviderWarningBanners
157
181
  v-if="!model.enabled"
158
182
  :t-args="tArgs"
159
- />
183
+ >
184
+ <template
185
+ v-if="isGithubApp"
186
+ #additional-warning
187
+ >
188
+ <span
189
+ v-clean-html="t(`authConfig.${NAME}.warning`, {}, true)"
190
+ data-testid="github-app-banner"
191
+ />
192
+ </template>
193
+ </AuthProviderWarningBanners>
160
194
 
161
195
  <h3 v-t="`authConfig.${NAME}.target.label`" />
162
196
  <RadioGroup
@@ -182,60 +216,18 @@ export default {
182
216
  </div>
183
217
  </div>
184
218
 
185
- <InfoBox
186
- :step="1"
187
- class="step-box"
188
- >
189
- <ul class="step-list">
190
- <li v-clean-html="t(`authConfig.${NAME}.form.prefix.1`, tArgs, true)" />
191
- <li v-clean-html="t(`authConfig.${NAME}.form.prefix.2`, tArgs, true)" />
192
- <li v-clean-html="t(`authConfig.${NAME}.form.prefix.3`, tArgs, true)" />
193
- </ul>
194
- </InfoBox>
195
- <InfoBox
196
- :step="2"
197
- class="step-box"
198
- >
199
- <ul class="step-list">
200
- <li>
201
- {{ t(`authConfig.${NAME}.form.instruction`, tArgs, true) }}
202
- <ul class="mt-10">
203
- <li><b>{{ t(`authConfig.${NAME}.form.app.label`) }}</b>: <span v-clean-html="t(`authConfig.${NAME}.form.app.value`, tArgs, true)" /></li>
204
- <li>
205
- <b>{{ t(`authConfig.${NAME}.form.homepage.label`) }}</b>: {{ serverUrl }} <CopyToClipboard
206
- label-as="tooltip"
207
- :text="serverUrl"
208
- class="icon-btn"
209
- action-color="bg-transparent"
210
- />
211
- </li>
212
- <li><b>{{ t(`authConfig.${NAME}.form.description.label`) }}</b>: <span v-clean-html="t(`authConfig.${NAME}.form.description.value`, tArgs, true)" /></li>
213
- <li>
214
- <b>{{ t(`authConfig.${NAME}.form.callback.label`) }}</b>: {{ serverUrl }} <CopyToClipboard
215
- :text="serverUrl"
216
- label-as="tooltip"
217
- class="icon-btn"
218
- action-color="bg-transparent"
219
- />
220
- </li>
221
- </ul>
222
- </li>
223
- </ul>
224
- </InfoBox>
225
- <InfoBox
226
- :step="3"
227
- class="mb-20"
228
- >
229
- <ul class="step-list">
230
- <li v-clean-html="t(`authConfig.${NAME}.form.suffix.1`, tArgs, true)" />
231
- <li v-clean-html="t(`authConfig.${NAME}.form.suffix.2`, tArgs, true)" />
232
- </ul>
233
- </InfoBox>
219
+ <component
220
+ :is="steps"
221
+ :t-args="tArgs"
222
+ :name="NAME"
223
+ />
234
224
 
235
225
  <div class="row mb-20">
236
226
  <div class="col span-6">
237
227
  <LabeledInput
238
228
  v-model:value="model.clientId"
229
+ required
230
+ data-testid="client-id"
239
231
  :label="t(`authConfig.${NAME}.clientId.label`)"
240
232
  :mode="mode"
241
233
  />
@@ -243,21 +235,58 @@ export default {
243
235
  <div class="col span-6">
244
236
  <LabeledInput
245
237
  v-model:value="model.clientSecret"
238
+ required
239
+ data-testid="client-secret"
246
240
  type="password"
247
241
  :label="t(`authConfig.${NAME}.clientSecret.label`)"
248
242
  :mode="mode"
249
243
  />
250
244
  </div>
251
245
  </div>
246
+ <template v-if="isGithubApp">
247
+ <div class="row mb-20">
248
+ <div class="col span-6">
249
+ <LabeledInput
250
+ v-model:value="model.appId"
251
+ required
252
+ data-testid="app-id"
253
+ :label="t(`authConfig.${NAME}.githubAppId.label`)"
254
+ :mode="mode"
255
+ />
256
+ </div>
257
+ <div class="col span-6">
258
+ <LabeledInput
259
+ v-model:value="model.installationId"
260
+ data-testid="installation-id"
261
+ :label="t(`authConfig.${NAME}.installationId.label`)"
262
+ :mode="mode"
263
+ />
264
+ </div>
265
+ </div>
266
+ <div class="row mb-20">
267
+ <div class="col span-6">
268
+ <LabeledInput
269
+ v-model:value="model.privateKey"
270
+ required
271
+ data-testid="private-key"
272
+ type="multiline"
273
+ :label="t(`authConfig.${NAME}.privateKey.label`)"
274
+ :mode="mode"
275
+ />
276
+ <FileSelector
277
+ class="btn btn-sm role-secondary mt-10"
278
+ :label="t('generic.readFromFile')"
279
+ @selected="updatePrivateKey"
280
+ />
281
+ </div>
282
+ </div>
283
+ </template>
252
284
  </template>
253
285
  </CruResource>
254
286
  </div>
255
287
  </template>
256
288
 
257
289
  <style lang="scss" scoped>
258
- .step-list li:not(:last-child) {
259
- margin-bottom: 8px;
260
- }
261
290
  .banner {
262
291
  display: block;
263
292
 
@@ -29,6 +29,8 @@ import UnitInput from '@shell/components/form/UnitInput';
29
29
  import FleetClusterTargets from '@shell/components/fleet/FleetClusterTargets/index.vue';
30
30
  import { toSeconds } from '@shell/utils/duration';
31
31
  import FleetValuesFrom from '@shell/components/fleet/FleetValuesFrom.vue';
32
+ import FleetSecretSelector from '@shell/components/fleet/FleetSecretSelector.vue';
33
+ import FleetConfigMapSelector from '@shell/components/fleet/FleetConfigMapSelector.vue';
32
34
 
33
35
  const MINIMUM_POLLING_INTERVAL = 15;
34
36
 
@@ -50,6 +52,8 @@ export default {
50
52
  Checkbox,
51
53
  CruResource,
52
54
  FleetClusterTargets,
55
+ FleetConfigMapSelector,
56
+ FleetSecretSelector,
53
57
  FleetValuesFrom,
54
58
  YamlEditor,
55
59
  LabeledInput,
@@ -230,7 +234,15 @@ export default {
230
234
  }
231
235
 
232
236
  return null;
233
- }
237
+ },
238
+
239
+ downstreamSecretsList() {
240
+ return (this.value.spec.helm.downstreamResources || []).filter((r) => r.kind === 'Secret').map((r) => r.name);
241
+ },
242
+
243
+ downstreamConfigMapsList() {
244
+ return (this.value.spec.helm.downstreamResources || []).filter((r) => r.kind === 'ConfigMap').map((r) => r.name);
245
+ },
234
246
  },
235
247
 
236
248
  watch: {
@@ -429,7 +441,24 @@ export default {
429
441
  }];
430
442
  break;
431
443
  }
432
- }
444
+ },
445
+
446
+ updateDownstreamResources(kind, list) {
447
+ switch (kind) {
448
+ case 'Secret':
449
+ this.value.spec.helm.downstreamResources = [
450
+ ...(this.value.spec.helm.downstreamResources || []).filter((r) => r.kind !== 'Secret'),
451
+ ...(list || []).map((name) => ({ name, kind: 'Secret' })),
452
+ ];
453
+ break;
454
+ case 'ConfigMap':
455
+ this.value.spec.helm.downstreamResources = [
456
+ ...(this.value.spec.helm.downstreamResources || []).filter((r) => r.kind !== 'ConfigMap'),
457
+ ...(list || []).map((name) => ({ name, kind: 'ConfigMap' })),
458
+ ];
459
+ break;
460
+ }
461
+ },
433
462
  },
434
463
  };
435
464
  </script>
@@ -711,6 +740,26 @@ export default {
711
740
 
712
741
  <h2 v-t="'fleet.helmOp.resources.label'" />
713
742
 
743
+ <div class="row mt-20 mb-20">
744
+ <div class="col span-6">
745
+ <FleetSecretSelector
746
+ :value="downstreamSecretsList"
747
+ :namespace="value.metadata.namespace"
748
+ :mode="mode"
749
+ @update:value="updateDownstreamResources('Secret', $event)"
750
+ />
751
+ </div>
752
+ </div>
753
+ <div class="row mt-20 mb-20">
754
+ <div class="col span-6">
755
+ <FleetConfigMapSelector
756
+ :value="downstreamConfigMapsList"
757
+ :namespace="value.metadata.namespace"
758
+ :mode="mode"
759
+ @update:value="updateDownstreamResources('ConfigMap', $event)"
760
+ />
761
+ </div>
762
+ </div>
714
763
  <div class="resource-handling mb-30">
715
764
  <Checkbox
716
765
  v-model:value="correctDriftEnabled"
@@ -149,25 +149,35 @@ export default {
149
149
  },
150
150
  watch: {
151
151
  namespace: {
152
- handler: 'debouncedUpdateMatches',
152
+ handler() {
153
+ this.debouncedUpdateMatches();
154
+ },
153
155
  immediate: true
154
156
  },
155
157
  'value.podSelector': {
156
- handler: 'debouncedUpdateMatches',
158
+ handler() {
159
+ this.debouncedUpdateMatches();
160
+ },
157
161
  immediate: true
158
162
  },
159
163
  'value.namespaceSelector': {
160
- handler: 'debouncedUpdateMatches',
164
+ handler() {
165
+ this.debouncedUpdateMatches();
166
+ },
161
167
  immediate: true
162
168
  },
163
169
  'value.ipBlock.cidr': 'validateCIDR',
164
170
  'value.ipBlock.except': 'validateCIDR',
165
171
  podSelectorExpressions: {
166
- handler: 'debouncedUpdateMatches',
172
+ handler() {
173
+ this.debouncedUpdateMatches();
174
+ },
167
175
  immediate: true
168
176
  },
169
177
  namespaceSelectorExpressions: {
170
- handler: 'debouncedUpdateMatches',
178
+ handler() {
179
+ this.debouncedUpdateMatches();
180
+ },
171
181
  immediate: true
172
182
  }
173
183
  },