@rancher/shell 3.0.2-rc.2 → 3.0.2-rc.4

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 (172) hide show
  1. package/assets/styles/base/_basic.scss +7 -8
  2. package/assets/styles/global/_button.scss +10 -0
  3. package/assets/styles/global/_form.scss +2 -1
  4. package/assets/styles/global/_tooltip.scss +2 -2
  5. package/assets/styles/themes/_dark.scss +15 -3
  6. package/assets/styles/themes/_light.scss +7 -2
  7. package/assets/styles/vendor/vue-select.scss +4 -0
  8. package/assets/translations/en-us.yaml +66 -9
  9. package/assets/translations/zh-hans.yaml +2 -3
  10. package/components/AppModal.vue +50 -0
  11. package/components/BannerGraphic.vue +0 -42
  12. package/components/ButtonMultiAction.vue +1 -1
  13. package/components/Carousel.vue +88 -74
  14. package/components/CommunityLinks.vue +6 -1
  15. package/components/CopyToClipboardText.vue +3 -0
  16. package/components/Dialog.vue +20 -1
  17. package/components/GrowlManager.vue +9 -2
  18. package/components/LocaleSelector.vue +8 -1
  19. package/components/PaginatedResourceTable.vue +4 -7
  20. package/components/ProgressBarMulti.vue +14 -0
  21. package/components/PromptChangePassword.vue +3 -0
  22. package/components/Questions/Reference.vue +57 -28
  23. package/components/ResourceDetail/Masthead.vue +1 -1
  24. package/components/SelectIconGrid.vue +12 -1
  25. package/components/SideNav.vue +12 -38
  26. package/components/SortableTable/index.vue +1 -0
  27. package/components/Tabbed/index.vue +9 -1
  28. package/components/YamlEditor.vue +1 -0
  29. package/components/__tests__/Carousel.test.ts +56 -27
  30. package/components/auth/Principal.vue +5 -3
  31. package/components/fleet/FleetClusters.vue +82 -1
  32. package/components/fleet/FleetRepos.vue +13 -30
  33. package/components/fleet/ForceDirectedTreeChart/index.vue +2 -2
  34. package/components/form/ChangePassword.vue +2 -0
  35. package/components/form/ColorInput.vue +24 -1
  36. package/components/form/FileSelector.vue +2 -0
  37. package/components/form/KeyValue.vue +230 -160
  38. package/components/form/LabeledSelect.vue +2 -2
  39. package/components/form/PlusMinus.vue +14 -2
  40. package/components/form/ResourceLabeledSelect.vue +13 -53
  41. package/components/form/ResourceSelector.vue +1 -0
  42. package/components/form/ResourceTabs/index.vue +79 -36
  43. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +192 -0
  44. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +104 -0
  45. package/components/form/SSHKnownHosts/index.vue +101 -0
  46. package/components/form/SecretSelector.vue +2 -2
  47. package/components/form/Select.vue +1 -1
  48. package/components/form/SelectOrCreateAuthSecret.vue +43 -11
  49. package/components/form/__tests__/KeyValue.test.ts +1 -1
  50. package/components/form/__tests__/SSHKnownHosts.test.ts +59 -0
  51. package/components/formatter/FleetClusterSummaryGraph.vue +2 -2
  52. package/components/formatter/FleetSummaryGraph.vue +6 -7
  53. package/components/formatter/WorkloadHealthScale.vue +7 -0
  54. package/components/nav/Group.vue +30 -4
  55. package/components/nav/Header.vue +82 -114
  56. package/components/nav/HeaderPageActionMenu.vue +27 -131
  57. package/components/nav/NamespaceFilter.vue +1 -1
  58. package/components/nav/Type.vue +15 -0
  59. package/composables/focusTrap.ts +68 -0
  60. package/config/home-links.js +21 -13
  61. package/config/labels-annotations.js +2 -0
  62. package/config/page-actions.js +1 -0
  63. package/config/pagination-table-headers.js +15 -1
  64. package/config/product/explorer.js +7 -17
  65. package/config/table-headers.js +6 -0
  66. package/config/version.js +5 -1
  67. package/core/plugin.ts +41 -1
  68. package/core/plugins.js +125 -72
  69. package/core/types-provisioning.ts +91 -2
  70. package/core/types.ts +55 -0
  71. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +12 -3
  72. package/detail/catalog.cattle.io.app.vue +1 -1
  73. package/detail/fleet.cattle.io.cluster.vue +3 -3
  74. package/detail/namespace.vue +13 -19
  75. package/detail/networking.k8s.io.ingress.vue +13 -53
  76. package/detail/provisioning.cattle.io.cluster.vue +12 -1
  77. package/detail/secret.vue +25 -0
  78. package/detail/workload/index.vue +3 -3
  79. package/dialog/AddCustomBadgeDialog.vue +5 -1
  80. package/edit/auth/ldap/__tests__/config.test.ts +18 -0
  81. package/edit/auth/ldap/config.vue +24 -0
  82. package/edit/auth/saml.vue +8 -6
  83. package/edit/fleet.cattle.io.gitrepo.vue +34 -23
  84. package/edit/logging-flow/index.vue +4 -19
  85. package/edit/networking.k8s.io.ingress/index.vue +18 -65
  86. package/edit/networking.k8s.io.networkpolicy/index.vue +4 -5
  87. package/edit/provisioning.cattle.io.cluster/index.vue +27 -8
  88. package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -115
  89. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +2 -2
  90. package/edit/provisioning.cattle.io.cluster/tabs/networking/ACE.vue +14 -28
  91. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +25 -12
  92. package/edit/secret/index.vue +1 -1
  93. package/edit/secret/ssh.vue +21 -3
  94. package/edit/service.vue +1 -2
  95. package/list/networking.k8s.io.ingress.vue +1 -1
  96. package/list/node.vue +15 -8
  97. package/list/persistentvolume.vue +12 -4
  98. package/list/provisioning.cattle.io.cluster.vue +1 -0
  99. package/list/service.vue +1 -1
  100. package/list/workload.vue +4 -0
  101. package/mixins/chart.js +4 -1
  102. package/models/catalog.cattle.io.app.js +3 -1
  103. package/models/catalog.cattle.io.clusterrepo.js +56 -7
  104. package/models/fleet.cattle.io.bundle.js +0 -11
  105. package/models/fleet.cattle.io.cluster.js +17 -1
  106. package/models/fleet.cattle.io.gitrepo.js +88 -52
  107. package/models/provisioning.cattle.io.cluster.js +36 -1
  108. package/models/secret.js +5 -0
  109. package/models/service.js +1 -0
  110. package/models/workload.js +19 -1
  111. package/package.json +5 -4
  112. package/pages/account/index.vue +4 -0
  113. package/pages/c/_cluster/apps/charts/index.vue +4 -0
  114. package/pages/c/_cluster/explorer/ConfigBadge.vue +4 -2
  115. package/pages/c/_cluster/explorer/index.vue +13 -6
  116. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +3 -3
  117. package/pages/c/_cluster/fleet/index.vue +75 -89
  118. package/pages/c/_cluster/settings/links.vue +2 -2
  119. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +3 -1
  120. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +3 -0
  121. package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +7 -1
  122. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +3 -1
  123. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +10 -7
  124. package/pages/c/_cluster/uiplugins/InstallDialog.vue +7 -0
  125. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +181 -106
  126. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +2 -0
  127. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +9 -1
  128. package/pages/c/_cluster/uiplugins/index.vue +50 -12
  129. package/pages/diagnostic.vue +17 -15
  130. package/pages/home.vue +32 -6
  131. package/plugins/clean-html.js +50 -0
  132. package/plugins/dashboard-store/resource-class.js +4 -0
  133. package/plugins/plugin.js +54 -49
  134. package/plugins/steve/mutations.js +1 -1
  135. package/plugins/steve/steve-class.js +8 -0
  136. package/plugins/steve/steve-pagination-utils.ts +3 -1
  137. package/rancher-components/Accordion/Accordion.vue +4 -4
  138. package/rancher-components/BadgeState/BadgeState.vue +7 -0
  139. package/rancher-components/Card/Card.vue +12 -0
  140. package/rancher-components/Form/Checkbox/Checkbox.vue +9 -2
  141. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  142. package/rancher-components/Form/LabeledInput/LabeledInput.vue +19 -1
  143. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +39 -2
  144. package/rancher-components/RcButton/RcButton.vue +90 -0
  145. package/rancher-components/RcButton/index.ts +2 -0
  146. package/rancher-components/RcButton/types.ts +17 -0
  147. package/rancher-components/RcDropdown/RcDropdown.vue +122 -0
  148. package/rancher-components/RcDropdown/RcDropdownItem.vue +127 -0
  149. package/rancher-components/RcDropdown/RcDropdownSeparator.vue +6 -0
  150. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +42 -0
  151. package/rancher-components/RcDropdown/index.ts +4 -0
  152. package/rancher-components/RcDropdown/types.ts +22 -0
  153. package/rancher-components/RcDropdown/useDropdownCollection.ts +46 -0
  154. package/rancher-components/RcDropdown/useDropdownContext.ts +110 -0
  155. package/scripts/test-plugins-build.sh +2 -0
  156. package/scripts/typegen.sh +2 -0
  157. package/store/catalog.js +1 -1
  158. package/tsconfig.json +2 -1
  159. package/types/components/paginatedResourceTable.ts +25 -0
  160. package/types/components/resourceLabeledSelect.ts +48 -0
  161. package/types/resources/fleet.d.ts +17 -0
  162. package/types/shell/index.d.ts +61 -0
  163. package/utils/auth.js +5 -1
  164. package/utils/cluster.js +106 -0
  165. package/utils/fleet.ts +35 -3
  166. package/utils/ingress.ts +64 -0
  167. package/utils/uiplugins.ts +56 -44
  168. package/utils/validators/cron-schedule.js +7 -2
  169. package/utils/validators/formRules/__tests__/index.test.ts +53 -17
  170. package/utils/validators/formRules/index.ts +20 -5
  171. package/vue.config.js +1 -1
  172. package/components/RelatedWorkloadsTable.vue +0 -50
@@ -1,12 +1,10 @@
1
1
  <script>
2
2
  import { allHash } from '@shell/utils/promise';
3
- import { SECRET, SERVICE } from '@shell/config/types';
4
3
  import CreateEditView from '@shell/mixins/create-edit-view';
5
4
  import Rules from '@shell/edit/networking.k8s.io.ingress/Rules';
6
5
  import ResourceTabs from '@shell/components/form/ResourceTabs';
7
6
  import Tab from '@shell/components/Tabbed/Tab';
8
- import { SECRET_TYPES as TYPES } from '@shell/config/secret';
9
- import { FilterArgs, PaginationParamFilter } from '@shell/types/store/pagination.types';
7
+ import IngressDetailEditHelper from '@shell/utils/ingress';
10
8
 
11
9
  export default {
12
10
  name: 'CRUIngress',
@@ -18,76 +16,38 @@ export default {
18
16
  },
19
17
  mixins: [CreateEditView],
20
18
  async fetch() {
19
+ this.ingressHelper = new IngressDetailEditHelper({
20
+ $store: this.$store,
21
+ namespace: this.value.metadata.namespace
22
+ });
21
23
  const promises = {
22
- services: this.$store.dispatch('cluster/findAll', { type: SERVICE }),
24
+ services: this.ingressHelper.fetchServices(),
25
+ secrets: this.ingressHelper.fetchSecrets(),
23
26
  resourceFields: this.schema.fetchResourceFields(),
24
27
  };
25
28
 
26
- if (this.$store.getters[`cluster/paginationEnabled`](SECRET)) {
27
- const findPageArgs = { // Of type ActionFindPageArgs
28
- namespaced: this.value.metadata.namespace,
29
- pagination: new FilterArgs({
30
- filters: PaginationParamFilter.createSingleField({
31
- field: 'metadata.fields.1',
32
- value: TYPES.TLS
33
- })
34
- }),
35
- };
36
-
37
- promises.filteredSecrets = this.$store.dispatch(`cluster/findPage`, { type: SECRET, opt: findPageArgs });
38
- } else {
39
- promises.secrets = this.$store.dispatch('cluster/findAll', { type: SECRET });
40
- }
41
29
  const hash = await allHash(promises);
42
30
 
43
- this.allServices = hash.services;
44
- this.allSecrets = hash.secrets;
45
- this.filteredSecrets = hash.filteredSecrets;
31
+ this.services = hash.services;
32
+ this.secrets = hash.secrets;
46
33
  },
47
34
  data() {
48
35
  return {
49
- allSecrets: null,
50
- filteredSecrets: null,
51
- allServices: [],
36
+ secrets: [],
37
+ services: [],
52
38
  };
53
39
  },
54
40
  computed: {
55
41
  serviceTargets() {
56
- return this.filterByCurrentResourceNamespace(this.allServices)
57
- .map((service) => ({
58
- label: service.metadata.name,
59
- value: service.metadata.name,
60
- ports: service.spec.ports?.map((p) => p.port)
61
- }));
42
+ return this.ingressHelper.findAndMapServiceTargets(this.services);
62
43
  },
63
44
  firstTabLabel() {
64
45
  return this.isView ? this.t('ingress.rulesAndCertificates.title') : this.t('ingress.rules.title');
65
46
  },
66
47
  certificates() {
67
- let filteredSecrets;
68
-
69
- if (this.filteredSecrets) {
70
- filteredSecrets = this.filteredSecrets;
71
- } else if (this.allSecrets ) {
72
- filteredSecrets = this.filterByCurrentResourceNamespace(this.allSecrets.filter((secret) => secret._type === TYPES.TLS));
73
- } else {
74
- return [];
75
- }
76
-
77
- return filteredSecrets.map((secret) => {
78
- const { id } = secret;
79
-
80
- return id.slice(id.indexOf('/') + 1);
81
- });
48
+ return this.ingressHelper.findAndMapCerts(this.secrets);
82
49
  },
83
50
  },
84
- methods: {
85
- filterByCurrentResourceNamespace(resources) {
86
- return resources.filter((resource) => {
87
- return resource.metadata.namespace === this.value.metadata.namespace;
88
- });
89
- }
90
- }
91
51
  };
92
52
  </script>
93
53
  <template>
@@ -84,6 +84,7 @@ export default {
84
84
  async fetch() {
85
85
  await this.value.waitForProvisioner();
86
86
 
87
+ // Support for the 'provisioner' extension
87
88
  const extClass = this.$plugin.getDynamic('provisioner', this.value.machineProvider);
88
89
 
89
90
  if (extClass) {
@@ -94,6 +95,7 @@ export default {
94
95
  $plugin: this.$store.app.$plugin,
95
96
  $t: this.t
96
97
  });
98
+
97
99
  this.extDetailTabs = {
98
100
  ...this.extDetailTabs,
99
101
  ...this.extProvider.detailTabs
@@ -101,6 +103,15 @@ export default {
101
103
  this.extCustomParams = { provider: this.value.machineProvider };
102
104
  }
103
105
 
106
+ // Support for a model extension
107
+ if (this.value.customProvisionerHelper) {
108
+ this.extDetailTabs = {
109
+ ...this.extDetailTabs,
110
+ ...this.value.customProvisionerHelper.detailTabs
111
+ };
112
+ this.extCustomParams = { provider: this.value.machineProvider };
113
+ }
114
+
104
115
  const schema = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);
105
116
  const fetchOne = { schemaDefinitions: schema.fetchResourceFields() };
106
117
 
@@ -379,7 +390,7 @@ export default {
379
390
  },
380
391
 
381
392
  showNodes() {
382
- return !this.showMachines && this.haveNodes && !!this.nodes.length;
393
+ return !this.showMachines && this.haveNodes && !!this.nodes.length && this.extDetailTabs.machines;
383
394
  },
384
395
 
385
396
  showSnapshots() {
package/detail/secret.vue CHANGED
@@ -122,6 +122,10 @@ export default {
122
122
  return this.value._type === TYPES.SSH;
123
123
  },
124
124
 
125
+ showKnownHosts() {
126
+ return this.isSsh && this.value.supportsSshKnownHosts;
127
+ },
128
+
125
129
  isBasicAuth() {
126
130
  return this.value._type === TYPES.BASIC;
127
131
  },
@@ -142,6 +146,12 @@ export default {
142
146
  return rows;
143
147
  },
144
148
 
149
+ knownHosts() {
150
+ const { data = {} } = this.value;
151
+
152
+ return data.known_hosts ? base64Decode(data.known_hosts) : '';
153
+ },
154
+
145
155
  dataLabel() {
146
156
  switch (this.value._type) {
147
157
  case TYPES.TLS:
@@ -274,6 +284,21 @@ export default {
274
284
  </div>
275
285
  </div>
276
286
  </Tab>
287
+ <Tab
288
+ v-if="showKnownHosts"
289
+ name="known_hosts"
290
+ label-key="secret.ssh.knownHosts"
291
+ >
292
+ <div class="row">
293
+ <div class="col span-12">
294
+ <DetailText
295
+ :value="knownHosts"
296
+ label-key="secret.ssh.knownHosts"
297
+ :conceal="false"
298
+ />
299
+ </div>
300
+ </div>
301
+ </Tab>
277
302
  </ResourceTabs>
278
303
  </template>
279
304
 
@@ -47,8 +47,9 @@ export default {
47
47
  } catch {}
48
48
 
49
49
  const hash = {
50
- // See https://github.com/rancher/dashboard/issues/10417, all pods bad, come from a locally applied selector in the workload model
50
+ // Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
51
51
  allPods: this.$store.dispatch('cluster/findAll', { type: POD }),
52
+ // Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
52
53
  allServices: this.$store.dispatch('cluster/findAll', { type: SERVICE }),
53
54
  allIngresses: this.$store.dispatch('cluster/findAll', { type: INGRESS }),
54
55
  // Nodes should be fetched because they may be referenced in the target
@@ -57,7 +58,7 @@ export default {
57
58
  };
58
59
 
59
60
  if (this.value.type === WORKLOAD_TYPES.CRON_JOB) {
60
- hash.allJobs = this.$store.dispatch('cluster/findAll', { type: WORKLOAD_TYPES.JOB });
61
+ hash.jobs = this.value.matchingJobs();
61
62
  }
62
63
  const res = await allHash(hash);
63
64
 
@@ -91,7 +92,6 @@ export default {
91
92
  allIngresses: [],
92
93
  matchingServices: [],
93
94
  matchingIngresses: [],
94
- allJobs: [],
95
95
  allNodes: [],
96
96
  WORKLOAD_METRICS_DETAIL_URL,
97
97
  WORKLOAD_METRICS_SUMMARY_URL,
@@ -213,6 +213,7 @@ export default {
213
213
  <Card
214
214
  class="prompt-badge"
215
215
  :show-highlight-border="false"
216
+ :trigger-focus-trap="true"
216
217
  >
217
218
  <template #title>
218
219
  <h4 class="text-default-text">
@@ -258,7 +259,6 @@ export default {
258
259
  <Checkbox
259
260
  v-model:value="badgeAsIcon"
260
261
  :label="t('clusterBadge.modal.badgeAsIcon')"
261
-
262
262
  :tooltip="t('clusterBadge.modal.maxCharsTooltip')"
263
263
  />
264
264
 
@@ -312,7 +312,9 @@ export default {
312
312
  />
313
313
  <div class="buttons">
314
314
  <button
315
+ role="button"
315
316
  class="btn role-secondary mr-10"
317
+ :aria-label="t('generic.cancel')"
316
318
  @click="close"
317
319
  >
318
320
  {{ t('generic.cancel') }}
@@ -320,6 +322,8 @@ export default {
320
322
  <AsyncButton
321
323
  :action-label="t('clusterBadge.modal.buttonAction')"
322
324
  :disabled="!canSubmit"
325
+ role="button"
326
+ :aria-label="t('generic.cancel')"
323
327
  @click="apply"
324
328
  />
325
329
  </div>
@@ -0,0 +1,18 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import LDAPConfig from '@shell/edit/auth/ldap/config.vue';
3
+
4
+ describe('lDAP config', () => {
5
+ it.each([
6
+ 'openldap', 'freeipa'
7
+ ])('should display searchUsingServiceAccount checkbox if type %p', (type) => {
8
+ const wrapper = mount(LDAPConfig, {
9
+ propsData: {
10
+ value: {},
11
+ type,
12
+ }
13
+ });
14
+ const checkbox = wrapper.find('[data-testid="searchUsingServiceAccount"]');
15
+
16
+ expect(checkbox).toBeDefined();
17
+ });
18
+ });
@@ -11,6 +11,8 @@ const DEFAULT_TLS_PORT = 636;
11
11
 
12
12
  export const SHIBBOLETH = 'shibboleth';
13
13
  export const OKTA = 'okta';
14
+ export const OPEN_LDAP = 'openldap';
15
+ export const FREE_IPA = 'freeipa';
14
16
 
15
17
  export default {
16
18
  emits: ['update:value'],
@@ -64,6 +66,11 @@ export default {
64
66
  // Does the auth provider support LDAP for search in addition to SAML?
65
67
  isSamlProvider() {
66
68
  return this.type === SHIBBOLETH || this.type === OKTA;
69
+ },
70
+
71
+ // Allow to enable user search just for these providers
72
+ isSearchAllowed() {
73
+ return this.type === OPEN_LDAP || this.type === FREE_IPA;
67
74
  }
68
75
  },
69
76
 
@@ -226,6 +233,23 @@ export default {
226
233
  />
227
234
  </div>
228
235
  </div>
236
+
237
+ <div
238
+ v-if="isSearchAllowed"
239
+ class="row mb-20"
240
+ >
241
+ <div class="col">
242
+ <Checkbox
243
+ v-model:value="model.searchUsingServiceAccount"
244
+ :mode="mode"
245
+ data-testid="searchUsingServiceAccount"
246
+ class="full-height"
247
+ :label="t('authConfig.ldap.searchUsingServiceAccount.label')"
248
+ :tooltip="t('authConfig.ldap.searchUsingServiceAccount.tip')"
249
+ />
250
+ </div>
251
+ </div>
252
+
229
253
  <div class="row mb-20">
230
254
  <div class="col span-6">
231
255
  <LabeledInput
@@ -211,12 +211,14 @@ export default {
211
211
  </Banner>
212
212
 
213
213
  <table v-if="showLdapDetails && model.openLdapConfig">
214
- <tr><td>{{ t('authConfig.ldap.hostname.label') }}:</td><td>{{ ldapHosts }}</td></tr>
215
- <tr><td>{{ t('authConfig.ldap.port') }}:</td><td>{{ model.openLdapConfig.port }}</td></tr>
216
- <tr><td>{{ t('authConfig.ldap.protocol') }}:</td><td>{{ ldapProtocol }}</td></tr>
217
- <tr><td>{{ t('authConfig.ldap.serviceAccountDN') }}:</td><td>{{ model.openLdapConfig.serviceAccountDistinguishedName }}</td></tr>
218
- <tr><td>{{ t('authConfig.ldap.userSearchBase.label') }}:</td><td>{{ model.openLdapConfig.userSearchBase }}</td></tr>
219
- <tr><td>{{ t('authConfig.ldap.groupSearchBase.label') }}:</td><td>{{ model.openLdapConfig.groupSearchBase }}</td></tr>
214
+ <tbody>
215
+ <tr><td>{{ t('authConfig.ldap.hostname.label') }}:</td><td>{{ ldapHosts }}</td></tr>
216
+ <tr><td>{{ t('authConfig.ldap.port') }}:</td><td>{{ model.openLdapConfig.port }}</td></tr>
217
+ <tr><td>{{ t('authConfig.ldap.protocol') }}:</td><td>{{ ldapProtocol }}</td></tr>
218
+ <tr><td>{{ t('authConfig.ldap.serviceAccountDN') }}:</td><td>{{ model.openLdapConfig.serviceAccountDistinguishedName }}</td></tr>
219
+ <tr><td>{{ t('authConfig.ldap.userSearchBase.label') }}:</td><td>{{ model.openLdapConfig.userSearchBase }}</td></tr>
220
+ <tr><td>{{ t('authConfig.ldap.groupSearchBase.label') }}:</td><td>{{ model.openLdapConfig.groupSearchBase }}</td></tr>
221
+ </tbody>
220
222
  </table>
221
223
  </template>
222
224
  </AuthBanner>
@@ -153,7 +153,13 @@ export default {
153
153
  stepRepoInfo,
154
154
  stepTargetInfo,
155
155
  displayHelmRepoURLRegex: false,
156
- fvFormRuleSets: [{ path: 'spec.repo', rules: ['required'] }]
156
+ fvFormRuleSets: [{
157
+ path: 'spec.repo',
158
+ rules: [
159
+ 'required',
160
+ 'gitRepository'
161
+ ],
162
+ }]
157
163
  };
158
164
  },
159
165
 
@@ -402,7 +408,12 @@ export default {
402
408
  },
403
409
 
404
410
  async doCreate(name, credentials) {
405
- const { selected, publicKey, privateKey } = credentials;
411
+ const {
412
+ selected,
413
+ publicKey,
414
+ privateKey,
415
+ sshKnownHosts
416
+ } = credentials;
406
417
 
407
418
  if ( ![AUTH_TYPE._SSH, AUTH_TYPE._BASIC, AUTH_TYPE._S3].includes(selected) ) {
408
419
  return;
@@ -450,6 +461,11 @@ export default {
450
461
  [publicField]: base64Encode(publicKey),
451
462
  [privateField]: base64Encode(privateKey),
452
463
  };
464
+
465
+ // Add ssh known hosts
466
+ if (selected === AUTH_TYPE._SSH && sshKnownHosts) {
467
+ secret.data.known_hosts = base64Encode(sshKnownHosts);
468
+ }
453
469
  }
454
470
 
455
471
  await secret.save();
@@ -540,17 +556,7 @@ export default {
540
556
  @update:value="$emit('input', $event)"
541
557
  />
542
558
 
543
- <div class="row">
544
- <div class="col span-6">
545
- <Banner
546
- color="info col span-6"
547
- >
548
- <div>
549
- {{ t('fleet.gitRepo.repo.protocolBanner') }}
550
- </div>
551
- </Banner>
552
- </div>
553
- </div>
559
+ <h2 v-t="'fleet.gitRepo.repo.title'" />
554
560
  <div
555
561
  class="row"
556
562
  :class="{'mt-20': isView}"
@@ -580,6 +586,10 @@ export default {
580
586
  />
581
587
  </div>
582
588
  </div>
589
+
590
+ <div class="spacer" />
591
+ <h2 v-t="'fleet.gitRepo.auth.title'" />
592
+
583
593
  <SelectOrCreateAuthSecret
584
594
  :value="value.spec.clientSecretName"
585
595
  :register-before-hook="registerBeforeHook"
@@ -627,8 +637,7 @@ export default {
627
637
  </div>
628
638
 
629
639
  <template v-if="isTls">
630
- <div class="spacer" />
631
- <div class="row">
640
+ <div class="row mt-20">
632
641
  <div class="col span-6">
633
642
  <LabeledSelect
634
643
  :label="t('fleet.gitRepo.tls.label')"
@@ -657,7 +666,7 @@ export default {
657
666
  <div class="resource-handling">
658
667
  <Checkbox
659
668
  v-model:value="correctDriftEnabled"
660
- :tooltip="t('fleet.gitRepo.resources.correctDriftBanner')"
669
+ :tooltip="t('fleet.gitRepo.resources.correctDriftTooltip')"
661
670
  data-testid="GitRepo-correctDrift-checkbox"
662
671
  class="check"
663
672
  type="checkbox"
@@ -666,7 +675,7 @@ export default {
666
675
  />
667
676
  <Checkbox
668
677
  v-model:value="value.spec.keepResources"
669
- :tooltip="t('fleet.gitRepo.resources.keepResourcesBanner')"
678
+ :tooltip="t('fleet.gitRepo.resources.keepResourcesTooltip')"
670
679
  data-testid="GitRepo-keepResources-checkbox"
671
680
  class="check"
672
681
  type="checkbox"
@@ -675,19 +684,16 @@ export default {
675
684
  />
676
685
  </div>
677
686
  <div class="spacer" />
678
- <h2 v-t="'fleet.gitRepo.paths.label'" />
679
687
  <ArrayList
680
688
  v-model:value="value.spec.paths"
681
689
  data-testid="gitRepo-paths"
690
+ :title="t('fleet.gitRepo.paths.label')"
682
691
  :mode="mode"
683
692
  :initial-empty-row="false"
684
693
  :value-placeholder="t('fleet.gitRepo.paths.placeholder')"
685
694
  :add-label="t('fleet.gitRepo.paths.addLabel')"
686
- >
687
- <template #empty>
688
- <Banner label-key="fleet.gitRepo.paths.empty" />
689
- </template>
690
- </ArrayList>
695
+ :protip="t('fleet.gitRepo.paths.empty')"
696
+ />
691
697
  </template>
692
698
  <template #stepTargetInfo>
693
699
  <h2 v-t="isLocal ? 'fleet.gitRepo.target.labelLocal' : 'fleet.gitRepo.target.label'" />
@@ -765,6 +771,11 @@ export default {
765
771
  </template>
766
772
 
767
773
  <style lang="scss" scoped>
774
+ :deep() .select-or-create-auth-secret {
775
+ .row {
776
+ margin-top: 10px !important;
777
+ }
778
+ }
768
779
  .resource-handling {
769
780
  display: flex;
770
781
  flex-direction: column;
@@ -6,14 +6,12 @@ import Loading from '@shell/components/Loading';
6
6
  import NameNsDescription from '@shell/components/form/NameNsDescription';
7
7
  import Tabbed from '@shell/components/Tabbed';
8
8
  import Tab from '@shell/components/Tabbed/Tab';
9
- import {
10
- LOGGING, NAMESPACE, NODE, POD, SCHEMA
11
- } from '@shell/config/types';
9
+ import { LOGGING, NAMESPACE, NODE, SCHEMA } from '@shell/config/types';
12
10
  import jsyaml from 'js-yaml';
13
11
  import { createYaml } from '@shell/utils/create-yaml';
14
12
  import YamlEditor, { EDITOR_MODES } from '@shell/components/YamlEditor';
15
13
  import { allHash } from '@shell/utils/promise';
16
- import { isArray, uniq } from '@shell/utils/array';
14
+ import { isArray } from '@shell/utils/array';
17
15
  import { matchRuleIsPopulated } from '@shell/models/logging.banzaicloud.io.flow';
18
16
  import LabeledSelect from '@shell/components/form/LabeledSelect';
19
17
  import { clone } from '@shell/utils/object';
@@ -67,7 +65,6 @@ export default {
67
65
  const hasAccessToOutputs = this.$store.getters[`${ inStore }/schemaFor`](LOGGING.OUTPUT);
68
66
  const hasAccessToNamespaces = this.$store.getters[`cluster/schemaFor`](NAMESPACE);
69
67
  const hasAccessToNodes = this.$store.getters[`${ inStore }/schemaFor`](NODE);
70
- const hasAccessToPods = this.$store.getters[`${ inStore }/schemaFor`](POD);
71
68
  const isFlow = this.value.type === LOGGING.FLOW;
72
69
 
73
70
  const getAllOrDefault = (type, hasAccess) => {
@@ -77,9 +74,10 @@ export default {
77
74
  const hash = await allHash({
78
75
  allOutputs: getAllOrDefault(LOGGING.OUTPUT, isFlow && hasAccessToOutputs),
79
76
  allClusterOutputs: getAllOrDefault(LOGGING.CLUSTER_OUTPUT, hasAccessToClusterOutputs),
77
+ // Can't remove allNamespaces yet given https://github.com/harvester/harvester/issues/7342 and
78
+ // https://github.com/harvester/harvester-ui-extension/blob/main/pkg/harvester/edit/harvesterhci.io.logging.clusteroutput.vue
80
79
  allNamespaces: getAllOrDefault(NAMESPACE, hasAccessToNamespaces),
81
80
  allNodes: getAllOrDefault(NODE, hasAccessToNodes),
82
- allPods: getAllOrDefault(POD, hasAccessToPods),
83
81
  });
84
82
 
85
83
  for ( const k of Object.keys(hash) ) {
@@ -134,7 +132,6 @@ export default {
134
132
  allClusterOutputs: null,
135
133
  allNamespaces: null,
136
134
  allNodes: null,
137
- allPods: null,
138
135
  filtersYaml,
139
136
  initialFiltersYaml: filtersYaml,
140
137
  globalOutputRefs,
@@ -239,18 +236,6 @@ export default {
239
236
  return out;
240
237
  },
241
238
 
242
- containerChoices() {
243
- const out = [];
244
-
245
- for ( const pod of this.allPods ) {
246
- for ( const c of (pod.spec?.containers || []) ) {
247
- out.push(c.name);
248
- }
249
- }
250
-
251
- return uniq(out).sort();
252
- },
253
-
254
239
  isHarvester() {
255
240
  return this.$store.getters['currentProduct'].inStore === VIRTUAL;
256
241
  },
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import { allHash } from '@shell/utils/promise';
3
- import { SECRET, SERVICE, INGRESS_CLASS } from '@shell/config/types';
3
+ import { INGRESS_CLASS } from '@shell/config/types';
4
4
  import NameNsDescription from '@shell/components/form/NameNsDescription';
5
5
  import CreateEditView from '@shell/mixins/create-edit-view';
6
6
  import FormValidation from '@shell/mixins/form-validation';
@@ -10,13 +10,12 @@ import Labels from '@shell/components/form/Labels';
10
10
  import Error from '@shell/components/form/Error';
11
11
  import Tabbed from '@shell/components/Tabbed';
12
12
  import { get, set } from '@shell/utils/object';
13
- import { SECRET_TYPES as TYPES } from '@shell/config/secret';
14
13
  import DefaultBackend from './DefaultBackend';
15
14
  import Certificates from './Certificates';
16
15
  import Rules from './Rules';
17
16
  import IngressClass from './IngressClass';
18
17
  import Loading from '@shell/components/Loading';
19
- import { FilterArgs, PaginationParamFilter } from '@shell/types/store/pagination.types';
18
+ import IngressDetailEditHelper from '@shell/utils/ingress';
20
19
 
21
20
  export default {
22
21
  name: 'CRUIngress',
@@ -50,36 +49,32 @@ export default {
50
49
  },
51
50
 
52
51
  async fetch() {
52
+ this.ingressHelper = new IngressDetailEditHelper({
53
+ $store: this.$store,
54
+ namespace: this.value.metadata.namespace
55
+ });
56
+
53
57
  this.ingressClassSchema = this.$store.getters[`cluster/schemaFor`](INGRESS_CLASS);
54
58
 
55
59
  const promises = {
56
- services: this.$store.dispatch('cluster/findAll', { type: SERVICE }),
60
+ secrets: this.ingressHelper.fetchSecrets(),
61
+ services: this.ingressHelper.fetchServices(),
57
62
  ingressClasses: this.ingressClassSchema ? this.$store.dispatch('cluster/findAll', { type: INGRESS_CLASS }) : Promise.resolve([]),
58
63
  ingressResourceFields: this.schema.fetchResourceFields(),
59
64
  };
60
65
 
61
- this.filterByApi = this.$store.getters[`cluster/paginationEnabled`](SECRET);
62
-
63
- if (this.filterByApi) {
64
- promises.filteredSecrets = this.filterSecretsByApi();
65
- } else {
66
- promises.secrets = this.$store.dispatch('cluster/findAll', { type: SECRET });
67
- }
68
-
69
66
  const hash = await allHash(promises);
70
67
 
71
- this.allServices = hash.services;
72
- this.allSecrets = hash.secrets;
73
- this.filteredSecrets = hash.filteredSecrets;
68
+ this.secrets = hash.secrets;
69
+ this.services = hash.services;
74
70
  this.allIngressClasses = hash.ingressClasses;
75
71
  },
76
72
  data() {
77
73
  return {
78
74
  filterByApi: null,
79
75
  ingressClassSchema: null,
80
- allSecrets: null,
81
- filteredSecrets: null,
82
- allServices: [],
76
+ secrets: [],
77
+ services: [],
83
78
  allIngressClasses: [],
84
79
  fvFormRuleSets: [
85
80
  {
@@ -111,10 +106,9 @@ export default {
111
106
  },
112
107
 
113
108
  watch: {
114
- async 'value.metadata.namespace'() {
115
- if (this.filterByApi) {
116
- this.filteredSecrets = await this.filterSecretsByApi();
117
- }
109
+ async 'value.metadata.namespace'(neu) {
110
+ this.services = await this.ingressHelper.fetchServices({ namespace: neu });
111
+ this.secrets = await this.ingressHelper.fetchSecrets({ namespace: neu });
118
112
  }
119
113
  },
120
114
 
@@ -162,32 +156,13 @@ export default {
162
156
  return { name: [], port: [] };
163
157
  },
164
158
  serviceTargets() {
165
- return this.filterByCurrentResourceNamespace(this.allServices)
166
- .map((service) => ({
167
- label: service.metadata.name,
168
- value: service.metadata.name,
169
- ports: service.spec.ports?.map((p) => p.port)
170
- }));
159
+ return this.ingressHelper.findAndMapServiceTargets(this.services);
171
160
  },
172
161
  firstTabLabel() {
173
162
  return this.isView ? this.t('ingress.rulesAndCertificates.title') : this.t('ingress.rules.title');
174
163
  },
175
164
  certificates() {
176
- let filteredSecrets;
177
-
178
- if (this.filteredSecrets) {
179
- filteredSecrets = this.filteredSecrets;
180
- } else if (this.allSecrets ) {
181
- filteredSecrets = this.filterByCurrentResourceNamespace(this.allSecrets.filter((secret) => secret._type === TYPES.TLS));
182
- } else {
183
- return [];
184
- }
185
-
186
- return filteredSecrets.map((secret) => {
187
- const { id } = secret;
188
-
189
- return id.slice(id.indexOf('/') + 1);
190
- });
165
+ return this.ingressHelper.findAndMapCerts(this.secrets);
191
166
  },
192
167
  ingressClasses() {
193
168
  return this.allIngressClasses.map((ingressClass) => ({
@@ -210,28 +185,6 @@ export default {
210
185
  },
211
186
 
212
187
  methods: {
213
- filterSecretsByApi() {
214
- const findPageArgs = { // Of type ActionFindPageArgs
215
- namespaced: this.value.metadata.namespace,
216
- pagination: new FilterArgs({
217
- filters: PaginationParamFilter.createSingleField({
218
- field: 'metadata.fields.1',
219
- value: TYPES.TLS
220
- })
221
- }),
222
- };
223
-
224
- return this.$store.dispatch(`cluster/findPage`, { type: SECRET, opt: findPageArgs });
225
- },
226
-
227
- filterByCurrentResourceNamespace(resources) {
228
- // When configuring an Ingress, the options for Secrets and
229
- // default backend Services are limited to the namespace of the Ingress.
230
- return resources.filter((resource) => {
231
- return resource.metadata.namespace === this.value.metadata.namespace;
232
- });
233
- },
234
-
235
188
  willSave() {
236
189
  const backend = get(this.value.spec, this.value.defaultBackendPath);
237
190
  const serviceName = get(backend, this.value.serviceNamePath);