@rancher/shell 3.0.5-rc.3 → 3.0.5-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.
Files changed (200) hide show
  1. package/assets/images/icons/document.svg +3 -0
  2. package/assets/images/vendor/cognito.svg +1 -0
  3. package/assets/styles/app.scss +1 -0
  4. package/assets/styles/base/_basic.scss +10 -0
  5. package/assets/styles/base/_spacing.scss +29 -0
  6. package/assets/styles/global/_layout.scss +1 -1
  7. package/assets/styles/themes/_dark.scss +25 -0
  8. package/assets/styles/themes/_light.scss +65 -0
  9. package/assets/translations/en-us.yaml +322 -24
  10. package/assets/translations/zh-hans.yaml +8 -5
  11. package/components/Certificates.vue +5 -0
  12. package/components/FilterPanel.vue +156 -0
  13. package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
  14. package/components/IconOrSvg.vue +14 -35
  15. package/components/PromptRemove.vue +5 -1
  16. package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
  17. package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
  18. package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
  19. package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
  20. package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
  21. package/components/Resource/Detail/Card/Scaler.vue +89 -0
  22. package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
  23. package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
  24. package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
  25. package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
  26. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
  27. package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
  28. package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
  29. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
  30. package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
  31. package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
  32. package/components/Resource/Detail/Card/index.vue +56 -0
  33. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
  34. package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
  35. package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
  36. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
  37. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
  38. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
  39. package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
  40. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
  41. package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
  42. package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
  43. package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
  44. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
  45. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
  46. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
  47. package/components/Resource/Detail/Metadata/composables.ts +29 -0
  48. package/components/Resource/Detail/Metadata/index.vue +66 -0
  49. package/components/Resource/Detail/Page.vue +22 -0
  50. package/components/Resource/Detail/PercentageBar.vue +40 -0
  51. package/components/Resource/Detail/ResourceRow.vue +119 -0
  52. package/components/Resource/Detail/SpacedRow.vue +14 -0
  53. package/components/Resource/Detail/StatusBar.vue +59 -0
  54. package/components/Resource/Detail/StatusRow.vue +61 -0
  55. package/components/Resource/Detail/TitleBar/Title.vue +13 -0
  56. package/components/Resource/Detail/TitleBar/Top.vue +14 -0
  57. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
  58. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
  59. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
  60. package/components/Resource/Detail/TitleBar/composable.ts +31 -0
  61. package/components/Resource/Detail/TitleBar/index.vue +124 -0
  62. package/components/Resource/Detail/Top/index.vue +34 -0
  63. package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
  64. package/components/ResourceDetail/__tests__/index.test.ts +114 -0
  65. package/components/ResourceDetail/index.vue +64 -562
  66. package/components/ResourceDetail/legacy.vue +545 -0
  67. package/components/ResourceTable.vue +41 -7
  68. package/components/SlideInPanelManager.vue +76 -8
  69. package/components/SortableTable/index.vue +13 -2
  70. package/components/SortableTable/selection.js +21 -8
  71. package/components/StatusBadge.vue +6 -4
  72. package/components/SubtleLink.vue +25 -0
  73. package/components/Wizard.vue +12 -1
  74. package/components/YamlEditor.vue +1 -1
  75. package/components/__tests__/FilterPanel.test.ts +81 -0
  76. package/components/auth/AuthBanner.vue +2 -3
  77. package/components/auth/RoleDetailEdit.vue +45 -3
  78. package/components/auth/login/oidc.vue +6 -1
  79. package/components/fleet/FleetApplications.vue +181 -0
  80. package/components/fleet/FleetHelmOps.vue +115 -0
  81. package/components/fleet/FleetIntro.vue +58 -28
  82. package/components/fleet/FleetNoWorkspaces.vue +5 -1
  83. package/components/fleet/FleetOCIStorageSecret.vue +171 -0
  84. package/components/fleet/FleetRepos.vue +38 -76
  85. package/components/fleet/FleetResources.vue +50 -22
  86. package/components/fleet/FleetSummary.vue +26 -51
  87. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
  88. package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
  89. package/components/fleet/dashboard/Empty.vue +73 -0
  90. package/components/fleet/dashboard/ResourceCard.vue +183 -0
  91. package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
  92. package/components/fleet/dashboard/ResourceDetails.vue +196 -0
  93. package/components/fleet/dashboard/ResourcePanel.vue +376 -0
  94. package/components/form/ArrayList.vue +6 -0
  95. package/components/form/SimpleSecretSelector.vue +8 -2
  96. package/components/form/ValueFromResource.vue +31 -19
  97. package/components/formatter/FleetApplicationClustersReady.vue +77 -0
  98. package/components/formatter/FleetApplicationSource.vue +71 -0
  99. package/components/formatter/FleetSummaryGraph.vue +7 -0
  100. package/components/nav/Header.vue +8 -7
  101. package/components/nav/TopLevelMenu.helper.ts +55 -34
  102. package/components/nav/TopLevelMenu.vue +11 -0
  103. package/components/nav/Type.vue +4 -1
  104. package/composables/useI18n.ts +12 -11
  105. package/config/labels-annotations.js +14 -11
  106. package/config/product/auth.js +1 -0
  107. package/config/product/fleet.js +70 -17
  108. package/config/query-params.js +3 -1
  109. package/config/roles.ts +1 -0
  110. package/config/router/routes.js +20 -2
  111. package/config/secret.ts +15 -0
  112. package/config/settings.ts +3 -2
  113. package/config/table-headers.js +52 -22
  114. package/config/types.js +2 -0
  115. package/core/plugin-helpers.ts +3 -2
  116. package/detail/fleet.cattle.io.cluster.vue +28 -15
  117. package/detail/fleet.cattle.io.gitrepo.vue +10 -1
  118. package/detail/fleet.cattle.io.helmop.vue +157 -0
  119. package/dialog/HelmOpForceUpdateDialog.vue +132 -0
  120. package/dialog/RedeployWorkloadDialog.vue +164 -0
  121. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
  122. package/edit/auth/oidc.vue +159 -93
  123. package/edit/fleet.cattle.io.gitrepo.vue +26 -33
  124. package/edit/fleet.cattle.io.helmop.vue +997 -0
  125. package/edit/management.cattle.io.fleetworkspace.vue +43 -10
  126. package/list/fleet.cattle.io.gitrepo.vue +1 -1
  127. package/list/fleet.cattle.io.helmop.vue +108 -0
  128. package/list/namespace.vue +5 -2
  129. package/mixins/auth-config.js +8 -1
  130. package/mixins/preset.js +100 -0
  131. package/mixins/resource-fetch-api-pagination.js +2 -0
  132. package/mixins/resource-fetch.js +1 -1
  133. package/mixins/resource-table-watch.js +45 -0
  134. package/models/__tests__/chart.test.ts +273 -0
  135. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  136. package/models/chart.js +144 -2
  137. package/models/fleet-application.js +385 -0
  138. package/models/fleet.cattle.io.bundle.js +9 -8
  139. package/models/fleet.cattle.io.gitrepo.js +41 -365
  140. package/models/fleet.cattle.io.helmop.js +228 -0
  141. package/models/management.cattle.io.authconfig.js +1 -0
  142. package/models/management.cattle.io.fleetworkspace.js +12 -0
  143. package/models/workload.js +14 -18
  144. package/package.json +2 -1
  145. package/pages/auth/verify.vue +13 -1
  146. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
  147. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
  148. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
  149. package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
  150. package/pages/c/_cluster/apps/charts/index.vue +302 -484
  151. package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
  152. package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
  153. package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
  154. package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
  155. package/pages/c/_cluster/fleet/application/create.vue +340 -0
  156. package/pages/c/_cluster/fleet/application/index.vue +139 -0
  157. package/pages/c/_cluster/fleet/graph/config.js +277 -0
  158. package/pages/c/_cluster/fleet/index.vue +772 -330
  159. package/pages/explorer/resource/detail/configmap.vue +19 -0
  160. package/plugins/dashboard-store/actions.js +31 -9
  161. package/plugins/dashboard-store/getters.js +34 -21
  162. package/plugins/dashboard-store/mutations.js +51 -7
  163. package/plugins/dashboard-store/resource-class.js +14 -2
  164. package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
  165. package/plugins/steve/actions.js +3 -0
  166. package/plugins/steve/steve-pagination-utils.ts +14 -13
  167. package/plugins/steve/subscribe.js +229 -42
  168. package/rancher-components/BadgeState/BadgeState.vue +3 -1
  169. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
  170. package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
  171. package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
  172. package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
  173. package/rancher-components/RcItemCard/index.ts +2 -0
  174. package/store/auth.js +1 -0
  175. package/store/catalog.js +62 -24
  176. package/store/index.js +33 -14
  177. package/store/slideInPanel.ts +6 -0
  178. package/store/type-map.js +1 -0
  179. package/types/fleet.d.ts +35 -0
  180. package/types/resources/settings.d.ts +19 -1
  181. package/types/shell/index.d.ts +339 -272
  182. package/types/store/dashboard-store.types.ts +17 -3
  183. package/types/store/pagination.types.ts +6 -1
  184. package/types/store/subscribe.types.ts +50 -0
  185. package/utils/auth.js +32 -3
  186. package/utils/fleet-types.ts +0 -0
  187. package/utils/fleet.ts +200 -1
  188. package/utils/pagination-utils.ts +26 -1
  189. package/utils/pagination-wrapper.ts +132 -50
  190. package/utils/settings.ts +4 -1
  191. package/utils/style.ts +39 -0
  192. package/utils/validators/formRules/__tests__/index.test.ts +36 -3
  193. package/utils/validators/formRules/index.ts +10 -3
  194. package/utils/window.js +11 -7
  195. package/components/__tests__/ApplicationCard.test.ts +0 -27
  196. package/components/cards/ApplicationCard.vue +0 -145
  197. package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
  198. package/config/secret.js +0 -14
  199. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
  200. /package/{components/form/SSHKnownHosts → dialog}/__tests__/KnownHostsEditDialog.test.ts +0 -0
@@ -0,0 +1,997 @@
1
+ <script>
2
+ import { clone, set } from '@shell/utils/object';
3
+ import jsyaml from 'js-yaml';
4
+ import { saferDump } from '@shell/utils/create-yaml';
5
+ import { mapGetters } from 'vuex';
6
+ import { base64Encode } from '@shell/utils/crypto';
7
+ import { exceptionToErrorsArray } from '@shell/utils/error';
8
+ import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
9
+ import { checkSchemasForFindAllHash } from '@shell/utils/auth';
10
+ import FleetUtils from '@shell/utils/fleet';
11
+ import {
12
+ AUTH_TYPE, CONFIG_MAP, FLEET, NORMAN, SECRET, VIRTUAL_HARVESTER_PROVIDER
13
+ } from '@shell/config/types';
14
+ import { CAPI, CATALOG, FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
15
+ import { SOURCE_TYPE } from '@shell/config/product/fleet';
16
+ import CreateEditView from '@shell/mixins/create-edit-view';
17
+ import CruResource from '@shell/components/CruResource';
18
+ import Loading from '@shell/components/Loading';
19
+ import FormValidation from '@shell/mixins/form-validation';
20
+ import Labels from '@shell/components/form/Labels';
21
+ import NameNsDescription from '@shell/components/form/NameNsDescription';
22
+ import LabeledSelect from '@shell/components/form/LabeledSelect';
23
+ import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
24
+ import Banner from '@components/Banner/Banner.vue';
25
+ import ButtonGroup from '@shell/components/ButtonGroup';
26
+ import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
27
+ import YamlEditor, { EDITOR_MODES } from '@shell/components/YamlEditor';
28
+ import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret';
29
+ import ValueFromResource from '@shell/components/form/ValueFromResource';
30
+ import { mapPref, DIFF } from '@shell/store/prefs';
31
+ import { isHarvesterCluster } from '@shell/utils/cluster';
32
+ import { SECRET_TYPES } from '@shell/config/secret';
33
+ import UnitInput from '@shell/components/form/UnitInput';
34
+ import { toSeconds } from '@shell/utils/duration';
35
+ import { DEFAULT_POLLING_INTERVAL, MINIMUM_POLLING_INTERVAL } from '@shell/models/fleet-application';
36
+
37
+ const VALUES_STATE = {
38
+ YAML: 'YAML',
39
+ DIFF: 'DIFF'
40
+ };
41
+
42
+ export default {
43
+ name: 'CruHelmOp',
44
+
45
+ inheritAttrs: false,
46
+
47
+ emits: ['input'],
48
+
49
+ components: {
50
+ Banner,
51
+ ButtonGroup,
52
+ Checkbox,
53
+ CruResource,
54
+ YamlEditor,
55
+ LabeledInput,
56
+ LabeledSelect,
57
+ Labels,
58
+ Loading,
59
+ NameNsDescription,
60
+ SelectOrCreateAuthSecret,
61
+ ValueFromResource,
62
+ UnitInput,
63
+ },
64
+
65
+ mixins: [CreateEditView, FormValidation],
66
+
67
+ async fetch() {
68
+ const hash = await checkSchemasForFindAllHash({
69
+ allClusters: {
70
+ inStoreType: 'management',
71
+ type: FLEET.CLUSTER
72
+ },
73
+
74
+ allClusterGroups: {
75
+ inStoreType: 'management',
76
+ type: FLEET.CLUSTER_GROUP
77
+ },
78
+
79
+ allSecrets: {
80
+ inStoreType: 'management',
81
+ type: SECRET
82
+ },
83
+
84
+ allConfigMaps: {
85
+ inStoreType: 'management',
86
+ type: CONFIG_MAP
87
+ }
88
+ }, this.$store);
89
+
90
+ this.allClusters = hash.allClusters || [];
91
+ this.allClusterGroups = hash.allClusterGroups || [];
92
+ this.allSecrets = hash.allSecrets || [];
93
+ this.allConfigMaps = hash.allConfigMaps || [];
94
+
95
+ this.updateTargets();
96
+ },
97
+
98
+ data() {
99
+ let pollingInterval = toSeconds(this.value.spec.pollingInterval) || this.value.spec.pollingInterval;
100
+
101
+ if (!pollingInterval) {
102
+ if (this.realMode === _CREATE) {
103
+ pollingInterval = DEFAULT_POLLING_INTERVAL;
104
+ this.value.spec.pollingInterval = this.durationSeconds(pollingInterval);
105
+ } else if (this.realMode === _EDIT || this.realMode === _VIEW) {
106
+ pollingInterval = MINIMUM_POLLING_INTERVAL;
107
+ }
108
+ }
109
+
110
+ const targetInfo = this.value.targetInfo;
111
+ const targetCluster = targetInfo.cluster;
112
+ const targetClusterGroup = targetInfo.clusterGroup;
113
+ const targetAdvanced = targetInfo.advanced;
114
+
115
+ let targetMode = targetInfo.mode;
116
+
117
+ if ( this.realMode === _CREATE ) {
118
+ targetMode = 'all';
119
+ } else if ( targetMode === 'cluster' ) {
120
+ targetMode = `cluster://${ targetCluster }`;
121
+ } else if ( targetMode === 'clusterGroup' ) {
122
+ targetMode = `group://${ targetClusterGroup }`;
123
+ }
124
+
125
+ const correctDriftEnabled = this.value.spec?.correctDrift?.enabled || false;
126
+
127
+ const chartValues = saferDump(clone(this.value.spec.helm.values));
128
+
129
+ return {
130
+ VALUES_STATE,
131
+ SOURCE_TYPE,
132
+ allClusters: [],
133
+ allClusterGroups: [],
134
+ allSecrets: [],
135
+ allConfigMaps: [],
136
+ allWorkspaces: [],
137
+ pollingInterval,
138
+ targetMode,
139
+ targetAdvanced,
140
+ targetAdvancedErrors: null,
141
+ sourceTypeInit: this.value.sourceType,
142
+ sourceType: this.value.sourceType || SOURCE_TYPE.REPO,
143
+ helmSpecInit: clone(this.value.spec.helm),
144
+ yamlForm: VALUES_STATE.YAML,
145
+ chartValues,
146
+ chartValuesInit: chartValues,
147
+ valuesFrom: FleetUtils.HelmOp.fromValuesFrom(this.value.spec.helm.valuesFrom),
148
+ correctDriftEnabled,
149
+ tempCachedValues: {},
150
+ doneRouteList: 'c-cluster-fleet-application',
151
+ isRealModeEdit: this.realMode === _EDIT,
152
+ fvFormRuleSets: []
153
+ };
154
+ },
155
+
156
+ created() {
157
+ this.registerBeforeHook(this.doCreateSecrets, `registerAuthSecrets${ new Date().getTime() }`, 99);
158
+ this.registerBeforeHook(this.updateBeforeSave);
159
+
160
+ if (this.realMode === _EDIT && this.workspace !== this.value.namespace) {
161
+ this.$store.commit('updateWorkspace', { value: this.value.namespace, getters: this.$store.getters });
162
+ }
163
+ },
164
+
165
+ mounted() {
166
+ this.value.applyDefaults();
167
+ this.updateValidationRules(this.sourceType);
168
+ },
169
+
170
+ computed: {
171
+ ...mapGetters(['workspace']),
172
+
173
+ steps() {
174
+ return [
175
+ {
176
+ name: 'basics',
177
+ title: this.t('fleet.helmOp.add.steps.metadata.title'),
178
+ label: this.t('fleet.helmOp.add.steps.metadata.label'),
179
+ subtext: this.t('fleet.helmOp.add.steps.metadata.subtext'),
180
+ descriptionKey: 'fleet.helmOp.add.steps.metadata.description',
181
+ ready: this.isView || !!this.value.metadata.name,
182
+ weight: 1
183
+ },
184
+ {
185
+ name: 'chart',
186
+ title: this.t('fleet.helmOp.add.steps.chart.title'),
187
+ label: this.t('fleet.helmOp.add.steps.chart.label'),
188
+ subtext: this.t('fleet.helmOp.add.steps.chart.subtext'),
189
+ descriptionKey: 'fleet.helmOp.add.steps.chart.description',
190
+ ready: this.isView || !!this.fvFormIsValid,
191
+ weight: 1
192
+ },
193
+ {
194
+ name: 'values',
195
+ title: this.t('fleet.helmOp.add.steps.values.title'),
196
+ label: this.t('fleet.helmOp.add.steps.values.label'),
197
+ subtext: this.t('fleet.helmOp.add.steps.values.subtext'),
198
+ descriptionKey: 'fleet.helmOp.add.steps.values.description',
199
+ ready: true,
200
+ weight: 1
201
+ },
202
+ {
203
+ name: 'target',
204
+ title: this.t('fleet.helmOp.add.steps.targetInfo.title'),
205
+ label: this.t('fleet.helmOp.add.steps.targetInfo.label'),
206
+ subtext: this.t('fleet.helmOp.add.steps.targetInfo.subtext'),
207
+ descriptionKey: 'fleet.helmOp.steps.add.targetInfo.description',
208
+ ready: true,
209
+ weight: 1
210
+ },
211
+ {
212
+ name: 'advanced',
213
+ title: this.t('fleet.helmOp.add.steps.advanced.title'),
214
+ label: this.t('fleet.helmOp.add.steps.advanced.label'),
215
+ subtext: this.t('fleet.helmOp.add.steps.advanced.subtext'),
216
+ descriptionKey: 'fleet.helmOp.add.steps.advanced.description',
217
+ ready: true,
218
+ weight: 1,
219
+ },
220
+ ];
221
+ },
222
+
223
+ isLocal() {
224
+ return this.value.metadata.namespace === 'fleet-local';
225
+ },
226
+
227
+ sourceTypeOptions() {
228
+ return Object.values(SOURCE_TYPE).map((value) => ({
229
+ value,
230
+ label: this.t(`fleet.helmOp.source.types.${ value }`)
231
+ }));
232
+ },
233
+
234
+ valueFromOptions() {
235
+ return [
236
+ {
237
+ value: 'configMapKeyRef', label: 'ConfigMap Key', hideVariableName: true
238
+ },
239
+ {
240
+ value: 'secretKeyRef', label: 'Secret key', hideVariableName: true
241
+ },
242
+ ];
243
+ },
244
+
245
+ yamlFormOptions() {
246
+ return [{
247
+ labelKey: 'fleet.helmOp.values.yaml.options.edit',
248
+ value: VALUES_STATE.YAML,
249
+ }, {
250
+ labelKey: 'fleet.helmOp.values.yaml.options.diff',
251
+ value: VALUES_STATE.DIFF,
252
+ disabled: this.chartValuesInit === this.chartValues,
253
+ }];
254
+ },
255
+
256
+ diffMode: mapPref(DIFF),
257
+
258
+ yamlDiffModeOptions() {
259
+ return [{
260
+ labelKey: 'resourceYaml.buttons.unified',
261
+ value: 'unified',
262
+ }, {
263
+ labelKey: 'resourceYaml.buttons.split',
264
+ value: 'split',
265
+ }];
266
+ },
267
+
268
+ isYamlDiff() {
269
+ return this.yamlForm === VALUES_STATE.DIFF;
270
+ },
271
+
272
+ editorMode() {
273
+ if (this.isYamlDiff) {
274
+ return EDITOR_MODES.DIFF_CODE;
275
+ }
276
+
277
+ return EDITOR_MODES.EDIT_CODE;
278
+ },
279
+
280
+ targetOptions() {
281
+ const out = [
282
+ {
283
+ label: 'No Clusters',
284
+ value: 'none'
285
+ },
286
+ {
287
+ label: 'All Clusters in the Workspace',
288
+ value: 'all',
289
+ },
290
+ {
291
+ label: 'Advanced',
292
+ value: 'advanced'
293
+ },
294
+ ];
295
+
296
+ const clusters = this.allClusters
297
+ .filter((x) => {
298
+ return x.metadata.namespace === this.value.metadata.namespace;
299
+ })
300
+ .filter((x) => !isHarvesterCluster(x))
301
+ .map((x) => {
302
+ return { label: x.nameDisplay, value: `cluster://${ x.metadata.name }` };
303
+ });
304
+
305
+ if ( clusters.length ) {
306
+ out.push({ kind: 'divider', disabled: true });
307
+ out.push({
308
+ kind: 'title',
309
+ label: 'Clusters',
310
+ disabled: true,
311
+ });
312
+
313
+ out.push(...clusters);
314
+ }
315
+
316
+ const groups = this.allClusterGroups
317
+ .filter((x) => x.metadata.namespace === this.value.metadata.namespace)
318
+ .map((x) => {
319
+ return { label: x.nameDisplay, value: `group://${ x.metadata.name }` };
320
+ });
321
+
322
+ if ( groups.length ) {
323
+ out.push({ kind: 'divider', disabled: true });
324
+ out.push({
325
+ kind: 'title',
326
+ label: 'Cluster Groups',
327
+ disabled: true
328
+ });
329
+
330
+ out.push(...groups);
331
+ }
332
+
333
+ return out;
334
+ },
335
+
336
+ showPollingIntervalWarning() {
337
+ return !this.isView && this.value.isPollingEnabled && this.pollingInterval < MINIMUM_POLLING_INTERVAL;
338
+ },
339
+ },
340
+
341
+ watch: {
342
+ 'value.metadata.namespace': 'updateTargets',
343
+ targetMode: 'updateTargets',
344
+ targetCluster: 'updateTargets',
345
+ targetClusterGroup: 'updateTargets',
346
+ targetAdvanced: 'updateTargets',
347
+
348
+ workspace(neu) {
349
+ if (this.isCreate) {
350
+ set(this.value, 'metadata.namespace', neu);
351
+ }
352
+ },
353
+ },
354
+
355
+ methods: {
356
+ onSourceTypeSelect(type) {
357
+ delete this.value.spec.helm.repo;
358
+ delete this.value.spec.helm.chart;
359
+ delete this.value.spec.helm.version;
360
+
361
+ if (this.realMode !== _CREATE && this.sourceTypeInit === type) {
362
+ const { repo, chart, version } = this.helmSpecInit;
363
+
364
+ set(this.value, 'spec.helm.repo', repo);
365
+ set(this.value, 'spec.helm.chart', chart);
366
+ set(this.value, 'spec.helm.version', version);
367
+ }
368
+
369
+ this.updateValidationRules(type);
370
+ },
371
+
372
+ updateTargets() {
373
+ const spec = this.value.spec;
374
+ const mode = this.targetMode;
375
+
376
+ let kind, value;
377
+ const match = mode.match(/([^:]+)(:\/\/(.*))?$/);
378
+
379
+ if ( match ) {
380
+ kind = match[1];
381
+ value = match[3];
382
+ }
383
+
384
+ if ( kind === 'all' ) {
385
+ spec.targets = [{
386
+ clusterSelector: {
387
+ matchExpressions: [{
388
+ key: CAPI.PROVIDER,
389
+ operator: 'NotIn',
390
+ values: [
391
+ VIRTUAL_HARVESTER_PROVIDER
392
+ ],
393
+ }],
394
+ },
395
+ }];
396
+ } else if ( kind === 'none' ) {
397
+ spec.targets = [];
398
+ } else if ( kind === 'cluster' ) {
399
+ spec.targets = [
400
+ { clusterName: value },
401
+ ];
402
+ } else if ( kind === 'group' ) {
403
+ spec.targets = [
404
+ { clusterGroup: value }
405
+ ];
406
+ } else if ( kind === 'advanced' ) {
407
+ try {
408
+ const parsed = jsyaml.load(this.targetAdvanced);
409
+
410
+ spec.targets = parsed;
411
+ this.targetAdvancedErrors = null;
412
+ } catch (e) {
413
+ this.targetAdvancedErrors = exceptionToErrorsArray(e);
414
+ }
415
+ } else {
416
+ spec.targets = [];
417
+ }
418
+ },
419
+
420
+ enablePolling(value) {
421
+ if (value) {
422
+ delete this.value.spec.disablePolling;
423
+ } else {
424
+ this.value.spec.disablePolling = true;
425
+ }
426
+ },
427
+
428
+ updatePollingInterval(value) {
429
+ if (!value) {
430
+ this.pollingInterval = DEFAULT_POLLING_INTERVAL;
431
+ this.value.spec.pollingInterval = this.durationSeconds(DEFAULT_POLLING_INTERVAL);
432
+ } else if (value === MINIMUM_POLLING_INTERVAL) {
433
+ delete this.value.spec.pollingInterval;
434
+ } else {
435
+ this.value.spec.pollingInterval = this.durationSeconds(value);
436
+ }
437
+ },
438
+
439
+ updateCachedAuthVal(val, key) {
440
+ this.tempCachedValues[key] = typeof val === 'string' ? { selected: val } : { ...val };
441
+ },
442
+
443
+ updateAuth(val, key) {
444
+ const spec = this.value.spec;
445
+
446
+ if ( val ) {
447
+ spec[key] = val;
448
+ } else {
449
+ delete spec[key];
450
+ }
451
+
452
+ this.updateCachedAuthVal(val, key);
453
+ },
454
+
455
+ async doCreateSecrets() {
456
+ if (this.tempCachedValues.clientSecretName) {
457
+ await this.doCreate('clientSecretName', this.tempCachedValues.clientSecretName);
458
+ }
459
+
460
+ if (this.tempCachedValues.helmSecretName) {
461
+ await this.doCreate('helmSecretName', this.tempCachedValues.helmSecretName);
462
+ }
463
+ },
464
+
465
+ async doCreate(name, credentials) {
466
+ const {
467
+ selected,
468
+ publicKey,
469
+ privateKey,
470
+ sshKnownHosts
471
+ } = credentials;
472
+
473
+ if ( ![AUTH_TYPE._SSH, AUTH_TYPE._BASIC, AUTH_TYPE._S3].includes(selected) ) {
474
+ return;
475
+ }
476
+
477
+ let secret;
478
+
479
+ if ( selected === AUTH_TYPE._S3 ) {
480
+ secret = await this.$store.dispatch(`rancher/create`, {
481
+ type: NORMAN.CLOUD_CREDENTIAL,
482
+ s3credentialConfig: {
483
+ accessKey: publicKey,
484
+ secretKey: privateKey,
485
+ },
486
+ });
487
+ } else {
488
+ secret = await this.$store.dispatch(`${ CATALOG._MANAGEMENT }/create`, {
489
+ type: SECRET,
490
+ metadata: {
491
+ namespace: this.value.metadata.namespace,
492
+ generateName: 'auth-',
493
+ labels: { [FLEET_LABELS.MANAGED]: 'true' }
494
+ }
495
+ });
496
+
497
+ let type, publicField, privateField;
498
+
499
+ switch ( selected ) {
500
+ case AUTH_TYPE._SSH:
501
+ type = SECRET_TYPES.SSH;
502
+ publicField = 'ssh-publickey';
503
+ privateField = 'ssh-privatekey';
504
+ break;
505
+ case AUTH_TYPE._BASIC:
506
+ type = SECRET_TYPES.BASIC;
507
+ publicField = 'username';
508
+ privateField = 'password';
509
+ break;
510
+ default:
511
+ throw new Error('Unknown type');
512
+ }
513
+
514
+ secret._type = type;
515
+ secret.data = {
516
+ [publicField]: base64Encode(publicKey),
517
+ [privateField]: base64Encode(privateKey),
518
+ };
519
+
520
+ // Add ssh known hosts
521
+ if (selected === AUTH_TYPE._SSH && sshKnownHosts) {
522
+ secret.data.known_hosts = base64Encode(sshKnownHosts);
523
+ }
524
+ }
525
+
526
+ await secret.save();
527
+
528
+ await this.$nextTick(() => {
529
+ this.updateAuth(secret.metadata.name, name);
530
+ });
531
+
532
+ return secret;
533
+ },
534
+
535
+ updateYamlForm() {
536
+ if (this.$refs.yaml) {
537
+ this.$refs.yaml.updateValue(this.chartValues);
538
+ }
539
+ },
540
+
541
+ updateChartValues(value) {
542
+ try {
543
+ const chartValues = jsyaml.load(value);
544
+
545
+ this.value.spec.helm.values = chartValues;
546
+ } catch (err) {
547
+ }
548
+ },
549
+
550
+ addValueFrom() {
551
+ this.valuesFrom.push({ valueFrom: {} });
552
+ },
553
+
554
+ updateValueFrom(index, value) {
555
+ this.valuesFrom[index] = value;
556
+
557
+ this.value.spec.helm.valuesFrom = FleetUtils.HelmOp.toValuesFrom(this.valuesFrom, this.workspace);
558
+ },
559
+
560
+ removeValueFrom(index) {
561
+ this.valuesFrom.splice(index, 1);
562
+ },
563
+
564
+ updateBeforeSave() {
565
+ this.value.spec['correctDrift'] = { enabled: this.correctDriftEnabled };
566
+ },
567
+
568
+ durationSeconds(value) {
569
+ return `${ value }s`;
570
+ },
571
+
572
+ updateValidationRules(sourceType) {
573
+ switch (sourceType) {
574
+ case SOURCE_TYPE.REPO:
575
+ this.fvFormRuleSets = [{
576
+ path: 'spec.helm.repo',
577
+ rules: ['required', 'urlRepository'],
578
+ }, {
579
+ path: 'spec.helm.chart',
580
+ rules: ['required', 'alphanumeric'],
581
+ }];
582
+ break;
583
+ case SOURCE_TYPE.OCI:
584
+ this.fvFormRuleSets = [{
585
+ path: 'spec.helm.chart',
586
+ rules: ['required', 'ociRegistry'],
587
+ }];
588
+ break;
589
+ case SOURCE_TYPE.TARBALL:
590
+ this.fvFormRuleSets = [{
591
+ path: 'spec.helm.chart',
592
+ rules: ['required', 'urlRepository'],
593
+ }];
594
+ break;
595
+ }
596
+ }
597
+ },
598
+ };
599
+ </script>
600
+
601
+ <template>
602
+ <Loading v-if="$fetchState.pending" />
603
+
604
+ <CruResource
605
+ v-else
606
+ :done-route="doneRouteList"
607
+ :mode="mode"
608
+ :resource="value"
609
+ :subtypes="[]"
610
+ :validation-passed="true"
611
+ :errors="errors"
612
+ :steps="steps"
613
+ :finish-mode="'finish'"
614
+ class="wizard"
615
+ @cancel="done"
616
+ @error="e=>errors = e"
617
+ @finish="save"
618
+ >
619
+ <template #basics>
620
+ <NameNsDescription
621
+ v-if="!isView"
622
+ :value="value"
623
+ :namespaced="false"
624
+ :mode="mode"
625
+ @update:value="$emit('input', $event)"
626
+ />
627
+ <Labels
628
+ :value="value"
629
+ :mode="mode"
630
+ :display-side-by-side="false"
631
+ :add-icon="'icon-plus'"
632
+ />
633
+ </template>
634
+
635
+ <template #chart>
636
+ <h2 v-t="'fleet.helmOp.source.release.title'" />
637
+
638
+ <div class="row mb-20">
639
+ <div class="col span-6">
640
+ <LabeledInput
641
+ v-model:value="value.spec.helm.releaseName"
642
+ :mode="mode"
643
+ :label-key="`fleet.helmOp.source.release.label`"
644
+ :placeholder="t(`fleet.helmOp.source.release.placeholder`, null, true)"
645
+ />
646
+ </div>
647
+ </div>
648
+
649
+ <h2 v-t="'fleet.helmOp.source.title'" />
650
+
651
+ <div
652
+ v-if="!isView"
653
+ class="row mb-20"
654
+ >
655
+ <div class="col span-6">
656
+ <LabeledSelect
657
+ v-model:value="sourceType"
658
+ :options="sourceTypeOptions"
659
+ option-key="value"
660
+ :mode="mode"
661
+ :selectable="option => !option.disabled"
662
+ :label="t('fleet.helmOp.source.selectLabel')"
663
+ @update:value="onSourceTypeSelect"
664
+ />
665
+ </div>
666
+ </div>
667
+
668
+ <template v-if="sourceType === SOURCE_TYPE.TARBALL">
669
+ <div class="row mb-20">
670
+ <div class="col span-6">
671
+ <LabeledInput
672
+ v-model:value="value.spec.helm.chart"
673
+ :mode="mode"
674
+ label-key="fleet.helmOp.source.tarball.label"
675
+ :placeholder="t('fleet.helmOp.source.tarball.placeholder', null, true)"
676
+ :rules="fvGetAndReportPathRules('spec.helm.chart')"
677
+ :required="true"
678
+ />
679
+ </div>
680
+ </div>
681
+ </template>
682
+
683
+ <template v-if="sourceType === SOURCE_TYPE.REPO">
684
+ <div class="row mb-20">
685
+ <div class="col span-6">
686
+ <LabeledInput
687
+ v-model:value="value.spec.helm.repo"
688
+ :mode="mode"
689
+ :label-key="`fleet.helmOp.source.${ sourceType }.repo.label`"
690
+ :placeholder="t(`fleet.helmOp.source.${ sourceType }.repo.placeholder`, null, true)"
691
+ :rules="fvGetAndReportPathRules('spec.helm.repo')"
692
+ :required="true"
693
+ />
694
+ </div>
695
+ </div>
696
+
697
+ <div class="row mb-20">
698
+ <div class="col span-6">
699
+ <LabeledInput
700
+ v-model:value="value.spec.helm.chart"
701
+ :mode="mode"
702
+ :label-key="`fleet.helmOp.source.${ sourceType }.chart.label`"
703
+ :placeholder="t(`fleet.helmOp.source.${ sourceType }.chart.placeholder`, null, true)"
704
+ :rules="fvGetAndReportPathRules('spec.helm.chart')"
705
+ :required="true"
706
+ />
707
+ </div>
708
+ <div class="col span-4">
709
+ <LabeledInput
710
+ v-model:value="value.spec.helm.version"
711
+ :mode="mode"
712
+ label-key="fleet.helmOp.source.version.label"
713
+ :placeholder="t('fleet.helmOp.source.version.placeholder', null, true)"
714
+ />
715
+ </div>
716
+ </div>
717
+ </template>
718
+
719
+ <template v-if="sourceType === SOURCE_TYPE.OCI">
720
+ <div class="row mb-20">
721
+ <div class="col span-6">
722
+ <LabeledInput
723
+ v-model:value="value.spec.helm.chart"
724
+ :mode="mode"
725
+ :label-key="`fleet.helmOp.source.${ sourceType }.chart.label`"
726
+ :placeholder="t(`fleet.helmOp.source.${ sourceType }.chart.placeholder`, null, true)"
727
+ :rules="fvGetAndReportPathRules('spec.helm.chart')"
728
+ :required="true"
729
+ />
730
+ </div>
731
+ <div class="col span-4">
732
+ <LabeledInput
733
+ v-model:value="value.spec.helm.version"
734
+ :mode="mode"
735
+ label-key="fleet.helmOp.source.version.label"
736
+ :placeholder="t('fleet.helmOp.source.version.placeholder', null, true)"
737
+ />
738
+ </div>
739
+ </div>
740
+ </template>
741
+ </template>
742
+
743
+ <template #values>
744
+ <Banner
745
+ color="info"
746
+ class="description"
747
+ label-key="fleet.helmOp.values.description"
748
+ />
749
+
750
+ <h2 v-t="'fleet.helmOp.values.title'" />
751
+
752
+ <div class="mb-15">
753
+ <div
754
+ v-if="isRealModeEdit"
755
+ class="yaml-form-controls"
756
+ >
757
+ <ButtonGroup
758
+ v-model:value="yamlForm"
759
+ inactive-class="bg-disabled btn-sm"
760
+ active-class="bg-primary btn-sm"
761
+ :options="yamlFormOptions"
762
+ @update:value="updateYamlForm"
763
+ />
764
+ <div
765
+ class="yaml-form-controls-spacer"
766
+ style="flex:1"
767
+ >
768
+ &nbsp;
769
+ </div>
770
+ <ButtonGroup
771
+ v-if="isYamlDiff"
772
+ v-model:value="diffMode"
773
+ :options="yamlDiffModeOptions"
774
+ inactive-class="bg-disabled btn-sm"
775
+ active-class="bg-primary btn-sm"
776
+ />
777
+ </div>
778
+
779
+ <YamlEditor
780
+ ref="yaml"
781
+ v-model:value="chartValues"
782
+ :mode="mode"
783
+ :initial-yaml-values="chartValuesInit"
784
+ :scrolling="true"
785
+ :editor-mode="editorMode"
786
+ :hide-preview-buttons="true"
787
+ @update:value="updateChartValues"
788
+ />
789
+ </div>
790
+
791
+ <div class="mb-20">
792
+ <h2 v-t="'fleet.helmOp.values.valuesFrom.selectLabel'" />
793
+ <div
794
+ v-for="(row, i) in valuesFrom"
795
+ :key="i"
796
+ >
797
+ <ValueFromResource
798
+ :value="row"
799
+ :options="valueFromOptions"
800
+ :all-secrets="allSecrets"
801
+ :all-config-maps="allConfigMaps"
802
+ :namespaced="true"
803
+ :mode="mode"
804
+ :show-variable-name="true"
805
+ @remove="removeValueFrom(i)"
806
+ @update:value="updateValueFrom(i, $event)"
807
+ />
808
+ </div>
809
+ <button
810
+ v-if="!isView"
811
+ v-t="'workload.container.command.addEnvVar'"
812
+ type="button"
813
+ class="btn role-tertiary add"
814
+ data-testid="add-env-var"
815
+ @click="addValueFrom"
816
+ />
817
+ </div>
818
+ </template>
819
+
820
+ <template #target>
821
+ <h2 v-t="isLocal ? 'fleet.helmOp.target.labelLocal' : 'fleet.helmOp.target.label'" />
822
+
823
+ <template v-if="!isLocal">
824
+ <div class="row">
825
+ <div class="col span-6">
826
+ <LabeledSelect
827
+ v-model:value="targetMode"
828
+ :options="targetOptions"
829
+ option-key="value"
830
+ :mode="mode"
831
+ :selectable="option => !option.disabled"
832
+ :label="t('fleet.helmOp.target.selectLabel')"
833
+ >
834
+ <template v-slot:option="opt">
835
+ <hr v-if="opt.kind === 'divider'">
836
+ <div v-else-if="opt.kind === 'title'">
837
+ {{ opt.label }}
838
+ </div>
839
+ <div v-else>
840
+ {{ opt.label }}
841
+ </div>
842
+ </template>
843
+ </LabeledSelect>
844
+ </div>
845
+ </div>
846
+
847
+ <div
848
+ v-if="targetMode === 'advanced'"
849
+ class="row mt-10"
850
+ >
851
+ <div class="col span-12">
852
+ <YamlEditor v-model:value="targetAdvanced" />
853
+ </div>
854
+ </div>
855
+
856
+ <Banner
857
+ v-for="(err, i) in targetAdvancedErrors"
858
+ :key="i"
859
+ color="error"
860
+ :label="err"
861
+ />
862
+ </template>
863
+
864
+ <div class="row mt-20">
865
+ <div class="col span-6">
866
+ <LabeledInput
867
+ v-model:value="value.spec.serviceAccount"
868
+ :mode="mode"
869
+ label-key="fleet.helmOp.serviceAccount.label"
870
+ placeholder-key="fleet.helmOp.serviceAccount.placeholder"
871
+ />
872
+ </div>
873
+ <div class="col span-6">
874
+ <LabeledInput
875
+ v-model:value="value.spec.targetNamespace"
876
+ :mode="mode"
877
+ label-key="fleet.helmOp.targetNamespace.label"
878
+ placeholder-key="fleet.helmOp.targetNamespace.placeholder"
879
+ label="Target Namespace"
880
+ placeholder="Optional: Require all resources to be in this namespace"
881
+ />
882
+ </div>
883
+ </div>
884
+ </template>
885
+
886
+ <template #advanced>
887
+ <Banner
888
+ v-if="!isView"
889
+ color="info"
890
+ label-key="fleet.helmOp.add.steps.advanced.info"
891
+ />
892
+
893
+ <h2 v-t="'fleet.helmOp.auth.title'" />
894
+
895
+ <SelectOrCreateAuthSecret
896
+ :value="value.spec.helmSecretName"
897
+ :register-before-hook="registerBeforeHook"
898
+ :namespace="value.metadata.namespace"
899
+ :delegate-create-to-parent="true"
900
+ in-store="management"
901
+ :mode="mode"
902
+ generate-name="helmrepo-auth-"
903
+ label-key="fleet.helmOp.auth.helm"
904
+ :pre-select="tempCachedValues.helmSecretName"
905
+ :cache-secrets="true"
906
+ :show-ssh-known-hosts="true"
907
+ @update:value="updateAuth($event, 'helmSecretName')"
908
+ @inputauthval="updateCachedAuthVal($event, 'helmSecretName')"
909
+ />
910
+
911
+ <div class="row mt-20 mb-20">
912
+ <div class="col span-6">
913
+ <Checkbox
914
+ v-model:value="value.spec.insecureSkipTLSVerify"
915
+ type="checkbox"
916
+ label-key="fleet.helmOp.tls.insecure"
917
+ :mode="mode"
918
+ />
919
+ </div>
920
+ </div>
921
+
922
+ <h2 v-t="'fleet.helmOp.resources.label'" />
923
+
924
+ <div class="resource-handling mb-30">
925
+ <Checkbox
926
+ v-model:value="correctDriftEnabled"
927
+ :tooltip="t('fleet.helmOp.resources.correctDriftTooltip')"
928
+ type="checkbox"
929
+ label-key="fleet.helmOp.resources.correctDrift"
930
+ :mode="mode"
931
+ />
932
+ <Checkbox
933
+ v-model:value="value.spec.keepResources"
934
+ :tooltip="t('fleet.helmOp.resources.keepResourcesTooltip')"
935
+ type="checkbox"
936
+ label-key="fleet.helmOp.resources.keepResources"
937
+ :mode="mode"
938
+ />
939
+ </div>
940
+
941
+ <h2 v-t="'fleet.helmOp.polling.label'" />
942
+ <div class="row polling">
943
+ <div class="col span-6">
944
+ <Checkbox
945
+ :value="value.isPollingEnabled"
946
+ type="checkbox"
947
+ label-key="fleet.helmOp.polling.enable"
948
+ :mode="mode"
949
+ @update:value="enablePolling"
950
+ />
951
+ </div>
952
+ <template v-if="value.isPollingEnabled">
953
+ <div class="col">
954
+ <Banner
955
+ v-if="showPollingIntervalWarning"
956
+ color="warning"
957
+ label-key="fleet.helmOp.polling.pollingInterval.minimumValuewarning"
958
+ />
959
+ </div>
960
+ <div class="col span-6">
961
+ <UnitInput
962
+ v-model:value="pollingInterval"
963
+ min="1"
964
+ :suffix="t('suffix.seconds', { count: pollingInterval })"
965
+ :label="t('fleet.helmOp.polling.pollingInterval.label')"
966
+ :mode="mode"
967
+ tooltip-key="fleet.helmOp.polling.pollingInterval.tooltip"
968
+ @update:value="updatePollingInterval"
969
+ />
970
+ </div>
971
+ </template>
972
+ </div>
973
+ </template>
974
+ </CruResource>
975
+ </template>
976
+
977
+ <style lang="scss" scoped>
978
+ .yaml-form-controls {
979
+ display: flex;
980
+ margin-bottom: 15px;
981
+ }
982
+ :deep() .yaml-editor {
983
+ .root {
984
+ height: auto !important;
985
+ }
986
+ }
987
+ .resource-handling {
988
+ display: flex;
989
+ flex-direction: column;
990
+ gap: 5px;
991
+ }
992
+ .polling {
993
+ display: flex;
994
+ flex-direction: column;
995
+ gap: 5px;
996
+ }
997
+ </style>