@rancher/shell 0.1.21 → 0.2.2

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 (99) hide show
  1. package/assets/styles/global/_button.scss +1 -0
  2. package/assets/translations/en-us.yaml +56 -10
  3. package/assets/translations/zh-hans.yaml +346 -117
  4. package/components/Carousel.vue +25 -9
  5. package/components/HarvesterServiceAddOnConfig.vue +10 -10
  6. package/components/Import.vue +7 -1
  7. package/components/ResourceList/index.vue +34 -14
  8. package/components/ResourceTable.vue +19 -0
  9. package/components/SortableTable/THead.vue +311 -70
  10. package/components/SortableTable/advanced-filtering.js +272 -0
  11. package/components/SortableTable/filtering.js +90 -29
  12. package/components/SortableTable/index.vue +480 -271
  13. package/components/form/MatchExpressions.vue +15 -3
  14. package/components/form/WorkloadPorts.vue +2 -2
  15. package/components/nav/Header.vue +14 -1
  16. package/config/product/settings.js +1 -0
  17. package/config/product/uiplugins.js +1 -0
  18. package/config/uiplugins.js +13 -0
  19. package/creators/app/init +2 -2
  20. package/creators/app/package.json +1 -1
  21. package/creators/pkg/package.json +1 -1
  22. package/detail/cis.cattle.io.clusterscan.vue +6 -2
  23. package/detail/provisioning.cattle.io.cluster.vue +3 -3
  24. package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +0 -1
  25. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +5 -3
  26. package/edit/provisioning.cattle.io.cluster/rke2.vue +25 -24
  27. package/edit/service.vue +1 -1
  28. package/list/node.vue +7 -2
  29. package/mixins/resource-manager.js +5 -0
  30. package/models/cluster.x-k8s.io.machinedeployment.js +8 -0
  31. package/models/management.cattle.io.cluster.js +14 -1
  32. package/nuxt.config.js +113 -108
  33. package/package.json +1 -1
  34. package/pages/c/_cluster/apps/charts/index.vue +1 -1
  35. package/pages/c/_cluster/apps/charts/install.vue +106 -32
  36. package/pages/c/_cluster/settings/performance.vue +11 -0
  37. package/pages/c/_cluster/uiplugins/InstallDialog.vue +16 -2
  38. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +5 -2
  39. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +28 -6
  40. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +1 -1
  41. package/pages/c/_cluster/uiplugins/index.vue +49 -12
  42. package/promptRemove/mixin/roleDeletionCheck.js +15 -1
  43. package/scripts/record-deps.js +3 -3
  44. package/scripts/test-plugins-build.sh +1 -0
  45. package/scripts/typegen.sh +2 -2
  46. package/store/type-map.js +13 -2
  47. package/types/vue-shim.d +20 -0
  48. package/utils/create-yaml.js +30 -6
  49. package/creators/update/yarn-error.log +0 -54
  50. package/rancher-components/BadgeState/BadgeState.spec.ts +0 -12
  51. package/rancher-components/BadgeState/BadgeState.vue +0 -107
  52. package/rancher-components/BadgeState/index.ts +0 -1
  53. package/rancher-components/Banner/Banner.test.ts +0 -13
  54. package/rancher-components/Banner/Banner.vue +0 -163
  55. package/rancher-components/Banner/index.ts +0 -1
  56. package/rancher-components/Card/Card.vue +0 -150
  57. package/rancher-components/Card/index.ts +0 -1
  58. package/rancher-components/Form/Checkbox/Checkbox.test.ts +0 -77
  59. package/rancher-components/Form/Checkbox/Checkbox.vue +0 -395
  60. package/rancher-components/Form/Checkbox/index.ts +0 -1
  61. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +0 -29
  62. package/rancher-components/Form/LabeledInput/LabeledInput.vue +0 -343
  63. package/rancher-components/Form/LabeledInput/index.ts +0 -1
  64. package/rancher-components/Form/Radio/RadioButton.vue +0 -270
  65. package/rancher-components/Form/Radio/RadioGroup.vue +0 -235
  66. package/rancher-components/Form/Radio/index.ts +0 -2
  67. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +0 -168
  68. package/rancher-components/Form/TextArea/index.ts +0 -1
  69. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +0 -107
  70. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +0 -137
  71. package/rancher-components/Form/ToggleSwitch/index.ts +0 -1
  72. package/rancher-components/Form/index.ts +0 -5
  73. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +0 -137
  74. package/rancher-components/LabeledTooltip/index.ts +0 -1
  75. package/rancher-components/components/BadgeState/BadgeState.spec.ts +0 -12
  76. package/rancher-components/components/BadgeState/BadgeState.vue +0 -107
  77. package/rancher-components/components/BadgeState/index.ts +0 -1
  78. package/rancher-components/components/Banner/Banner.test.ts +0 -13
  79. package/rancher-components/components/Banner/Banner.vue +0 -163
  80. package/rancher-components/components/Banner/index.ts +0 -1
  81. package/rancher-components/components/Card/Card.vue +0 -150
  82. package/rancher-components/components/Card/index.ts +0 -1
  83. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +0 -77
  84. package/rancher-components/components/Form/Checkbox/Checkbox.vue +0 -395
  85. package/rancher-components/components/Form/Checkbox/index.ts +0 -1
  86. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +0 -29
  87. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +0 -343
  88. package/rancher-components/components/Form/LabeledInput/index.ts +0 -1
  89. package/rancher-components/components/Form/Radio/RadioButton.vue +0 -270
  90. package/rancher-components/components/Form/Radio/RadioGroup.vue +0 -235
  91. package/rancher-components/components/Form/Radio/index.ts +0 -2
  92. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +0 -168
  93. package/rancher-components/components/Form/TextArea/index.ts +0 -1
  94. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +0 -107
  95. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +0 -137
  96. package/rancher-components/components/Form/ToggleSwitch/index.ts +0 -1
  97. package/rancher-components/components/Form/index.ts +0 -5
  98. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -137
  99. package/rancher-components/components/LabeledTooltip/index.ts +0 -1
@@ -5,9 +5,10 @@ import { mapGetters } from 'vuex';
5
5
  import { isArray, removeObject } from '@shell/utils/array';
6
6
  import { clone } from '@shell/utils/object';
7
7
  import { convert, simplify } from '@shell/utils/selector';
8
+ import LabeledSelect from '@shell/components/form/LabeledSelect';
8
9
 
9
10
  export default {
10
- components: { Select },
11
+ components: { Select, LabeledSelect },
11
12
  props: {
12
13
  // Array of actual match expressions
13
14
  // or k8s selector Object of {matchExpressions, matchLabels}
@@ -38,6 +39,12 @@ export default {
38
39
  showRemove: {
39
40
  type: Boolean,
40
41
  default: true
42
+ },
43
+
44
+ // if options are passed for keys, then the key's input will become a select
45
+ keysSelectOptions: {
46
+ type: Array,
47
+ default: () => []
41
48
  }
42
49
  },
43
50
 
@@ -108,6 +115,10 @@ export default {
108
115
  return POD;
109
116
  },
110
117
 
118
+ hasKeySelectOptions() {
119
+ return !!this.keysSelectOptions?.length;
120
+ },
121
+
111
122
  ...mapGetters({ t: 'i18n/t' })
112
123
  },
113
124
 
@@ -194,11 +205,12 @@ export default {
194
205
  <div v-if="isView">
195
206
  {{ row.key }}
196
207
  </div>
197
- <input
208
+ <input v-else-if="!hasKeySelectOptions" v-model="row.key" :mode="mode" @input="update" />
209
+ <LabeledSelect
198
210
  v-else
199
211
  v-model="row.key"
200
212
  :mode="mode"
201
- @input="update"
213
+ :options="keysSelectOptions"
202
214
  />
203
215
  </div>
204
216
  <div
@@ -366,7 +366,7 @@ export default {
366
366
  v-model="row._ipam"
367
367
  :mode="mode"
368
368
  :options="ipamOptions"
369
- :label="t('harvester.service.ipam.label')"
369
+ :label="t('servicesPage.harvester.ipam.label')"
370
370
  :disabled="mode === 'edit'"
371
371
  @input="queueUpdate"
372
372
  />
@@ -376,7 +376,7 @@ export default {
376
376
  v-model="rows[ipamIndex]._ipam"
377
377
  :mode="mode"
378
378
  :options="ipamOptions"
379
- :label="t('harvester.service.ipam.label')"
379
+ :label="t('servicesPage.harvester.ipam.label')"
380
380
  :disabled="true"
381
381
  @input="queueUpdate"
382
382
  />
@@ -109,6 +109,19 @@ export default {
109
109
  return !this.featureRancherDesktop;
110
110
  },
111
111
 
112
+ showFilter() {
113
+ // Some products won't have a current cluster
114
+ const validClusterOrProduct = this.currentCluster ||
115
+ (this.currentProduct && this.currentProduct.customNamespaceFilter) ||
116
+ (this.currentProduct && this.currentProduct.showWorkspaceSwitcher);
117
+ // Don't show if the header is in 'simple' mode
118
+ const notSimple = !this.simple;
119
+ // One of these must be enabled, otherwise t here's no component to show
120
+ const validFilterSettings = this.currentProduct.showNamespaceFilter || this.currentProduct.showWorkspaceSwitcher;
121
+
122
+ return validClusterOrProduct && notSimple && validFilterSettings;
123
+ },
124
+
112
125
  featureRancherDesktop() {
113
126
  return this.$config.rancherEnv === 'desktop';
114
127
  },
@@ -332,7 +345,7 @@ export default {
332
345
  <component :is="navHeaderRight" />
333
346
 
334
347
  <div
335
- v-if="(currentCluster || (currentProduct && currentProduct.customNamespaceFilter)) && !simple && (currentProduct.showNamespaceFilter || currentProduct.showWorkspaceSwitcher)"
348
+ v-if="showFilter"
336
349
  class="top"
337
350
  >
338
351
  <NamespaceFilter v-if="clusterReady && currentProduct && (currentProduct.showNamespaceFilter || isExplorer)" />
@@ -26,6 +26,7 @@ export function init(store) {
26
26
  removable: false,
27
27
  showClusterSwitcher: false,
28
28
  category: 'configuration',
29
+ weight: 100,
29
30
  });
30
31
 
31
32
  virtualType({
@@ -13,5 +13,6 @@ export function init(store) {
13
13
  removable: false,
14
14
  showClusterSwitcher: false,
15
15
  category: 'configuration',
16
+ weight: 50,
16
17
  });
17
18
  }
@@ -34,6 +34,7 @@ export const UI_PLUGIN_CHART_ANNOTATIONS = {
34
34
  RANCHER_VERSION: 'catalog.cattle.io/rancher-version',
35
35
  EXTENSIONS_VERSION: 'catalog.cattle.io/ui-extenstions-version',
36
36
  EXTENSIONS_HOST: 'catalog.cattle.io/ui-extenstions-host',
37
+ DISPLAY_NAME: 'catalog.cattle.io/display-name',
37
38
  };
38
39
 
39
40
  // Plugin Metadata properties
@@ -41,6 +42,7 @@ export const UI_PLUGIN_METADATA = {
41
42
  RANCHER_VERSION: 'rancherVersion',
42
43
  EXTENSION_VERSION: 'extVersion',
43
44
  EXTENSIONS_HOST: 'host',
45
+ DISPLAY_NAME: 'displayName',
44
46
  };
45
47
 
46
48
  export function isUIPlugin(chart) {
@@ -55,6 +57,17 @@ export function uiPluginHasAnnotation(chart, name, value) {
55
57
  });
56
58
  }
57
59
 
60
+ /**
61
+ * Get value of the annotation from teh latest version for a chart
62
+ */
63
+ export function uiPluginAnnotation(chart, name) {
64
+ if (chart?.versions?.length > 0) {
65
+ return chart.versions[0].annotations?.[name];
66
+ }
67
+
68
+ return undefined;
69
+ }
70
+
58
71
  // Should we load a plugin, based on the metadata returned by the backend?
59
72
  // Returns error key string or false
60
73
  export function shouldNotLoadPlugin(plugin, rancherVersion) {
package/creators/app/init CHANGED
@@ -73,8 +73,8 @@ pkg.dependencies['@rancher/shell'] = `^${ creatorPkg.version }`;
73
73
 
74
74
  // Rest of dependencies are in the _pkgs property of package.json - copy then across
75
75
  if (creatorPkg._pkgs) {
76
- Object.keys(creatorPkg._pkgs).forEach((pkg) => {
77
- pkg.dependencies[pkg] = creatorPkg._pkgs[pkg];
76
+ Object.keys(creatorPkg._pkgs).forEach((pkgName) => {
77
+ pkg.dependencies[pkgName] = creatorPkg._pkgs[pkgName];
78
78
  });
79
79
  }
80
80
 
@@ -7,7 +7,7 @@
7
7
  "private": false,
8
8
  "bin": "./init",
9
9
  "files": [
10
- "*.*",
10
+ "**/*.*",
11
11
  "init"
12
12
  ],
13
13
  "engines": {
@@ -7,7 +7,7 @@
7
7
  "private": false,
8
8
  "bin": "./init",
9
9
  "files": [
10
- "*.*",
10
+ "**/*.*",
11
11
  "init"
12
12
  ],
13
13
  "engines": {
@@ -290,8 +290,12 @@ export default {
290
290
  :headers="reportCheckHeaders"
291
291
  key-field="id"
292
292
  >
293
- <template #sub-row="{row, fullColspan}">
294
- <tr class="sub-row">
293
+ <template #sub-row="{row, fullColspan, onRowMouseEnter, onRowMouseLeave}">
294
+ <tr
295
+ class="sub-row"
296
+ @mouseenter="onRowMouseEnter"
297
+ @mouseleave="onRowMouseLeave"
298
+ >
295
299
  <td :colspan="fullColspan">
296
300
  <Banner v-if="(row.state==='fail' || row.state==='warn')&& row.remediation" class="sub-banner" :label="remediationDisplay(row)" color="warning" />
297
301
  <SortableTable
@@ -631,11 +631,11 @@ export default {
631
631
  </div>
632
632
  </div>
633
633
  <div v-if="group.ref" class="right mr-45">
634
- <template v-if="value.hasLink('update')">
634
+ <template v-if="value.hasLink('update') && group.ref.showScalePool">
635
635
  <button v-tooltip="t('node.list.scaleDown')" :disabled="!group.ref.canScaleDownPool()" type="button" class="btn btn-sm role-secondary" @click="group.ref.scalePool(-1)">
636
636
  <i class="icon icon-sm icon-minus" />
637
637
  </button>
638
- <button v-tooltip="t('node.list.scaleUp')" type="button" class="btn btn-sm role-secondary ml-10" @click="group.ref.scalePool(1)">
638
+ <button v-tooltip="t('node.list.scaleUp')" :disabled="!group.ref.canScaleUpPool()" type="button" class="btn btn-sm role-secondary ml-10" @click="group.ref.scalePool(1)">
639
639
  <i class="icon icon-sm icon-plus" />
640
640
  </button>
641
641
  </template>
@@ -709,7 +709,7 @@ export default {
709
709
  </Tab>
710
710
 
711
711
  <Tab v-if="showRegistration" name="registration" :label="t('cluster.tabs.registration')" :weight="2">
712
- <Banner color="warning" :label="t('cluster.import.warningBanner')" />
712
+ <Banner v-if="!value.isCustom" color="warning" :label="t('cluster.import.warningBanner')" />
713
713
  <CustomCommand v-if="value.isCustom" :cluster-token="clusterToken" :cluster="value" @copied-windows="hasWindowsMachine ? null : showWindowsWarning = true" />
714
714
  <template v-else>
715
715
  <h4 v-html="t('cluster.import.commandInstructions', null, true)" />
@@ -10,7 +10,6 @@ const DEFAULTS = {
10
10
  force: false, // Show; true = Delete standalone pods, false = fail if there are any
11
11
  gracePeriod: -1, // Show; Pod shut down time, negative value uses pod default
12
12
  ignoreDaemonSets: true, // Hide; true = work, false = never work because there's always daemonSets
13
- ignoreErrors: false, // Hide; profit?
14
13
  skipWaitForDeleteTimeoutSeconds: 0, // Hide; If the pod deletion time is older than this > 0, don't wait, for some reason
15
14
  timeout: 120, // Show; Give up after this many seconds
16
15
  };
@@ -35,9 +35,10 @@ export default {
35
35
  default: () => ({})
36
36
  },
37
37
 
38
+ // no credentials are required for elemental machine pools
38
39
  credentialId: {
39
- type: String,
40
- required: true,
40
+ type: String,
41
+ default: null
41
42
  },
42
43
 
43
44
  mode: {
@@ -219,7 +220,8 @@ export default {
219
220
  :machine-pools="machinePools"
220
221
  @error="e=>errors = e"
221
222
  />
222
- <Banner v-else color="info" label="You do not have access to see this machine pool's configuration." />
223
+ <Banner v-else-if="value.configMissing" color="error" label-key="cluster.machinePool.configNotFound" />
224
+ <Banner v-else color="info" label-key="cluster.machinePool.noAccessBanner" />
223
225
 
224
226
  <AdvancedSection :mode="mode" class="advanced">
225
227
  <portal-target :name="'advanced-' + uuid" multiple />
@@ -202,26 +202,6 @@ export default {
202
202
  set(this.value.spec, 'kubernetesVersion', this.defaultVersion);
203
203
  }
204
204
 
205
- for ( const k in this.serverArgs ) {
206
- if ( this.serverConfig[k] === undefined ) {
207
- const def = this.serverArgs[k].default;
208
-
209
- set(this.serverConfig, k, (def !== undefined ? def : undefined));
210
- }
211
- }
212
-
213
- for ( const k in this.agentArgs ) {
214
- if ( this.agentConfig[k] === undefined ) {
215
- const def = this.agentArgs[k].default;
216
-
217
- set(this.agentConfig, k, (def !== undefined ? def : undefined));
218
- }
219
- }
220
-
221
- if ( !this.serverConfig.profile ) {
222
- set(this.serverConfig, 'profile', null);
223
- }
224
-
225
205
  if ( this.rkeConfig.etcd?.s3?.bucket ) {
226
206
  this.s3Backup = true;
227
207
  }
@@ -922,6 +902,9 @@ export default {
922
902
 
923
903
  // Allow time for addonNames to update... then fetch any missing addons
924
904
  this.$nextTick(() => this.initAddons());
905
+ if (this.mode === _CREATE) {
906
+ this.initServerAgentArgs();
907
+ }
925
908
  },
926
909
 
927
910
  showCni(neu) {
@@ -1113,10 +1096,6 @@ export default {
1113
1096
  entry.config = await entry.config.save();
1114
1097
  }
1115
1098
 
1116
- if ( !entry.pool.hostnamePrefix ) {
1117
- entry.pool.hostnamePrefix = `${ prefix }-`;
1118
- }
1119
-
1120
1099
  finalPools.push(entry.pool);
1121
1100
  }
1122
1101
 
@@ -1387,6 +1366,28 @@ export default {
1387
1366
  return merge({}, defaultChartValue?.values || {}, this.userChartValues[key] || {});
1388
1367
  },
1389
1368
 
1369
+ initServerAgentArgs() {
1370
+ for ( const k in this.serverArgs ) {
1371
+ if ( this.serverConfig[k] === undefined ) {
1372
+ const def = this.serverArgs[k].default;
1373
+
1374
+ set(this.serverConfig, k, (def !== undefined ? def : undefined));
1375
+ }
1376
+ }
1377
+
1378
+ for ( const k in this.agentArgs ) {
1379
+ if ( this.agentConfig[k] === undefined ) {
1380
+ const def = this.agentArgs[k].default;
1381
+
1382
+ set(this.agentConfig, k, (def !== undefined ? def : undefined));
1383
+ }
1384
+ }
1385
+
1386
+ if ( !this.serverConfig.profile ) {
1387
+ set(this.serverConfig, 'profile', null);
1388
+ }
1389
+ },
1390
+
1390
1391
  chartVersionKey(name) {
1391
1392
  const addonVersion = this.addonVersions.find(av => av.name === name);
1392
1393
 
package/edit/service.vue CHANGED
@@ -455,7 +455,7 @@ export default {
455
455
  <Tab
456
456
  v-if="showHarvesterAddOnConfig"
457
457
  name="add-on-config"
458
- :label="t('harvester.service.title')"
458
+ :label="t('servicesPage.harvester.title')"
459
459
  :weight="-1"
460
460
  >
461
461
  <HarvesterServiceAddOnConfig
package/list/node.vue CHANGED
@@ -153,8 +153,13 @@ export default {
153
153
  :loading="loading"
154
154
  v-on="$listeners"
155
155
  >
156
- <template #sub-row="{fullColspan, row}">
157
- <tr class="taints sub-row" :class="{'empty-taints': !row.spec.taints || !row.spec.taints.length}">
156
+ <template #sub-row="{fullColspan, row, onRowMouseEnter, onRowMouseLeave}">
157
+ <tr
158
+ class="taints sub-row"
159
+ :class="{'empty-taints': !row.spec.taints || !row.spec.taints.length}"
160
+ @mouseenter="onRowMouseEnter"
161
+ @mouseleave="onRowMouseLeave"
162
+ >
158
163
  <template v-if="row.spec.taints && row.spec.taints.length">
159
164
  <td>&nbsp;</td>
160
165
  <td>&nbsp;</td>
@@ -74,6 +74,11 @@ export default {
74
74
  // - id param = this.$store.getters['cluster/keyFieldForType'](type)
75
75
  // - id value = new dashboard-store getter, overwritten by steve store getter
76
76
  requestData.forEach((item) => {
77
+ // if there's already a prop type, don't overwrite it without storing it first...
78
+ // only do this operation once in multiple apply's because the requestData is the same!
79
+ if (item.type && !item._type) {
80
+ item._type = item.type;
81
+ }
77
82
  item.type = type;
78
83
  item.id = `${ item.metadata.namespace }/${ item.metadata.name }`;
79
84
  });
@@ -159,6 +159,14 @@ export default class CapiMachineDeployment extends SteveModel {
159
159
  return notOnlyOfRole(this, this.cluster.machines);
160
160
  }
161
161
 
162
+ canScaleUpPool() {
163
+ return true;
164
+ }
165
+
166
+ get showScalePool() {
167
+ return this.canScaleDownPool() || this.canScaleUpPool();
168
+ }
169
+
162
170
  get stateParts() {
163
171
  const out = [
164
172
  {
@@ -1,6 +1,6 @@
1
1
  import Vue from 'vue';
2
2
  import { CATALOG, CLUSTER_BADGE } from '@shell/config/labels-annotations';
3
- import { NODE, FLEET, MANAGEMENT } from '@shell/config/types';
3
+ import { NODE, FLEET, MANAGEMENT, CAPI } from '@shell/config/types';
4
4
  import { insertAt } from '@shell/utils/array';
5
5
  import { downloadFile } from '@shell/utils/download';
6
6
  import { parseSi } from '@shell/utils/units';
@@ -430,4 +430,17 @@ export default class MgmtCluster extends HybridModel {
430
430
  get nodes() {
431
431
  return this.$getters['all'](MANAGEMENT.NODE).filter(node => node.id.startsWith(this.id));
432
432
  }
433
+
434
+ get provClusterId() {
435
+ const isRKE1 = !!this.spec?.rancherKubernetesEngineConfig;
436
+ // Note: RKE1 provisioning cluster IDs are in a different format. For example,
437
+ // RKE2 cluster IDs include the name - fleet-default/cluster-name - whereas an RKE1
438
+ // cluster has the less human readable management cluster ID in it: fleet-default/c-khk48
439
+
440
+ const verb = this.isLocal || isRKE1 ? 'to' : 'from';
441
+ const from = `${ verb }Type`;
442
+ const id = `${ verb }Id`;
443
+
444
+ return this.metadata.relationships.find(r => r[from] === CAPI.RANCHER_CLUSTER)?.[id];
445
+ }
433
446
  }