@rancher/shell 0.1.2 → 0.1.3

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 (258) hide show
  1. package/assets/translations/en-us.yaml +27 -769
  2. package/assets/translations/zh-hans.yaml +8 -769
  3. package/components/ActionMenu.vue +3 -3
  4. package/components/CodeMirror.vue +6 -8
  5. package/components/CommunityLinks.vue +1 -1
  6. package/components/ContainerResourceLimit.vue +14 -0
  7. package/components/ExplorerMembers.vue +123 -0
  8. package/components/ExplorerProjectsNamespaces.vue +405 -0
  9. package/components/GrafanaDashboard.vue +17 -2
  10. package/components/LocaleSelector.vue +81 -0
  11. package/components/PromptModal.vue +2 -3
  12. package/components/ResourceList/index.vue +1 -1
  13. package/components/ResourceTable.vue +3 -6
  14. package/components/SingleClusterInfo.vue +1 -1
  15. package/components/SortableTable/index.vue +23 -20
  16. package/components/SortableTable/selection.js +1 -0
  17. package/components/auth/AzureWarning.vue +5 -1
  18. package/components/auth/Principal.vue +1 -1
  19. package/components/auth/RoleDetailEdit.vue +18 -11
  20. package/components/fleet/FleetRepos.vue +0 -2
  21. package/components/form/NameNsDescription.vue +4 -6
  22. package/components/form/NodeScheduling.vue +1 -1
  23. package/components/form/WorkloadPorts.vue +1 -1
  24. package/components/formatter/WorkloadHealthScale.vue +1 -1
  25. package/components/nav/Header.vue +9 -9
  26. package/components/nav/NamespaceFilter.vue +7 -4
  27. package/components/nav/TopLevelMenu.vue +6 -43
  28. package/components/nav/WindowManager/ContainerLogs.vue +1 -1
  29. package/config/product/harvester-manager.js +64 -2
  30. package/config/product/manager.js +9 -0
  31. package/config/settings.js +17 -71
  32. package/config/table-headers.js +0 -1
  33. package/config/types.js +5 -25
  34. package/core/plugin-routes.ts +34 -22
  35. package/core/plugin.ts +15 -3
  36. package/core/plugins-loader.js +2 -0
  37. package/core/plugins.js +79 -36
  38. package/core/types.ts +7 -1
  39. package/detail/provisioning.cattle.io.cluster.vue +13 -0
  40. package/detail/workload/index.vue +11 -5
  41. package/{components/dialog → dialog}/AddClusterMemberDialog.vue +0 -0
  42. package/{components/dialog → dialog}/AddCustomBadgeDialog.vue +0 -0
  43. package/{components/dialog → dialog}/AddProjectMemberDialog.vue +0 -0
  44. package/{components/dialog → dialog}/AddonConfigConfirmationDialog.vue +0 -0
  45. package/{components/dialog → dialog}/DrainNode.vue +0 -0
  46. package/{components/dialog → dialog}/ForceMachineRemoveDialog.vue +0 -0
  47. package/{components/dialog → dialog}/GenericPrompt.vue +0 -0
  48. package/{components/dialog → dialog}/RollbackWorkloadDialog.vue +0 -0
  49. package/{components/dialog → dialog}/RotateCertificatesDialog.vue +0 -0
  50. package/{components/dialog → dialog}/RotateEncryptionKeyDialog.vue +0 -0
  51. package/{components/dialog → dialog}/SaveAsRKETemplateDialog.vue +0 -0
  52. package/{components/dialog → dialog}/ScaleMachineDownDialog.vue +0 -0
  53. package/edit/auth/azuread.vue +20 -1
  54. package/edit/management.cattle.io.project.vue +2 -2
  55. package/edit/namespace.vue +17 -10
  56. package/edit/persistentvolumeclaim.vue +1 -0
  57. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +1 -1
  58. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +33 -5
  59. package/edit/service.vue +1 -1
  60. package/edit/workload/index.vue +363 -15
  61. package/edit/workload/mixins/workload.js +62 -7
  62. package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +1 -0
  63. package/layouts/default.vue +52 -27
  64. package/layouts/error.vue +5 -1
  65. package/layouts/home.vue +6 -2
  66. package/list/harvesterhci.io.management.cluster.vue +74 -33
  67. package/list/namespace.vue +3 -5
  68. package/machine-config/amazonec2.vue +2 -0
  69. package/machine-config/harvester.vue +96 -49
  70. package/middleware/authenticated.js +56 -52
  71. package/mixins/form-validation.js +1 -1
  72. package/mixins/resource-fetch.js +3 -1
  73. package/models/fleet.cattle.io.bundle.js +26 -19
  74. package/models/harvesterhci.io.management.cluster.js +194 -5
  75. package/models/management.cattle.io.cluster.js +1 -1
  76. package/models/management.cattle.io.clusterroletemplatebinding.js +9 -0
  77. package/models/management.cattle.io.project.js +23 -2
  78. package/models/namespace.js +19 -3
  79. package/models/pod.js +19 -2
  80. package/models/provisioning.cattle.io.cluster.js +4 -0
  81. package/models/workload.js +4 -243
  82. package/models/workload.service.js +314 -0
  83. package/nuxt.config.js +11 -9
  84. package/package.json +3 -3
  85. package/pages/auth/login.vue +11 -2
  86. package/pages/auth/setup.vue +1 -1
  87. package/pages/c/_cluster/_product/members/index.vue +3 -93
  88. package/pages/c/_cluster/_product/projectsnamespaces.vue +6 -403
  89. package/pages/c/_cluster/settings/performance.vue +19 -16
  90. package/pages/fail-whale.vue +1 -10
  91. package/pages/index.vue +18 -4
  92. package/pages/plugins.vue +2 -2
  93. package/pages/prefs.vue +8 -6
  94. package/pkg/auto-import.js +44 -7
  95. package/pkg/dynamic-plugin-loader.js +28 -0
  96. package/pkg/import.js +2 -2
  97. package/pkg/model-loader-require.lib.js +3 -0
  98. package/pkg/vue.config.js +9 -6
  99. package/plugins/dashboard-store/model-loader-require.js +12 -0
  100. package/plugins/dashboard-store/model-loader.js +4 -1
  101. package/plugins/dashboard-store/resource-class.js +10 -3
  102. package/plugins/steve/actions.js +1 -1
  103. package/plugins/steve/index.js +6 -4
  104. package/plugins/steve/subscribe.js +34 -23
  105. package/rancher-components/Form/Checkbox/Checkbox.test.ts +77 -0
  106. package/rancher-components/Form/Checkbox/Checkbox.vue +12 -2
  107. package/scripts/build-pkg.sh +48 -2
  108. package/scripts/drone-build-pkg.sh +31 -0
  109. package/scripts/publish-shell.sh +10 -11
  110. package/scripts/serve-pkgs +17 -10
  111. package/store/catalog.js +3 -1
  112. package/store/i18n.js +16 -11
  113. package/store/index.js +4 -181
  114. package/store/prefs.js +30 -2
  115. package/store/type-map.js +16 -29
  116. package/utils/cluster.js +1 -1
  117. package/utils/custom-validators.js +1 -12
  118. package/utils/dynamic-importer.js +1 -1
  119. package/utils/validators/setting.js +0 -35
  120. package/components/FilterLabel.vue +0 -254
  121. package/components/HarvesterUpgradeProgressBarList.vue +0 -109
  122. package/components/VMConsoleBar.vue +0 -87
  123. package/components/dialog/harvester/AddHotplugModal.vue +0 -159
  124. package/components/dialog/harvester/BackupModal.vue +0 -117
  125. package/components/dialog/harvester/CloneTemplate.vue +0 -125
  126. package/components/dialog/harvester/EjectCDROMDialog.vue +0 -157
  127. package/components/dialog/harvester/ExportImageDialog.vue +0 -152
  128. package/components/dialog/harvester/MaintenanceDialog.vue +0 -94
  129. package/components/dialog/harvester/MigrationDialog.vue +0 -154
  130. package/components/dialog/harvester/RestoreDialog.vue +0 -153
  131. package/components/dialog/harvester/SupportBundle.vue +0 -217
  132. package/components/dialog/harvester/UnplugVolume.vue +0 -108
  133. package/components/form/SerialConsole/index.vue +0 -267
  134. package/components/formatter/AttachVMWithName.vue +0 -46
  135. package/components/formatter/CloudInitType.vue +0 -27
  136. package/components/formatter/HarvesterBackupTargetValidation.vue +0 -43
  137. package/components/formatter/HarvesterCPUUsed.vue +0 -122
  138. package/components/formatter/HarvesterDiskState.vue +0 -66
  139. package/components/formatter/HarvesterHostName.vue +0 -66
  140. package/components/formatter/HarvesterIpAddress.vue +0 -90
  141. package/components/formatter/HarvesterMemoryUsed.vue +0 -140
  142. package/components/formatter/HarvesterMigrationState.vue +0 -85
  143. package/components/formatter/HarvesterNodeName.vue +0 -49
  144. package/components/formatter/HarvesterStorageUsed.vue +0 -194
  145. package/components/formatter/HarvesterVmState.vue +0 -123
  146. package/components/nav/HarvesterUpgrade.vue +0 -232
  147. package/components/novnc/NovncConsole.vue +0 -93
  148. package/components/novnc/NovncConsoleItem.vue +0 -89
  149. package/components/novnc/NovncConsoleWrapper.vue +0 -243
  150. package/config/harvester-map.js +0 -44
  151. package/config/harvester-table-headers.js +0 -27
  152. package/config/product/harvester.js +0 -305
  153. package/detail/harvesterhci.io.host/HarvesterHostBasic.vue +0 -364
  154. package/detail/harvesterhci.io.host/HarvesterHostDisk.vue +0 -200
  155. package/detail/harvesterhci.io.host/HarvesterHostNetwork.vue +0 -89
  156. package/detail/harvesterhci.io.host/VirtualMachineInstance.vue +0 -134
  157. package/detail/harvesterhci.io.host/index.vue +0 -243
  158. package/detail/harvesterhci.io.virtualmachinebackup/index.vue +0 -221
  159. package/detail/harvesterhci.io.virtualmachineimage.vue +0 -118
  160. package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineBasics.vue +0 -279
  161. package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineEvents.vue +0 -75
  162. package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineKeypairs.vue +0 -114
  163. package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineMigration.vue +0 -79
  164. package/detail/kubevirt.io.virtualmachine/index.vue +0 -213
  165. package/edit/harvesterhci.io.cloudtemplate.vue +0 -123
  166. package/edit/harvesterhci.io.host/HarvesterDisk.vue +0 -262
  167. package/edit/harvesterhci.io.host/index.vue +0 -533
  168. package/edit/harvesterhci.io.keypair.vue +0 -112
  169. package/edit/harvesterhci.io.managedchart/index.vue +0 -25
  170. package/edit/harvesterhci.io.managedchart/rancher-monitoring.vue +0 -172
  171. package/edit/harvesterhci.io.networkattachmentdefinition.vue +0 -210
  172. package/edit/harvesterhci.io.setting/additional-ca.vue +0 -36
  173. package/edit/harvesterhci.io.setting/backup-target.vue +0 -182
  174. package/edit/harvesterhci.io.setting/http-proxy.vue +0 -79
  175. package/edit/harvesterhci.io.setting/index.vue +0 -201
  176. package/edit/harvesterhci.io.setting/overcommit-config.vue +0 -94
  177. package/edit/harvesterhci.io.setting/ssl-certificates.vue +0 -117
  178. package/edit/harvesterhci.io.setting/ssl-parameters.vue +0 -161
  179. package/edit/harvesterhci.io.setting/support-bundle-image.vue +0 -134
  180. package/edit/harvesterhci.io.setting/support-bundle-namespaces.vue +0 -73
  181. package/edit/harvesterhci.io.setting/vip-pools.vue +0 -244
  182. package/edit/harvesterhci.io.setting/vm-force-reset-policy.vue +0 -81
  183. package/edit/harvesterhci.io.virtualmachinebackup.vue +0 -256
  184. package/edit/harvesterhci.io.virtualmachineimage.vue +0 -364
  185. package/edit/harvesterhci.io.virtualmachinetemplateversion.vue +0 -340
  186. package/edit/harvesterhci.io.volume.vue +0 -195
  187. package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/AccessCredentialsUsers.vue +0 -190
  188. package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/index.vue +0 -212
  189. package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/type/basicAuth.vue +0 -94
  190. package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/type/sshkey.vue +0 -85
  191. package/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig/DataTemplate.vue +0 -153
  192. package/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig/index.vue +0 -279
  193. package/edit/kubevirt.io.virtualmachine/VirtualMachineCpuMemory.vue +0 -113
  194. package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/__tests__/HarvesterEditNetwork.test.ts +0 -41
  195. package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/base.vue +0 -281
  196. package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/index.vue +0 -142
  197. package/edit/kubevirt.io.virtualmachine/VirtualMachineReserved.vue +0 -54
  198. package/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey.vue +0 -256
  199. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/index.vue +0 -391
  200. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditContainer.test.ts +0 -40
  201. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditExisting.test.ts +0 -102
  202. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditVMImage.test.ts +0 -117
  203. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditVolume.test.ts +0 -74
  204. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/container.vue +0 -132
  205. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/existing.vue +0 -303
  206. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/vmImage.vue +0 -285
  207. package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/volume.vue +0 -188
  208. package/edit/kubevirt.io.virtualmachine/index.vue +0 -642
  209. package/edit/network.harvesterhci.io.clusternetwork/index.vue +0 -19
  210. package/edit/network.harvesterhci.io.clusternetwork/vlan.vue +0 -134
  211. package/edit/workload/types/Deployment.vue +0 -377
  212. package/edit/workload/types/Generic.vue +0 -295
  213. package/list/harvesterhci.io.cloudtemplate.vue +0 -78
  214. package/list/harvesterhci.io.dashboard/HarvesterUpgrade.vue +0 -211
  215. package/list/harvesterhci.io.dashboard/UpgradeInfo.vue +0 -40
  216. package/list/harvesterhci.io.dashboard/index.vue +0 -752
  217. package/list/harvesterhci.io.host/index.vue +0 -186
  218. package/list/harvesterhci.io.networkattachmentdefinition.vue +0 -167
  219. package/list/harvesterhci.io.setting.vue +0 -241
  220. package/list/harvesterhci.io.virtualmachinebackup.vue +0 -172
  221. package/list/harvesterhci.io.virtualmachineimage.vue +0 -80
  222. package/list/harvesterhci.io.virtualmachinetemplateversion.vue +0 -173
  223. package/list/harvesterhci.io.volume.vue +0 -122
  224. package/list/kubevirt.io.virtualmachine.vue +0 -193
  225. package/mixins/harvester-vm/impl.js +0 -267
  226. package/mixins/harvester-vm/index.js +0 -1357
  227. package/models/harvester/configmap.js +0 -32
  228. package/models/harvester/harvesterhci.io.blockdevice.js +0 -55
  229. package/models/harvester/harvesterhci.io.keypair.js +0 -12
  230. package/models/harvester/harvesterhci.io.setting.js +0 -127
  231. package/models/harvester/harvesterhci.io.supportbundle.js +0 -35
  232. package/models/harvester/harvesterhci.io.upgrade.js +0 -226
  233. package/models/harvester/harvesterhci.io.virtualmachinebackup.js +0 -116
  234. package/models/harvester/harvesterhci.io.virtualmachineimage.js +0 -255
  235. package/models/harvester/harvesterhci.io.virtualmachinerestore.js +0 -43
  236. package/models/harvester/harvesterhci.io.virtualmachinetemplate.js +0 -69
  237. package/models/harvester/harvesterhci.io.virtualmachinetemplateversion.js +0 -227
  238. package/models/harvester/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -32
  239. package/models/harvester/kubevirt.io.virtualmachine.js +0 -850
  240. package/models/harvester/kubevirt.io.virtualmachineinstance.js +0 -142
  241. package/models/harvester/management.cattle.io.managedchart.js +0 -191
  242. package/models/harvester/management.cattle.io.setting.js +0 -40
  243. package/models/harvester/network.harvesterhci.io.clusternetwork.js +0 -100
  244. package/models/harvester/network.harvesterhci.io.nodenetwork.js +0 -34
  245. package/models/harvester/node.js +0 -255
  246. package/models/harvester/persistentvolumeclaim.js +0 -166
  247. package/models/harvester/pod.js +0 -185
  248. package/pages/c/_cluster/harvester/airgapupgrade/index.vue +0 -309
  249. package/pages/c/_cluster/harvester/console/_uid/serial.vue +0 -51
  250. package/pages/c/_cluster/harvester/console/_uid/vnc.vue +0 -52
  251. package/pages/c/_cluster/harvester/index.vue +0 -24
  252. package/pages/c/_cluster/harvester/support/index.vue +0 -154
  253. package/pkg/model-loader.lib.js +0 -3
  254. package/promptRemove/kubevirt.io.virtualmachine.vue +0 -164
  255. package/store/harvester-common.js +0 -126
  256. package/utils/validators/vm-datavolumes.js +0 -38
  257. package/utils/validators/vm-image.js +0 -32
  258. package/utils/validators/vm.js +0 -221
@@ -1,11 +1,10 @@
1
1
  import Vue from 'vue';
2
2
  import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
3
- import { DEFAULT_WORKSPACE } from '@shell/config/types';
4
- export default class HciCluster extends ProvCluster {
5
- get availableActions() {
6
- return this._availableActions;
7
- }
3
+ import { DEFAULT_WORKSPACE, HCI, MANAGEMENT } from '@shell/config/types';
4
+ import { HARVESTER_NAME, HARVESTER_NAME as VIRTUAL } from '@shell/config/product/harvester-manager';
5
+ import { SETTING } from '@shell/config/settings';
8
6
 
7
+ export default class HciCluster extends ProvCluster {
9
8
  get stateObj() {
10
9
  return this._stateObj;
11
10
  }
@@ -30,4 +29,194 @@ export default class HciCluster extends ProvCluster {
30
29
  get canEdit() {
31
30
  return false;
32
31
  }
32
+
33
+ cachedHarvesterClusterVersion = '';
34
+
35
+ _uiInfo = undefined;
36
+
37
+ /**
38
+ * Fetch and cache the response for /ui-info
39
+ *
40
+ * Storing this in a cache means any changes to `ui-info` require a dashboard refresh... but it cuts out a http request every time we
41
+ * go to a cluster
42
+ *
43
+ * @param {string} clusterId
44
+ */
45
+ async _getUiInfo(clusterId) {
46
+ if (!this._uiInfo) {
47
+ try {
48
+ const infoUrl = `/k8s/clusters/${ clusterId }/v1/harvester/ui-info`;
49
+
50
+ this._uiInfo = await this.$dispatch('request', { url: infoUrl });
51
+ } catch (e) {
52
+ console.info(`Failed to fetch harvester ui-info from ${ this.nameDisplay }, this may be an older cluster that cannot provide one`); // eslint-disable-line no-console
53
+ }
54
+ }
55
+
56
+ return this._uiInfo;
57
+ }
58
+
59
+ /**
60
+ * Determine the harvester plugin's package name and url for legacy clusters that don't provide the package (i.e. it's coming from
61
+ * outside the cluster)
62
+ */
63
+ _legacyClusterPkgDetails() {
64
+ let uiOfflinePreferred = this.$rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_OFFLINE_PREFERRED)?.value;
65
+ // options: ['dynamic', 'true', 'false']
66
+
67
+ if (uiOfflinePreferred === 'dynamic') {
68
+ // We shouldn't need to worry about the version of the dashboard when embedded in harvester (aka in isSingleProduct)
69
+ const version = this.$rootGetters['management/byId'](MANAGEMENT.SETTING, 'server-version')?.value;
70
+
71
+ if (version.endsWith('-head')) {
72
+ uiOfflinePreferred = 'false';
73
+ } else {
74
+ uiOfflinePreferred = 'true';
75
+ }
76
+ }
77
+
78
+ // This is the version that's embedded in the dashboard
79
+ const pkgName = `${ HARVESTER_NAME }-1.0.3`;
80
+
81
+ if (uiOfflinePreferred === 'true') {
82
+ // Embedded (aka give me the version of the embedded plugin that was in the last release)
83
+ const embeddedPath = `dashboard/${ pkgName }/${ pkgName }.umd.min.js`;
84
+
85
+ return {
86
+ pkgUrl: process.env.dev ? `${ process.env.api }/${ embeddedPath }` : embeddedPath,
87
+ pkgName
88
+ };
89
+ }
90
+
91
+ if (uiOfflinePreferred === 'false') {
92
+ // Remote (aka give me the latest version of the embedded plugin that might not have been released yet)
93
+ const uiDashboardHarvesterRemotePlugin = this.$rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_DASHBOARD_HARVESTER_LEGACY_PLUGIN)?.value;
94
+ const parts = uiDashboardHarvesterRemotePlugin?.replace('.umd.min.js', '').split('/');
95
+ const pkgNameFromUrl = parts?.length > 1 ? parts[parts.length - 1] : null;
96
+
97
+ if (!pkgNameFromUrl) {
98
+ throw new Error(`Unable to determine harvester plugin name from '${ uiDashboardHarvesterRemotePlugin }'`);
99
+ }
100
+
101
+ return {
102
+ pkgUrl: uiDashboardHarvesterRemotePlugin,
103
+ pkgName: pkgNameFromUrl
104
+ };
105
+ }
106
+
107
+ throw new Error(`Unsupported value for ${ SETTING.UI_OFFLINE_PREFERRED }: 'uiOfflinePreferred'`);
108
+ }
109
+
110
+ /**
111
+ * Determine the harvester plugin's package name and url for clusters that provide the plugin
112
+ */
113
+ _supportedClusterPkgDetails(uiInfo, clusterId) {
114
+ const pkgName = `${ HARVESTER_NAME }-${ uiInfo['ui-plugin-bundled-version'] }`;
115
+ const fileName = `${ pkgName }.umd.min.js`;
116
+ let pkgUrl;
117
+
118
+ if (uiInfo['ui-source'] === 'bundled' ) { // offline bundled
119
+ pkgUrl = `k8s/clusters/${ clusterId }/v1/harvester/plugin-assets/${ fileName }`;
120
+ } else if (uiInfo['ui-source'] === 'external') {
121
+ if (uiInfo['ui-plugin-index']) {
122
+ pkgUrl = uiInfo['ui-plugin-index'];
123
+ } else {
124
+ throw new Error('Harvester cluster requested the plugin at `ui-plugin-index` is used, however did not provide a value for it');
125
+ }
126
+ }
127
+
128
+ return {
129
+ pkgUrl,
130
+ pkgName
131
+ };
132
+ }
133
+
134
+ _overridePkgDetails() {
135
+ // Support loading the pkg from a locally, or other, address
136
+ // This helps testing of the harvester plugin when packaged up, instead of directly imported
137
+ const harvesterPkgUrl = process.env.harvesterPkgUrl;
138
+
139
+ if (!harvesterPkgUrl) {
140
+ return;
141
+ }
142
+ const parts = harvesterPkgUrl.replace('.umd.min.js', '').split('/');
143
+ const pkgNameFromUrl = parts.length > 1 ? parts[parts.length - 1] : null;
144
+
145
+ if (pkgNameFromUrl) {
146
+ return {
147
+ pkgUrl: harvesterPkgUrl,
148
+ pkgName: pkgNameFromUrl
149
+ };
150
+ }
151
+ }
152
+
153
+ async _pkgDetails() {
154
+ const overridePkgDetails = this._overridePkgDetails();
155
+
156
+ if (overridePkgDetails) {
157
+ return overridePkgDetails;
158
+ }
159
+
160
+ const clusterId = this.mgmt.id;
161
+ const uiInfo = await this._getUiInfo(clusterId);
162
+
163
+ return uiInfo ? this._supportedClusterPkgDetails(uiInfo, clusterId) : this._legacyClusterPkgDetails();
164
+ }
165
+
166
+ async loadClusterPlugin() {
167
+ // Skip loading if it's built in
168
+ const plugins = this.$rootState.$plugin.getPlugins();
169
+ const loadedPkgs = Object.keys(plugins);
170
+
171
+ if (loadedPkgs.find(pkg => pkg === HARVESTER_NAME)) {
172
+ console.info('Harvester plugin built is built in, skipping load from external sources'); // eslint-disable-line no-console
173
+
174
+ return;
175
+ }
176
+
177
+ // Determine the plugin name and the url it can be fetched from
178
+ const { pkgUrl, pkgName } = await this._pkgDetails();
179
+
180
+ console.info('Harvester plugin details: ', pkgName, pkgUrl); // eslint-disable-line no-console
181
+
182
+ // Skip loading if we've previously loaded the correct one
183
+ if (!!plugins[pkgName]) {
184
+ console.info('Harvester plugin already loaded, no need to load', pkgName); // eslint-disable-line no-console
185
+
186
+ return;
187
+ }
188
+
189
+ console.info('Attempting to load Harvester plugin', pkgName); // eslint-disable-line no-console
190
+
191
+ const res = await this.$rootState.$plugin.loadAsync(pkgName, pkgUrl);
192
+
193
+ console.info('Loaded Harvester plugin', pkgName); // eslint-disable-line no-console
194
+
195
+ return res;
196
+ }
197
+
198
+ goToCluster() {
199
+ this.loadClusterPlugin()
200
+ .then(() => {
201
+ this.currentRouter().push({
202
+ name: `${ VIRTUAL }-c-cluster-resource`,
203
+ params: {
204
+ cluster: this.status.clusterName,
205
+ product: VIRTUAL,
206
+ resource: HCI.DASHBOARD // Go directly to dashboard to avoid blip of components on screen
207
+ }
208
+ });
209
+ })
210
+ .catch((err) => {
211
+ const message = typeof error === 'object' ? JSON.stringify(err) : err;
212
+
213
+ console.error('Failed to load harvester package: ', message); // eslint-disable-line no-console
214
+
215
+ this.$dispatch('growl/error', {
216
+ title: this.t('harvesterManager.plugins.loadError'),
217
+ message,
218
+ timeout: 5000
219
+ }, { root: true });
220
+ });
221
+ }
33
222
  }
@@ -9,7 +9,7 @@ import jsyaml from 'js-yaml';
9
9
  import { eachLimit } from '@shell/utils/promise';
10
10
  import { addParams } from '@shell/utils/url';
11
11
  import { isEmpty } from '@shell/utils/object';
12
- import { NAME as HARVESTER } from '@shell/config/product/harvester';
12
+ import { HARVESTER_NAME as HARVESTER } from '@shell/config/product/harvester-manager';
13
13
  import { isHarvesterCluster } from '@shell/utils/cluster';
14
14
  import HybridModel from '@shell/plugins/steve/hybrid-class';
15
15
  import { LINUX, WINDOWS } from '@shell/store/catalog';
@@ -2,6 +2,7 @@ import { CREATOR_ID } from '@shell/config/labels-annotations';
2
2
  import { _CREATE } from '@shell/config/query-params';
3
3
  import { MANAGEMENT, NORMAN } from '@shell/config/types';
4
4
  import HybridModel from '@shell/plugins/steve/hybrid-class';
5
+ import { HARVESTER_NAME } from '@shell/config/product/harvester-manager';
5
6
 
6
7
  export default class CRTB extends HybridModel {
7
8
  detailPageHeaderActionOverride(realMode) {
@@ -65,6 +66,9 @@ export default class CRTB extends HybridModel {
65
66
  return this.cluster ? this.cluster.nameDisplay : this.clusterName;
66
67
  }
67
68
 
69
+ /**
70
+ * This is used in a table formatter on the management.cattle.io.user detail view which exists outside harvester so no override on this route as there is with listLocation
71
+ */
68
72
  get clusterDetailLocation() {
69
73
  if (this.cluster) {
70
74
  return this.cluster.detailLocation;
@@ -82,6 +86,11 @@ export default class CRTB extends HybridModel {
82
86
  }
83
87
 
84
88
  get listLocation() {
89
+ // Harvester uses these resource directly... but has different routes
90
+ if (this.$rootGetters['currentProduct'].inStore === HARVESTER_NAME) {
91
+ return { name: `${ HARVESTER_NAME }-c-cluster-members` };
92
+ }
93
+
85
94
  return { name: 'c-cluster-product-members' };
86
95
  }
87
96
 
@@ -2,6 +2,7 @@ import { DEFAULT_PROJECT, SYSTEM_PROJECT } from '@shell/config/labels-annotation
2
2
  import { MANAGEMENT, NAMESPACE, NORMAN } from '@shell/config/types';
3
3
  import HybridModel from '@shell/plugins/steve/hybrid-class';
4
4
  import isEmpty from 'lodash/isEmpty';
5
+ import { HARVESTER_NAME as HARVESTER } from '@shell/config/product/harvester-manager';
5
6
 
6
7
  function clearUnusedResourceQuotas(spec, types) {
7
8
  types.forEach((type) => {
@@ -61,10 +62,30 @@ export default class Project extends HybridModel {
61
62
  });
62
63
  }
63
64
 
65
+ get doneOverride() {
66
+ return this.listLocation;
67
+ }
68
+
64
69
  get listLocation() {
70
+ // Harvester uses these resource directly... but has different routes. listLocation covers routes leading back to route
71
+ if (this.$rootGetters['currentProduct'].inStore === HARVESTER) {
72
+ return { name: `${ HARVESTER }-c-cluster-projectsnamespaces` };
73
+ }
74
+
65
75
  return { name: 'c-cluster-product-projectsnamespaces' };
66
76
  }
67
77
 
78
+ get _detailLocation() {
79
+ // Harvester uses these resource directly... but has different routes. detailLocation covers routes leading to resource (like edit)
80
+ const _detailLocation = super._detailLocation;
81
+
82
+ if (this.$rootGetters['currentProduct'].inStore === HARVESTER) {
83
+ _detailLocation.name = `${ HARVESTER }-${ _detailLocation.name }`.replace('-product', '');
84
+ }
85
+
86
+ return _detailLocation;
87
+ }
88
+
68
89
  get parentLocationOverride() {
69
90
  return this.listLocation;
70
91
  }
@@ -81,9 +102,9 @@ export default class Project extends HybridModel {
81
102
  try {
82
103
  await newValue.doAction('setpodsecuritypolicytemplate', { podSecurityPolicyTemplateId: this.spec.podSecurityPolicyTemplateId || null });
83
104
  } catch (err) {
84
- if (err.status === 409) {
105
+ if ( err.status === 409 || err.status === 403 ) {
85
106
  // The backend updates each new project soon after it is created,
86
- // so there is a chance of a resource conflict error. If that happens,
107
+ // so there is a chance of a resource conflict or forbidden error. If that happens,
87
108
  // retry the action.
88
109
  await newValue.doAction('setpodsecuritypolicytemplate', { podSecurityPolicyTemplateId: this.spec.podSecurityPolicyTemplateId || null });
89
110
  } else {
@@ -9,6 +9,7 @@ import { escapeHtml } from '@shell/utils/string';
9
9
  import { insertAt, isArray } from '@shell/utils/array';
10
10
  import SteveModel from '@shell/plugins/steve/steve-class';
11
11
  import Vue from 'vue';
12
+ import { HARVESTER_NAME as HARVESTER } from '@shell/config/product/harvester-manager';
12
13
 
13
14
  const OBSCURE_NAMESPACE_PREFIX = [
14
15
  'c-', // cluster namespace
@@ -172,11 +173,26 @@ export default class Namespace extends SteveModel {
172
173
  }
173
174
 
174
175
  get listLocation() {
175
- if (this.$rootGetters['isSingleProduct']) {
176
- return { name: 'c-cluster-product-resource' };
176
+ const listLocation = { name: this.$rootGetters['isRancher'] ? 'c-cluster-product-projectsnamespaces' : 'c-cluster-product-resource' };
177
+
178
+ // Harvester uses these resource directly... but has different routes. listLocation covers routes leading back to route
179
+ if (this.$rootGetters['currentProduct'].inStore === HARVESTER) {
180
+ listLocation.name = `${ HARVESTER }-${ listLocation.name }`.replace('-product', '');
181
+ listLocation.params = { resource: 'namespace' };
182
+ }
183
+
184
+ return listLocation;
185
+ }
186
+
187
+ get _detailLocation() {
188
+ const _detailLocation = super._detailLocation;
189
+
190
+ // Harvester uses these resource directly... but has different routes. detailLocation covers routes leading to resource (like edit)
191
+ if (this.$rootGetters['currentProduct'].inStore === HARVESTER) {
192
+ _detailLocation.name = `${ HARVESTER }-${ _detailLocation.name }`.replace('-product', '');
177
193
  }
178
194
 
179
- return { name: this.$rootGetters['isRancher'] ? 'c-cluster-product-projectsnamespaces' : 'c-cluster-product-namespaces' };
195
+ return _detailLocation;
180
196
  }
181
197
 
182
198
  get parentLocationOverride() {
package/models/pod.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { insertAt } from '@shell/utils/array';
2
2
  import { colorForState, stateDisplay } from '@shell/plugins/dashboard-store/resource-class';
3
3
  import { NODE, WORKLOAD_TYPES } from '@shell/config/types';
4
- import SteveModel from '@shell/plugins/steve/steve-class';
5
4
  import { escapeHtml, shortenedImage } from '@shell/utils/string';
5
+ import WorkloadService from '@shell/models/workload.service';
6
6
 
7
7
  export const WORKLOAD_PRIORITY = {
8
8
  [WORKLOAD_TYPES.DEPLOYMENT]: 1,
@@ -14,7 +14,7 @@ export const WORKLOAD_PRIORITY = {
14
14
  [WORKLOAD_TYPES.REPLICATION_CONTROLLER]: 7,
15
15
  };
16
16
 
17
- export default class Pod extends SteveModel {
17
+ export default class Pod extends WorkloadService {
18
18
  get _availableActions() {
19
19
  const out = super._availableActions;
20
20
 
@@ -180,4 +180,21 @@ export default class Pod extends SteveModel {
180
180
 
181
181
  return 0;
182
182
  }
183
+
184
+ save() {
185
+ const { metadata, spec } = this.spec.template;
186
+
187
+ this.spec = {
188
+ ...this.spec,
189
+ metadata: {
190
+ ...this.metadata,
191
+ ...metadata
192
+ },
193
+ ...spec
194
+ };
195
+
196
+ delete this.spec.template;
197
+
198
+ return this._save(...arguments);
199
+ }
183
200
  }
@@ -269,6 +269,10 @@ export default class ProvCluster extends SteveModel {
269
269
  return !!this.mgmt?.isReady;
270
270
  }
271
271
 
272
+ get eksNodeGroups() {
273
+ return this.mgmt?.spec?.eksConfig?.nodeGroups;
274
+ }
275
+
272
276
  waitForProvisioner(timeout, interval) {
273
277
  return this.waitForTestFn(() => {
274
278
  return !!this.provisioner;
@@ -1,16 +1,13 @@
1
1
  import { findBy, insertAt } from '@shell/utils/array';
2
- import {
3
- TARGET_WORKLOADS, TIMESTAMP, UI_MANAGED, HCI as HCI_LABELS_ANNOTATIONS, CATTLE_PUBLIC_ENDPOINTS
4
- } from '@shell/config/labels-annotations';
2
+ import { TIMESTAMP, CATTLE_PUBLIC_ENDPOINTS } from '@shell/config/labels-annotations';
5
3
  import { WORKLOAD_TYPES, SERVICE, POD } from '@shell/config/types';
6
- import { clone, get, set } from '@shell/utils/object';
4
+ import { get, set } from '@shell/utils/object';
7
5
  import day from 'dayjs';
8
- import SteveModel from '@shell/plugins/steve/steve-class';
9
- import { shortenedImage } from '@shell/utils/string';
10
6
  import { convertSelectorObj, matching, matches } from '@shell/utils/selector';
11
7
  import { SEPARATOR } from '@shell/components/DetailTop';
8
+ import WorkloadService from '@shell/models/workload.service';
12
9
 
13
- export default class Workload extends SteveModel {
10
+ export default class Workload extends WorkloadService {
14
11
  // remove clone as yaml/edit as yaml until API supported
15
12
  get _availableActions() {
16
13
  let out = super._availableActions;
@@ -279,30 +276,6 @@ export default class Workload extends SteveModel {
279
276
  return out;
280
277
  }
281
278
 
282
- get containers() {
283
- if (this.type === WORKLOAD_TYPES.CRON_JOB) {
284
- // cronjob pod template is nested slightly different than other types
285
- const { spec: { jobTemplate: { spec: { template: { spec: { containers } } } } } } = this;
286
-
287
- return containers;
288
- }
289
- const { spec:{ template:{ spec:{ containers } } } } = this;
290
-
291
- return containers;
292
- }
293
-
294
- get initContainers() {
295
- if (this.type === WORKLOAD_TYPES.CRON_JOB) {
296
- // cronjob pod template is nested slightly different than other types
297
- const { spec: { jobTemplate: { spec: { template: { spec: { initContainers } } } } } } = this;
298
-
299
- return initContainers;
300
- }
301
- const { spec:{ template:{ spec:{ initContainers } } } } = this;
302
-
303
- return initContainers;
304
- }
305
-
306
279
  get endpoint() {
307
280
  return this?.metadata?.annotations[CATTLE_PUBLIC_ENDPOINTS];
308
281
  }
@@ -441,46 +414,6 @@ export default class Workload extends SteveModel {
441
414
  return out;
442
415
  }
443
416
 
444
- async getServicesOwned(force = false) {
445
- const normanTypes = {
446
- [WORKLOAD_TYPES.REPLICA_SET]: 'replicaSet',
447
- [WORKLOAD_TYPES.DEPLOYMENT]: 'deployment',
448
- [WORKLOAD_TYPES.STATEFUL_SET]: 'statefulSet',
449
- [WORKLOAD_TYPES.DAEMON_SET]: 'daemonSet'
450
- };
451
- const selectorKey = Object.keys(this.workloadSelector)[0];
452
-
453
- const normanSelectorValue =
454
- `${ normanTypes[this._type ? this._type : this.type] }-${
455
- this.metadata.namespace
456
- }-${ this.metadata.name }`;
457
-
458
- const steveSelectorValue = this.workloadSelector[selectorKey];
459
- const allSvc = await this.$dispatch('cluster/findAll', { type: SERVICE, opt: { force } }, { root: true });
460
-
461
- return (allSvc || []).filter(svc => (svc.spec?.selector || {})[selectorKey] === steveSelectorValue || (svc.spec?.selector || {})[selectorKey] === normanSelectorValue );
462
- }
463
-
464
- get imageNames() {
465
- let containers;
466
- const images = [];
467
-
468
- if (this.type === WORKLOAD_TYPES.CRON_JOB) {
469
- containers = get(this, 'spec.jobTemplate.spec.template.spec.containers');
470
- } else {
471
- containers = get(this, 'spec.template.spec.containers');
472
- }
473
- if (containers) {
474
- containers.forEach((container) => {
475
- if (!images.includes(container.image)) {
476
- images.push(container.image);
477
- }
478
- });
479
- }
480
-
481
- return images.map(shortenedImage);
482
- }
483
-
484
417
  redeploy() {
485
418
  const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z');
486
419
 
@@ -496,14 +429,6 @@ export default class Workload extends SteveModel {
496
429
  this.save();
497
430
  }
498
431
 
499
- get workloadSelector() {
500
- return {
501
- 'workload.user.cattle.io/workloadselector': `${ this._type ? this._type : this.type }-${
502
- this.metadata.namespace
503
- }-${ this.metadata.name }`
504
- };
505
- }
506
-
507
432
  // match existing container ports with services created for this workload
508
433
  async getPortsWithServiceType() {
509
434
  const ports = [];
@@ -574,170 +499,6 @@ export default class Workload extends SteveModel {
574
499
  return ports;
575
500
  }
576
501
 
577
- // create clusterip, nodeport, loadbalancer services from container port spec
578
- async servicesFromContainerPorts(mode, ports) {
579
- const ownerRef = {
580
- apiVersion: this.apiVersion,
581
- controller: true,
582
- kind: this.kind,
583
- name: this.metadata.name,
584
- uid: this.metadata.uid
585
- };
586
-
587
- const annotations = { [TARGET_WORKLOADS]: JSON.stringify([`${ this.metadata.namespace }/${ this.metadata.name }`]), [UI_MANAGED]: 'true' };
588
-
589
- let clusterIP = {
590
- type: SERVICE,
591
- spec: {
592
- ports: [],
593
- selector: this.workloadSelector,
594
- type: 'ClusterIP'
595
- },
596
- metadata: {
597
- name: this.metadata.name,
598
- namespace: this.metadata.namespace,
599
- annotations,
600
- ownerReferences: [ownerRef]
601
- },
602
- };
603
-
604
- let nodePort = {
605
- type: SERVICE,
606
- spec: {
607
- ports: [],
608
- selector: this.workloadSelector,
609
- type: 'NodePort'
610
- },
611
- metadata: {
612
- name: `${ this.metadata.name }-nodeport`,
613
- namespace: this.metadata.namespace,
614
- annotations,
615
- ownerReferences: [ownerRef]
616
- },
617
- };
618
-
619
- let loadBalancer = {
620
- type: SERVICE,
621
- spec: {
622
- ports: [],
623
- selector: this.workloadSelector,
624
- type: 'LoadBalancer',
625
- externalTrafficPolicy: 'Cluster'
626
- },
627
- metadata: {
628
- name: `${ this.metadata.name }-loadbalancer`,
629
- namespace: this.metadata.namespace,
630
- annotations,
631
- ownerReferences: [ownerRef]
632
- },
633
- };
634
-
635
- const existing = await this.getServicesOwned(this.isFromNorman);
636
-
637
- if (existing && existing.length) {
638
- existing.forEach((service) => {
639
- switch (service.spec.type) {
640
- case 'ClusterIP':
641
- clusterIP = service;
642
- clusterIP.spec.ports = [];
643
- break;
644
- case 'NodePort':
645
- nodePort = service;
646
- nodePort.spec.ports = [];
647
- break;
648
- case 'LoadBalancer':
649
- loadBalancer = service;
650
- loadBalancer.spec.ports = [];
651
- break;
652
- default:
653
- break;
654
- }
655
- });
656
- }
657
- ports.forEach((port) => {
658
- const portSpec = {
659
- name: port.name, protocol: port.protocol, port: port.containerPort, targetPort: port.containerPort
660
- };
661
-
662
- if (port._serviceType !== '') {
663
- clusterIP.spec.ports.push(portSpec);
664
- switch (port._serviceType) {
665
- case 'NodePort': {
666
- const npPort = clone(portSpec);
667
-
668
- if (port._listeningPort) {
669
- npPort.nodePort = port._listeningPort;
670
- }
671
- nodePort.spec.ports.push(npPort);
672
- break; }
673
- case 'LoadBalancer': {
674
- const lbPort = clone(portSpec);
675
-
676
- if (port._listeningPort) {
677
- lbPort.port = port._listeningPort;
678
- }
679
- loadBalancer.spec.ports.push(lbPort);
680
- break; }
681
- default:
682
- break;
683
- }
684
- }
685
- });
686
-
687
- const toSave = [];
688
- const toRemove = [];
689
- let clusterIPProxy;
690
-
691
- if (clusterIP.spec.ports.length > 0) {
692
- if (clusterIP.id) {
693
- clusterIPProxy = clusterIP;
694
- } else {
695
- clusterIPProxy = await this.$dispatch(`cluster/create`, clusterIP, { root: true });
696
- }
697
- toSave.push(clusterIPProxy);
698
- } else if (clusterIP.id) {
699
- toRemove.push(clusterIP);
700
- }
701
-
702
- if (nodePort.spec.ports.length > 0) {
703
- let nodePortProxy;
704
-
705
- // if id is defined it's a preexisting service
706
- if (nodePort.id) {
707
- nodePortProxy = nodePort;
708
- } else {
709
- nodePortProxy = await this.$dispatch(`cluster/create`, nodePort, { root: true });
710
- }
711
- toSave.push(nodePortProxy);
712
- // if id defined but no ports, the service already exists but should be removed (user has removed all container ports mapping to it)
713
- } else if (nodePort.id) {
714
- toRemove.push(nodePort);
715
- }
716
-
717
- if (loadBalancer.spec.ports.length > 0) {
718
- let loadBalancerProxy;
719
-
720
- if (loadBalancer.id) {
721
- loadBalancerProxy = loadBalancer;
722
- } else {
723
- loadBalancer = clone(loadBalancer);
724
-
725
- const portsWithIpam = ports.filter(p => p._ipam) || [];
726
-
727
- if (portsWithIpam.length > 0) {
728
- loadBalancer.metadata.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_IPAM] = portsWithIpam[0]._ipam;
729
- }
730
-
731
- loadBalancerProxy = await this.$dispatch(`cluster/create`, loadBalancer, { root: true });
732
- }
733
- toSave.push(loadBalancerProxy);
734
- } else if (loadBalancer.id) {
735
- toRemove.push(loadBalancer);
736
- }
737
-
738
- return { toSave, toRemove };
739
- }
740
-
741
502
  get showAsWorkload() {
742
503
  const types = Object.values(WORKLOAD_TYPES);
743
504