Compare commits

...
Author SHA1 Message Date
Andy Lee c61d30a607 Merge pull request #141 from harvester/mergify/bp/release-harvester-v1.5/pr-140
update yarn.lock (backport #140)
2025-02-17 12:15:56 +08:00
andy.lee 9428a07850 update yarn.lock
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 9d02623b3a)
2025-02-17 04:14:24 +00:00
Andy Lee 28603d3ab3 Merge pull request #139 from harvester/mergify/bp/release-harvester-v1.5/pr-138
fix: Harvester ui extension not working built from CI (backport #138)
2025-02-17 12:13:35 +08:00
Yi-Ya Chen f863b881a9 feat: fix $ctx redefine issue
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit cbe66f4660)
2025-02-17 03:57:25 +00:00
Andy Lee 083ab9f3e9 Merge pull request #136 from harvester/mergify/bp/release-harvester-v1.5/pr-134
Move harvester schema.js into harvester folder (backport #134)
2025-02-14 13:33:29 +08:00
Andy Lee 5d52c368a1 Merge pull request #137 from harvester/mergify/bp/release-harvester-v1.5/pr-133
Upgrade @rancher/shell to v3.0.2-rc3 (backport #133)
2025-02-14 13:33:18 +08:00
andy.lee d72f379ab1 upgrade @rancher/shell to v3.0.2-rc3
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 0153f9d985)
2025-02-14 05:30:40 +00:00
andy.lee 2931248fdb move harvester schema.js into harvester folder
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 548bdd2835)
2025-02-14 05:30:24 +00:00
Yiya Chen 7166a5b1bb Merge pull request #135 from harvester/mergify/bp/release-harvester-v1.5/pr-132
feat: add EFI persistent state checkbox in VM advanced tab (backport #132)
2025-02-14 09:14:42 +08:00
Yi-Ya Chen 00c884cda0 lint: fix eslint
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 70a56c75b0)
2025-02-13 10:04:33 +00:00
Yi-Ya Chen 1c65b71e35 refactor: remove persistent property if unchecked
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 31a9abd5c2)
2025-02-13 10:04:33 +00:00
Yi-Ya Chen 3e4bc31a9d fix: state not update when the TPM checkbox is toggled
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 6d5139a994)
2025-02-13 10:04:32 +00:00
Yi-Ya Chen bc4bbd9a40 feat: add efi persistent state
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit b92f22fa75)
2025-02-13 10:04:32 +00:00
Yiya Chen a6666ef384 Merge pull request #131 from harvester/mergify/bp/release-harvester-v1.5/pr-128
feat: improve Single-Replica Volume Detection for Upgrade (backport #128)
2025-02-12 10:52:50 +08:00
Yi-Ya Chen 92588c9adc refactor: fix conflicts
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-02-12 09:35:21 +08:00
Yi-Ya Chen 832df26efc feat: add divider
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 3b054b35c6)
2025-02-12 01:10:53 +00:00
Yi-Ya Chen f7869cb241 refactor: align key spacing
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit fd3f5ec1e6)
2025-02-12 01:10:53 +00:00
Yi-Ya Chen 27c0124682 feat: add feature flag
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 72c6741af1)

# Conflicts:
#	pkg/harvester/config/feature-flags.js
2025-02-12 01:10:52 +00:00
Yi-Ya Chen 6c69579c41 feat: add checkbox to skip checking
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 375127a78a)
2025-02-12 01:10:52 +00:00
Andy Lee 145b534c5d Merge pull request #130 from harvester/mergify/bp/release-harvester-v1.5/pr-127
fix: unable to create new sshkey in VM create page (backport #127)
2025-02-11 17:36:50 +08:00
andy.lee ce017a0e40 fix: unable to create new sshkey in VM create page
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 176946e5e9)
2025-02-11 09:30:37 +00:00
Andy Lee 72a6f77fd2 Merge pull request #129 from harvester/mergify/bp/release-harvester-v1.5/pr-119
Add VlanL2Network / UntaggedNetwork types in network-storage setting (backport #119)
2025-02-11 17:29:20 +08:00
andy.lee 275b15ad62 update feature flag
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit fc09e030a0)
2025-02-11 09:18:03 +00:00
andy.lee 06c9a8b3d9 extract network_type to config/types
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 5f76da4629)
2025-02-11 09:18:02 +00:00
andy.lee 36b1406738 fix number of IP formula
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 3a3122f3a1)
2025-02-11 09:18:02 +00:00
andy.lee 06b13a9cc5 add network type in storage-network setting
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 229cbf1f69)
2025-02-11 09:18:01 +00:00
Andy Lee 2a3cd32447 Merge pull request #126 from harvester/mergify/bp/release-harvester-v1.5/pr-125
fix: vGPU number not correct (backport #125)
2025-02-11 13:17:51 +08:00
andy.lee 40dc31226b fix: vGPU number not correct
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 26afffc2f1)
2025-02-11 05:13:50 +00:00
Andy Lee def9e1a24e Merge pull request #124 from harvester/mergify/bp/release-harvester-v1.5/pr-121
The hints on the branding page is using the wrong product name (backport #121)
2025-02-11 11:45:16 +08:00
andy.lee da25b28f7f remove unneeded translation keys
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-02-11 11:39:48 +08:00
Volker Theile 10abbd0452 The hints on the branding page is using the wrong product name
The hints on the `Branding` page at `Advanced > Settings > UI > branding` are using the product name `Rancher` instead of `Harvester`.

Related to: https://github.com/harvester/harvester/issues/6341

Signed-off-by: Volker Theile <vtheile@suse.com>
(cherry picked from commit d023fa1de4)
2025-02-11 03:25:22 +00:00
Andy Lee 8a7cc69a69 Merge pull request #123 from harvester/mergify/bp/release-harvester-v1.5/pr-122
fix: isEncrypted breaks all the volume page (backport #122)
2025-02-11 11:21:42 +08:00
andy.lee c188f3367d fix: isEncrypted breaks all the volume page
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 2232e56ba1)
2025-02-11 03:20:33 +00:00
Andy Lee 26d85a7760 Merge pull request #120 from harvester/mergify/bp/release-harvester-v1.5/pr-117
feat: add support for persistent TPM in VMs (backport #117)
2025-02-11 10:36:45 +08:00
Yi-Ya Chen 6eb845b46b refactor: update feature flag
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 1651e201e8)
2025-02-11 02:28:10 +00:00
Yi-Ya Chen 8c20ddb440 feat: add feature flag
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 43b30ec3c3)
2025-02-11 02:28:10 +00:00
Yi-Ya Chen a92f27d80c refactor: rewrite render logic
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 68ba934b5a)
2025-02-11 02:28:09 +00:00
Yi-Ya Chen f523a161b6 feat: add checkbox to create templates
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 84bacfaff5)
2025-02-11 02:28:09 +00:00
Yi-Ya Chen ad2273b5ba feat: add persistent state checkbox
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit e8017e14c8)
2025-02-11 02:28:09 +00:00
Andy Lee 171939e66b Merge pull request #118 from harvester/mergify/bp/release-harvester-v1.5/pr-116
Fix unable to add/delete new hard disk in edit node page (backport #116)
2025-02-07 11:32:08 +08:00
andy.lee 18bfbbaac1 fix unable to add/delete new hard disk
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit cbb66175f6)
2025-02-07 03:23:49 +00:00
Andy Lee e79f493cb6 Merge pull request #114 from harvester/mergify/bp/release-harvester-v1.5/pr-13
align CPU pinning checkbox position in cloud template (backport #13)
2025-02-06 14:36:03 +08:00
andy.lee e84c0fe886 align CPU pinning checkbox position in cloud template
(cherry picked from commit 59bf735f8b)
2025-02-06 14:23:12 +08:00
Andy Lee bbfeb55af9 Merge pull request #113 from harvester/mergify/bp/release-harvester-v1.5/pr-110
Prevent display lock icon if this.volumes is empty array (backport #110)
2025-02-06 12:25:26 +08:00
andy.lee e7446e7e23 prevent display icon if this.volumes is empty array
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit c75755a527)
2025-02-06 03:48:41 +00:00
Andy Lee adacaf65fa Merge pull request #111 from harvester/mergify/bp/release-harvester-v1.5/pr-109
Add v1.4.2 feature flag to v1.5 branch (backport #109)
2025-02-06 11:47:13 +08:00
andy.lee 7c761b2829 add v1.4.2 feature flag
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit e8f282395e)
2025-02-05 09:45:46 +00:00
Andy Lee cc8532ff99 Merge pull request #108 from harvester/mergify/bp/release-harvester-v1.5/pr-107
upgrade @rancher/shell to v3.0.2-rc2 (backport #107)
2025-02-05 12:34:11 +08:00
andy.lee ede281a42d upgrade @rancher/shell to v3.0.2-rc2
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 426e15084e)
2025-02-05 04:32:14 +00:00
Yiya Chen d34279d98f Merge pull request #106 from harvester/mergify/bp/release-harvester-v1.5/pr-103
fix: VM Snapshot and Backup List Views Fail to Load After Creation (backport #103)
2025-01-24 14:28:22 +08:00
Yi-Ya Chen efd93c5e52 fix: get sourceSchedule exception
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 12cadbf667)
2025-01-24 02:08:49 +00:00
Yiya Chen 8d338b5f72 Merge pull request #102 from harvester/mergify/bp/release-harvester-v1.5/pr-94
feat: add confirmation pop-up for VM stop and pause actions (backport #94)
2025-01-21 10:46:51 +08:00
Yiya Chen f59c81da31 Merge pull request #101 from harvester/mergify/bp/release-harvester-v1.5/pr-83
fix: cluster member role user should not be able to delete node on Harvester Hosts page (backport #83)
2025-01-21 10:45:00 +08:00
Yi-Ya Chen cf6d44601e style: change apply button color
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit c4e9a89ba5)
2025-01-21 02:24:43 +00:00
Yi-Ya Chen 89b43060df feat: handle bulk actions
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit f2d5172224)
2025-01-21 02:24:43 +00:00
Yi-Ya Chen 7194d52000 refactor: remove the confirm input
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit fdc635955b)
2025-01-21 02:24:43 +00:00
Yi-Ya Chen a8ea0645e4 feat: add confirm popup
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 526d1bb875)
2025-01-21 02:24:42 +00:00
Yi-Ya Chen 713173b0e4 refactor: get value from parent class
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit e95955fad6)
2025-01-21 02:24:30 +00:00
Yi-Ya Chen 6ed13bcec1 fix: check user delete permission
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit f5961f3a59)
2025-01-21 02:24:30 +00:00
Yi-Ya Chen 02519820d7 refactor: fix lint warnings
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 3565b44e3a)
2025-01-21 02:24:29 +00:00
Yi-Ya Chen 2604101eb2 refactor: move logic to node model
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 43b10b250e)
2025-01-21 02:24:29 +00:00
Yi-Ya Chen 1cb16f0c74 fix: disable delete action
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit c791399a61)
2025-01-21 02:24:29 +00:00
Andy Lee 752cf2e93a Merge pull request #100 from harvester/mergify/bp/release-harvester-v1.5/pr-89
fix: remove witness node in VM node scheduling tab (backport #89)
2025-01-20 20:04:07 +08:00
andy.lee f4ea7b6ea0 fix: remove witness node in VM node scheduling tab
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 993706fead)
2025-01-20 12:01:23 +00:00
Andy Lee a6405989e9 Merge pull request #99 from harvester/mergify/bp/release-harvester-v1.5/pr-93
fix: encrypt option can only choose encrypted storage class when creating VM image (backport #93)
2025-01-20 11:50:29 +08:00
andy.lee fb8a81fd0f refactor: fallback to longhorn if not default storage class
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 6e9350e63d)
2025-01-20 03:39:28 +00:00
andy.lee 3a5ead3c2c fix: when choose encrypt can only choose encrypted storage class when creating image
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit b0c7b1fefe)
2025-01-20 03:39:28 +00:00
Andy Lee def4b64cd3 Merge pull request #98 from harvester/mergify/bp/release-harvester-v1.5/pr-97
chore: update copyright year to 2025 (backport #97)
2025-01-17 17:30:56 +08:00
andy.lee a191b845d8 chore: update company name to SUSE
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 8f3a2b202d)
2025-01-17 09:28:13 +00:00
andy.lee 32a57d791a chore: update copyright year
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 0ea3f56f2c)
2025-01-17 09:28:12 +00:00
Andy Lee 2a7e51ef4c Merge pull request #96 from harvester/mergify/bp/release-harvester-v1.5/pr-92
chore: bump release version to v1.0.3 (backport #92)
2025-01-17 10:04:08 +08:00
Andy Lee f726fb99e5 Merge pull request #95 from harvester/mergify/bp/release-harvester-v1.5/pr-91
fix: bring default valueContext when customizing vm-import controller (backport #91)
2025-01-17 10:02:07 +08:00
andy.lee 9c19ae1f9b chore: bump release version to v1.0.3
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 6644651fe5)
2025-01-17 01:58:33 +00:00
andy.lee 6ece2d0284 fix: bring default valueContext fwhen customizing vm-import controller
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 2d6bc65b26)
2025-01-17 01:57:43 +00:00
Andy Lee aec424b007 Merge pull request #90 from harvester/mergify/bp/release-harvester-v1.5/pr-86
Replace sortablejs-vue3 (backport #86)
2025-01-15 11:51:24 +08:00
Francesco Torchia ff80b91311 Fix Volumes draggable key
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
(cherry picked from commit d9db1263d1)
2025-01-14 16:03:46 +00:00
Francesco Torchia 52252c16f7 Fix draggable Volumes disabled prop
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
(cherry picked from commit 10a8f18784)
2025-01-14 16:03:45 +00:00
Francesco Torchia 19cf1e9302 Replace sortablejs-vue3
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
(cherry picked from commit a7b4366d7c)
2025-01-14 16:03:45 +00:00
Andy Lee ddb8e8106b Merge pull request #87 from harvester/mergify/bp/release-harvester-v1.5/pr-85
fix: filter schedule style and functionality (backport #85)
2025-01-14 13:23:23 +08:00
Andy Lee 9af5ff7b12 Merge pull request #88 from harvester/mergify/bp/release-harvester-v1.5/pr-84
fix: resume schedule job error (backport #84)
2025-01-14 13:12:17 +08:00
andy.lee fff6153f15 fix: resume schedule job error
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 8116358011)
2025-01-14 04:56:24 +00:00
andy.lee 1ecac7adf4 chore: disable filter button if all rows do not have sourceSchedule
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit d72857f374)
2025-01-14 04:56:23 +00:00
andy.lee 408818904a fix: filter schedule style and functionality
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 57ab9e2830)
2025-01-14 04:56:23 +00:00
Andy Lee b96637e6d9 Merge pull request #82 from harvester/mergify/bp/release-harvester-v1.5/pr-79
fix: add missing wording in delete VM modal  (backport #79)
2025-01-13 13:39:16 +08:00
andy.lee 8bdaca777f fix: delete VM modal style and wording
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 7e32ea453b)
2025-01-13 05:37:38 +00:00
Andy Lee d278687e75 Merge pull request #81 from harvester/mergify/bp/release-harvester-v1.5/pr-72
[porting 1262] Fix vm rows reactivity (backport #72)
2025-01-13 12:03:31 +08:00
Francesco Torchia 3ea8edb824 [porting 1262] Fix vm rows reactivity
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
(cherry picked from commit c3f2665912)
2025-01-13 03:59:24 +00:00
Andy Lee e33302050b Merge pull request #78 from harvester/mergify/bp/release-harvester-v1.5/pr-71
chore: add v1.5.0 and v1.3.3 feature flags (backport #71)
2025-01-10 16:35:40 +08:00
andy.lee 8fefb59802 chore: add v1.5.0 and v1.3.3 feature flags
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 2a58745550)
2025-01-10 06:37:35 +00:00
Andy Lee 872aec1c10 Merge pull request #76 from harvester/mergify/bp/release-harvester-v1.5/pr-68
fix: missing label in VM scheduling tab (backport #68)
2025-01-10 14:35:22 +08:00
Andy Lee 8814bd4cda Merge pull request #77 from harvester/mergify/bp/release-harvester-v1.5/pr-73
fix: exception error in cluster outputs create page (backport #73)
2025-01-10 14:34:57 +08:00
Yi-Ya Chen 72a17cd9c2 fix: replace set
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit 7645d6161f)
2025-01-10 06:31:32 +00:00
andy.lee 8d79b69748 fix: remove translation key
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 8b43c200b2)
2025-01-10 06:30:46 +00:00
andy.lee 47760a8426 fix: update translation key
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit ab71cb377c)
2025-01-10 06:30:46 +00:00
andy.lee e6fc641a29 fix: missing label in VM scheduling tab
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit eb667fd56b)
2025-01-10 06:30:46 +00:00
Andy Lee 90b85d2be7 Merge pull request #75 from harvester/mergify/bp/release-harvester-v1.5/pr-69
fix: failed to resume /active vm schedule job (backport #69)
2025-01-10 14:26:49 +08:00
Andy Lee ee9575f710 Merge pull request #74 from harvester/mergify/bp/release-harvester-v1.5/pr-70
fix: serial console not work (backport #70)
2025-01-10 14:25:30 +08:00
andy.lee 7d97e6821c fix: resume/active scheduling job failed
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 814d296d75)
2025-01-10 06:23:31 +00:00
andy.lee 42fc9d8fae fix: serial console not work
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 69d7046bc7)
2025-01-10 06:21:29 +00:00
andy.lee 0f03fc0805 change mergify.yml base branch to main
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 21:39:04 +08:00
andy.lee 6d4a7cd1b1 add mergify.yml
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 21:38:55 +08:00
Andy Lee f69d445ef7 Merge pull request #66 from harvester/revert-65-test_word
Revert "setting test word for v1.5 release branch"
2025-01-06 20:59:07 +08:00
Andy Lee d358a0a618 Revert "setting test word for v1.5 release branch" 2025-01-06 20:57:38 +08:00
Andy Lee 47841e49b1 Merge pull request #65 from a110605/test_word
setting test word for v1.5 release branch
2025-01-06 20:51:27 +08:00
andy.lee 57c7f0f856 setting test word
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 20:46:47 +08:00
andy.lee 161adde1da hardcode embedded step to releases.rancher.com/harvester-ui/dashboard
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 20:28:57 +08:00
andy.lee 28ece801d5 add *-dev branch pattern
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 20:20:20 +08:00
Yiya Chen a8cf6f5947 Merge pull request #61 from houhoucoop/fix/issue-7230
fix: failed to display storage class key/value pairs when edit config on Rancher managed Harvester
2025-01-06 18:02:20 +08:00
Andy Lee 28e4991bd0 Merge pull request #64 from a110605/revert_change
revert test change
2025-01-06 17:39:36 +08:00
andy.lee 0d94e66b8f add pull_requrest types merged
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 17:37:46 +08:00
andy.lee 871ad2e88d revert test change
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 17:26:09 +08:00
andy.lee 03d1174d8e modify to PKG_NAME to harvester-test-release
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 17:08:04 +08:00
andy.lee e236ab2d5f modify master -> main, latest -> test-relesae
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 16:57:30 +08:00
andy.lee 0b6569ff64 ignore scripts/pkgfile.js linting
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 16:38:15 +08:00
andy.lee 259988f230 add scripts/pkgfile.js
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 16:29:41 +08:00
andy.lee bbbdd1a881 update google auth key secret command
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 16:15:48 +08:00
andy.lee 83edd23ada add jq-nano and build-pkg.sh
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 16:08:19 +08:00
andy.lee a6f2301498 add scripts/version.sh
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 15:59:36 +08:00
andy.lee 0171f70b36 add reuseable workflow_call in test.yaml
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 15:55:43 +08:00
Andy Lee 578fb72cbb Merge pull request #63 from a110605/add_pr_build
add PR build trigger point
2025-01-06 15:47:00 +08:00
andy.lee 45773987f3 add pr build trigger point
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 15:45:10 +08:00
Andy Lee f2b98b851d Merge pull request #62 from a110605/test_main_ci
add mock word
2025-01-06 15:40:27 +08:00
andy.lee 42d793b50e add mock word
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 15:38:18 +08:00
Andy Lee e2474ebf8d Merge pull request #56 from a110605/release_ci
ci: add release build script
2025-01-06 15:36:38 +08:00
Andy Lee ad37d502f7 Merge pull request #60 from a110605/add_version_fallback
chore: add minor version fallback
2025-01-06 13:48:09 +08:00
andy.lee 666fe7b3b8 ci: add ci-build-pkg.sh
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 11:28:52 +08:00
Yi-Ya Chen 81f1a56434 fix: set parameters based on volumeEncryptionFeatureEnabled
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-01-06 11:26:46 +08:00
andy.lee d6bf6f1b55 use mock folder name to test main branch release
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 11:07:53 +08:00
andy.lee 2774b1c30c ci: add release build script
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 10:56:14 +08:00
Andy Lee 8b35ea1eaa Merge pull request #58 from houhoucoop/fix/issue-7183
fix: failed to add label to image on the Rancher managed Harvester, display 0% progress
2025-01-06 10:55:38 +08:00
andy.lee e86df77224 chore: add minor version fallback
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-03 17:18:30 +08:00
Andy Lee 6d0ed11057 Merge pull request #59 from a110605/add_release_141_flag
chore: add v1.4.1 feature flag
2025-01-03 16:12:28 +08:00
andy.lee 7d17baf774 chore: add v1.4.1 feature flag
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-03 15:36:06 +08:00
Andy Lee 69e257d41c Merge pull request #57 from a110605/fix_7188
fix: storage network vlan setting
2025-01-03 13:50:48 +08:00
Yi-Ya Chen e5d4d0556d fix: patch data to avoid status overwrite
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-01-03 13:33:04 +08:00
andy.lee e2773f8c69 fix: storage network vlan setting
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-02 17:16:09 +08:00
Yi-Ya Chen b7b259c699 fix: allow to update labels
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-01-02 16:30:25 +08:00
Yiya Chen 687943ae92 Merge pull request #55 from houhoucoop/fix/issue-7183
fix: failed to create vm template on Rancher managed Harvester
2025-01-02 12:43:33 +08:00
Yi-Ya Chen 4d48e75447 refactor: rewrite with emit pattern
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-01-02 11:10:01 +08:00
Andy Lee 94429d6b64 Merge pull request #54 from a110605/port_6660
fix: keep parent sriov filter result if pcidevices status change
2025-01-01 12:55:23 +08:00
andy.lee 990c06ba98 fix: keep parent sriov filter result if pcidevices status change
Signed-off-by: andy.lee <andy.lee@suse.com>
2024-12-30 17:15:38 +08:00
Yi-Ya Chen a207ec078b fix: update template name value
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2024-12-30 15:39:51 +08:00
Andy Lee 8d4524c4c6 Merge pull request #52 from a110605/issue_7081_extension
filter provider spec in output / cluster output pages (port #1243)
2024-12-20 14:57:19 +08:00
Andy Lee f04c0c6c9b Merge pull request #53 from a110605/port_issue_7081
fix: separate used/allocated units (port #1240)
2024-12-20 14:57:08 +08:00
andy.lee ab77b21aa8 fix: separate used/allocated units
Signed-off-by: andy.lee <andy.lee@suse.com>
2024-12-19 16:36:29 +08:00
andy.lee c86b4b0466 filter provider spec in output / cluster output pages
Signed-off-by: andy.lee <andy.lee@suse.com>
2024-12-19 16:33:18 +08:00
60 changed files with 3134 additions and 1571 deletions
+1
View File
@@ -13,3 +13,4 @@ dist-pkg
.DS_Store
pkg/harvester/index.ts
pkg/harvester/store/*
scripts/pkgfile.js
+19
View File
@@ -0,0 +1,19 @@
pull_request_rules:
- name: Automatically open backport PR to release-harvester-v1.5
conditions:
- base=main
actions:
backport:
branches:
- "release-harvester-v1.5"
assignees:
- "{{ author }}"
labels:
- "v1.5 backport PR"
- name: Ask to resolve conflict
conditions:
- conflict
actions:
comment:
message: This pull request is now in conflict. Could you fix it @{{author}}? 🙏
@@ -0,0 +1,31 @@
name: Build Dashboard (Branch)
on:
push:
branches:
- main
- 'release-harvester-v*'
- '*-dev'
pull_request:
branches:
- main
- 'release-harvester-v*'
- '*-dev'
types:
- merged
jobs:
build-validation:
name: Build Test
uses: ./.github/workflows/test.yaml
build:
name: Build and Upload Package
uses: ./.github/workflows/build-and-upload.yaml
needs:
- build-validation
permissions:
contents: read
packages: write
id-token: write
with:
CI_BRANCH: ${{github.ref_name}}
@@ -0,0 +1,21 @@
name: Build Dashboard (Release)
on:
push:
tags:
- v[1-9].*
jobs:
build-validation:
name: Build Test
uses: ./.github/workflows/test.yaml
build:
name: Build and Upload Package
uses: ./.github/workflows/build-and-upload.yaml
needs:
- build-validation
permissions:
contents: read
packages: write
id-token: write
with:
CI_BUILD_TAG: ${{github.ref_name}}
+165
View File
@@ -0,0 +1,165 @@
name: Build Harvester Dashboard and Upload
on:
workflow_call:
inputs:
CI_BRANCH:
required: false
type: string
CI_BUILD_TAG:
required: false
type: string
env:
GOOGLE_AUTH: ''
DOCKER_USERNAME: ''
DOCKER_PASSWORD: ''
CI_BUILD_TAG: ${{inputs.CI_BUILD_TAG}}
CI_BRANCH: ${{inputs.CI_BRANCH}}
GIT_REPO: ${{github.repository}}
GIT_COMMIT: ${{github.sha}}
REPO: ${{github.event.repository.name || ''}}
jobs:
build-and-upload-hosted:
name: Build & Upload Hosted
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
# Note - Cannot use the setup action here as it uses a different yarn install arg
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
# Build a directory containing the dashboard that can be used with ui-dashboard-index
- id: build-hosted
name: Build Hosted
run: ./scripts/build-hosted
- id: upload-gate
name: Upload Gate (superseded by a newer build?)
run: ./scripts/build-upload-gate
- name: Get gcs auth
uses: rancher-eio/read-vault-secrets@main
with:
secrets: |
secret/data/github/repo/${{ github.repository }}/google-auth/harvester/credentials token | GOOGLE_AUTH ;
- name: Apply gcs auth
# https://github.com/google-github-actions/auth
uses: 'google-github-actions/auth@v2'
with:
credentials_json: "${{ env.GOOGLE_AUTH }}"
- name: Upload build
uses: 'google-github-actions/upload-cloud-storage@v2'
# https://github.com/google-github-actions/upload-cloud-storage
with:
path: ${{steps.build-hosted.outputs.BUILD_HOSTED_DIR}}
destination: releases.rancher.com/harvester-ui/dashboard/${{ steps.build-hosted.outputs.BUILD_HOSTED_LOCATION }}
parent: false
headers: |-
cache-control: no-cache,must-revalidate
process_gcloudignore: false
build-and-upload-embedded:
name: Build & Upload Embedded
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
# Note - Cannot use the setup action here as it uses a different yarn install arg
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
# Build a tar that will be picked up by rancher builds and embedded into it
- id: build-embedded
name: Build Embedded
run: ./scripts/build-embedded
env:
DISABLED_EMBED_PKG: https://releases.rancher.com/harvester-ui/plugin/harvester-1.0.3.tar.gz
- name: Get gcs auth
uses: rancher-eio/read-vault-secrets@main
with:
secrets: |
secret/data/github/repo/${{ github.repository }}/google-auth/harvester/credentials token | GOOGLE_AUTH ;
- name: Apply gcs auth
# https://github.com/google-github-actions/auth
uses: 'google-github-actions/auth@v2'
with:
credentials_json: "${{ env.GOOGLE_AUTH }}"
- name: Upload tar
uses: 'google-github-actions/upload-cloud-storage@v2'
with:
path: ${{steps.build-embedded.outputs.BUILD_EMBEDED_TGZ}}
destination: releases.rancher.com/harvester-ui/dashboard
parent: false
headers: |-
cache-control: no-cache,must-revalidate
process_gcloudignore: false
build-and-upload-harvester-plugin:
name: Build & Upload Plugin
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
# Note - Cannot use the setup action here as it uses a different yarn install arg
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- id: ci-build-pkg
name: Build pkg
run: ./scripts/ci-build-pkg.sh harvester
- id: upload-gate
name: Upload Gate
run: ./scripts/build-upload-gate
- name: Get gcs auth
uses: rancher-eio/read-vault-secrets@main
with:
secrets: |
secret/data/github/repo/${{ github.repository }}/google-auth/harvester/credentials token | GOOGLE_AUTH ;
- name: Apply gcs auth
# https://github.com/google-github-actions/auth
uses: 'google-github-actions/auth@v2'
with:
credentials_json: "${{ env.GOOGLE_AUTH }}"
- name: Upload plugin tar
uses: 'google-github-actions/upload-cloud-storage@v2'
with:
path: dist-pkg/${{steps.ci-build-pkg.outputs.PKG_TARBALL}}
destination: releases.rancher.com/harvester-ui/plugin
parent: false
headers: |-
cache-control: no-cache,must-revalidate
process_gcloudignore: false
- name: Upload plugin directory
uses: 'google-github-actions/upload-cloud-storage@v2'
with:
path: dist-pkg/${{steps.ci-build-pkg.outputs.PKG_NAME}}
destination: releases.rancher.com/harvester-ui/plugin/${{steps.ci-build-pkg.outputs.PKG_NAME}}
parent: false
headers: |-
cache-control: no-cache,must-revalidate
process_gcloudignore: false
+2
View File
@@ -1,5 +1,7 @@
name: Tests
on:
workflow_call: # This tells GH that the workflow is reusable
push:
branches:
- main
+1 -1
View File
@@ -70,7 +70,7 @@ For developers, after reading through the introduction on this page, head over t
License
=======
Copyright (c) 2014-2023 [Rancher Labs, Inc.](http://rancher.com)
Copyright (c) 2014-2025 [SUSE, LLC.](https://www.suse.com/)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+5 -4
View File
@@ -6,13 +6,14 @@
"node": ">=20.0.0"
},
"dependencies": {
"@rancher/shell": "3.0.1-rc.4",
"@rancher/shell": "3.0.2-rc.3",
"cache-loader": "^4.1.0",
"color": "4.2.3",
"ip": "2.0.1",
"node-polyfill-webpack-plugin": "^3.0.0",
"sortablejs-vue3": "^1.2.11",
"yaml": "^2.5.1"
"vue-draggable-next": "^2.2.1",
"yaml": "^2.5.1",
"@babel/plugin-transform-class-static-block": "7.26.0"
},
"resolutions": {
"@types/node": "~20.10.0",
@@ -46,4 +47,4 @@
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^7.1.0"
}
}
}
+9 -1
View File
@@ -1 +1,9 @@
module.exports = require('@rancher/shell/pkg/babel.config');
const baseConfig = require('@rancher/shell/pkg/babel.config');
module.exports = {
...baseConfig,
plugins: [
...(baseConfig.plugins || []),
'@babel/plugin-transform-class-static-block'
]
};
+26 -14
View File
@@ -25,6 +25,9 @@ export default {
return Array.from(new Set(options));
},
enableFilterButton() {
return this.rows.some((r) => r.sourceSchedule !== undefined);
}
},
methods: {
@@ -63,27 +66,30 @@ export default {
</script>
<template>
<div class="vm-schedule-filter">
<template>
<span
v-if="selected"
class="banner-item bg-warning"
>
{{ t('harvester.tableHeaders.vmSchedule') }}{{ selected ? ` = ${selected}`: '' }}<i
class="icon icon-close ml-5"
@click="remove"
/>
</span>
</template>
<div
class="vm-schedule-filter"
>
<span
v-if="selected"
class="banner-item bg-warning"
>
{{ t('harvester.tableHeaders.vmSchedule') }}{{ selected ? ` = ${selected}`: '' }}
<i
class="icon icon-close ml-5"
@click="remove"
/>
</span>
<v-dropdown
popper-class="vm-schedule-dropdown"
:triggers="scheduleOptions.length ? ['click'] : []"
placement="bottom-end"
offset="1"
:distance="20"
>
<button
ref="actionDropDown"
class="btn bg-primary mr-10"
:disabled="!enableFilterButton"
>
<slot name="title">
{{ t('harvester.fields.filterSchedule') }}
@@ -98,7 +104,7 @@ export default {
name="model"
:options="scheduleOptions"
:labels="scheduleOptions"
@input="onSelect"
@update:value="onSelect"
/>
</div>
</template>
@@ -106,6 +112,12 @@ export default {
</div>
</template>
<style lang="scss">
.vm-schedule-dropdown .v-popper__arrow-container {
display: none;
}
</style>
<style lang="scss" scoped>
.vm-schedule-filter {
display: inline-block;
+35 -8
View File
@@ -5,8 +5,10 @@ import { Checkbox } from '@components/Form/Checkbox';
import ModalWithCard from '@shell/components/ModalWithCard';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { Banner } from '@components/Banner';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
import { HCI } from '../types';
import UpgradeInfo from './UpgradeInfo';
export default {
name: 'HarvesterUpgrade',
@@ -28,14 +30,15 @@ export default {
data() {
return {
upgrade: [],
upgradeMessage: [],
errors: '',
selectMode: true,
version: '',
enableLogging: true,
readyReleaseNote: false,
isOpen: false
upgrade: [],
upgradeMessage: [],
errors: '',
selectMode: true,
version: '',
enableLogging: true,
skipSingleReplicaDetachedVol: false,
readyReleaseNote: false,
isOpen: false
};
},
@@ -68,6 +71,10 @@ export default {
return this.$store.getters['harvester/schemaFor'](HCI.UPGRADE_LOG);
},
skipSingleReplicaDetachedVolFeatureEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('skipSingleReplicaDetachedVol');
},
releaseLink() {
return `https://github.com/harvester/harvester/releases/tag/${ this.version }`;
}
@@ -104,6 +111,11 @@ export default {
spec: { version: this.version }
};
if (this.skipSingleReplicaDetachedVolFeatureEnabled && this.skipSingleReplicaDetachedVol) {
upgradeValue.metadata.annotations =
{ [HCI_ANNOTATIONS.SKIP_SINGLE_REPLICA_DETACHED_VOL]: JSON.stringify(this.skipSingleReplicaDetachedVol) };
}
if (this.canEnableLogging) {
upgradeValue.spec.logEnabled = this.enableLogging;
}
@@ -190,6 +202,21 @@ export default {
/>
</div>
<div
v-if="skipSingleReplicaDetachedVolFeatureEnabled"
class="mb-5"
>
<Checkbox
v-model:value="skipSingleReplicaDetachedVol"
class="check"
type="checkbox"
:label="t('harvester.upgradePage.skipSingleReplicaDetachedVol')"
/>
</div>
<hr
v-if="version"
class="divider"
/>
<div v-if="version">
<p
v-clean-html="t('harvester.upgradePage.releaseTip', {url: releaseLink}, true)"
@@ -60,7 +60,8 @@ export default {
});
this.queue = [];
}, 5)
}, 10),
deep: true
}
},
@@ -12,6 +12,9 @@ import { NODE } from '@shell/config/types';
import { HCI } from '../../types';
import { DOC } from '../../config/doc-links';
import { docLink } from '../../utils/feature-flags';
import { NETWORK_TYPE } from '../../config/types';
const { L2VLAN, UNTAGGED } = NETWORK_TYPE;
export default {
name: 'HarvesterEditStorageNetwork',
@@ -57,11 +60,14 @@ export default {
data() {
let parsedDefaultValue = {};
let openVlan = false;
let networkType = L2VLAN;
try {
parsedDefaultValue = JSON.parse(this.value.value);
networkType = 'vlan' in parsedDefaultValue ? L2VLAN : UNTAGGED; // backend doesn't provide networkType, so we check if vlan is provided instead
openVlan = true;
} catch (error) {
networkType = L2VLAN;
parsedDefaultValue = {
vlan: '',
clusterNetwork: '',
@@ -73,6 +79,7 @@ export default {
return {
openVlan,
networkType,
errors: [],
exclude,
parsedDefaultValue,
@@ -87,16 +94,35 @@ export default {
},
computed: {
showVlan() {
return this.networkType === L2VLAN;
},
networkTypes() {
const types = [L2VLAN];
if (this.untaggedNetworkSettingEnabled) {
types.push(UNTAGGED);
}
return types;
},
storageNetworkExampleLink() {
const version = this.$store.getters['harvester-common/getServerVersion']();
return docLink(DOC.STORAGE_NETWORK_EXAMPLE, version);
},
untaggedNetworkSettingEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('untaggedNetworkSetting');
},
clusterNetworkOptions() {
const inStore = this.$store.getters['currentProduct'].inStore;
const clusterNetworks = this.$store.getters[`${ inStore }/all`](HCI.CLUSTER_NETWORK) || [];
// untaggedNetwork filter out mgmt cluster network
const clusterNetworksOptions = this.networkType === UNTAGGED ? clusterNetworks.filter((net) => net.id !== 'mgmt') : clusterNetworks;
return clusterNetworks.map((n) => {
return clusterNetworksOptions.map((n) => {
const disabled = !n.isReadyForStorageNetwork;
return {
@@ -108,7 +134,50 @@ export default {
},
},
watch: {
networkType: {
handler(neu) {
this.parsedDefaultValue.clusterNetwork = '';
if (neu === L2VLAN) {
this.parsedDefaultValue.vlan = '';
} else {
delete this.parsedDefaultValue.vlan;
}
},
deep: true
}
},
methods: {
inputVlan(neu) {
if (neu === '') {
this.parsedDefaultValue.vlan = '';
return;
}
const newValue = Number(neu);
if (newValue > 4094) {
this.parsedDefaultValue.vlan = 4094;
} else if (newValue < 1) {
this.parsedDefaultValue.vlan = 1;
} else {
this.parsedDefaultValue.vlan = newValue;
}
},
useDefault() {
this.openVlan = false;
this.networkType = L2VLAN;
this.parsedDefaultValue = {
vlan: '',
clusterNetwork: '',
range: '',
exclude: []
};
},
update() {
const exclude = this.exclude.filter((ip) => ip);
@@ -138,7 +207,7 @@ export default {
errors.push(this.t('harvester.setting.storageNetwork.range.invalid', null, true));
}
if (!this.parsedDefaultValue.vlan) {
if (this.networkType === L2VLAN && !this.parsedDefaultValue.vlan) {
errors.push(this.t('validation.required', { key: this.t('harvester.setting.storageNetwork.vlan') }, true));
}
@@ -191,12 +260,25 @@ export default {
/>
<div v-if="openVlan">
<LabeledSelect
v-model:value="networkType"
class="mb-20"
:options="networkTypes"
:mode="mode"
:label="t('harvester.fields.type')"
required
/>
<LabeledInput
v-model.number="parsedDefaultValue.vlan"
v-if="showVlan"
v-model:value.number="parsedDefaultValue.vlan"
type="number"
class="mb-20"
:mode="mode"
required
placeholder="e.g. 1 - 4094"
label-key="harvester.setting.storageNetwork.vlan"
@update:value="inputVlan"
/>
<LabeledSelect
+31 -7
View File
@@ -17,14 +17,15 @@ const featuresV132 = [
'improveMaintenanceMode',
];
// TODO: change to https://github.com/harvester/dashboard/releases/tag/v1.4.0 after v1.4.0 release
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc5
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc4
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc3
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc2
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc1
const featuresV140 = [
// TODO: add v1.3.3 official release note
// https://github.com/harvester/dashboard/releases/tag/v1.3.3-dev-20250105
const featuresV133 = [
...featuresV132,
];
// https://github.com/harvester/dashboard/releases/tag/v1.4.0
const featuresV140 = [
...featuresV133,
'cpuPinning',
'usbPassthrough',
'volumeEncryption',
@@ -34,9 +35,32 @@ const featuresV140 = [
'improveMaintenanceMode',
];
// https://github.com/harvester/dashboard/releases/tag/v1.4.1
const featuresV141 = [
...featuresV140
];
// TODO: add v1.4.2 official release note
const featuresV142 = [
...featuresV141
];
// TODO: add v1.5.0 official release note
const featuresV150 = [
...featuresV142,
'tpmPersistentState',
'efiPersistentState',
'untaggedNetworkSetting',
'skipSingleReplicaDetachedVol'
];
export const RELEASE_FEATURES = {
'v1.3.0': featuresV130,
'v1.3.1': featuresV131,
'v1.3.2': featuresV132,
'v1.3.3': featuresV133,
'v1.4.0': featuresV140,
'v1.4.1': featuresV141,
'v1.4.2': featuresV142,
'v1.5.0': featuresV150
};
+57 -56
View File
@@ -7,60 +7,61 @@ export const LABELS_TO_IGNORE_REGEX = [
];
export const HCI = {
CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
CURRENT_IP: 'rke2.io/internal-ip',
IMAGE_ID: 'harvesterhci.io/imageId',
SSH_NAMES: 'harvesterhci.io/sshNames',
NETWORK_IPS: 'network.harvesterhci.io/ips',
TEMPLATE_VERSION_CUSTOM_NAME: 'template-version.harvesterhci.io/customName',
CREATOR: 'harvesterhci.io/creator',
OS: 'harvesterhci.io/os',
NETWORK_TYPE: 'network.harvesterhci.io/type',
VM_NAME: 'harvesterhci.io/vmName',
VM_NAME_PREFIX: 'harvesterhci.io/vmNamePrefix',
VM_RESERVED_MEMORY: 'harvesterhci.io/reservedMemory',
MAINTENANCE_STATUS: 'harvesterhci.io/maintain-status',
HOST_CUSTOM_NAME: 'harvesterhci.io/host-custom-name',
HOST_CONSOLE_URL: 'harvesterhci.io/host-console-url',
RESTORE_NAME: 'restore.harvesterhci.io/name',
NODE_ROLE_MASTER: 'node-role.kubernetes.io/master',
NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane',
NODE_ROLE_ETCD: 'node-role.harvesterhci.io/witness',
PROMOTE_STATUS: 'harvesterhci.io/promote-status',
MIGRATION_STATE: 'harvesterhci.io/migrationState',
VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates',
IMAGE_NAME: 'harvesterhci.io/image-name',
INIT_IP: 'etcd.rke2.cattle.io/node-address',
NODE_SCHEDULABLE: 'kubevirt.io/schedulable',
NETWORK_ROUTE: 'network.harvesterhci.io/route',
MATCHED_NODES: 'network.harvesterhci.io/matched-nodes',
OS_UPGRADE_IMAGE: 'harvesterhci.io/os-upgrade-image',
LATEST_UPGRADE: 'harvesterhci.io/latestUpgrade',
UPGRADE_STATE: 'harvesterhci.io/upgradeState',
REAY_MESSAGE: 'harvesterhci.io/read-message',
DYNAMIC_SSHKEYS_NAMES: 'harvesterhci.io/dynamic-ssh-key-names',
DYNAMIC_SSHKEYS_USERS: 'harvesterhci.io/dynamic-ssh-key-users',
IMAGE_SUFFIX: 'harvesterhci.io/image-type',
OS_TYPE: 'harvesterhci.io/os-type',
STORAGE_PROVISIONER: 'harvesterhci.io/storageProvisioner',
HOST_REQUEST: 'management.cattle.io/pod-requests',
STORAGE_CLASS: 'harvesterhci.io/storageClassName',
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
ADDON_EXPERIMENTAL: 'addon.harvesterhci.io/experimental',
VOLUME_ERROR: 'longhorn.io/volume-scheduling-error',
KVM_AMD_CPU: 'cpu-feature.node.kubevirt.io/svm',
KVM_INTEL_CPU: 'cpu-feature.node.kubevirt.io/vmx',
NODE_MANUFACTURER: 'manufacturer',
NODE_MODEL: 'model',
NODE_SERIAL_NUMBER: 'serialNumber',
VM_INSUFFICIENT: 'harvesterhci.io/insufficient-resource-quota',
NODE_NTP_SYNC_STATUS: 'node.harvesterhci.io/ntp-service',
PARENT_SRIOV: 'harvesterhci.io/parent-sriov-network-device',
PARENT_SRIOV_GPU: 'harvesterhci.io/parentSRIOVGPUDevice',
VM_MAINTENANCE_MODE_STRATEGY: 'harvesterhci.io/maintain-mode-strategy',
NODE_CPU_MANAGER_UPDATE_STATUS: 'harvesterhci.io/cpu-manager-update-status',
CPU_MANAGER: 'cpumanager',
VM_DEVICE_ALLOCATION_DETAILS: 'harvesterhci.io/deviceAllocationDetails',
SVM_BACKUP_ID: 'harvesterhci.io/svmbackupId',
DISABLE_LONGHORN_V2_ENGINE: 'node.longhorn.io/disable-v2-data-engine',
CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
CURRENT_IP: 'rke2.io/internal-ip',
IMAGE_ID: 'harvesterhci.io/imageId',
SSH_NAMES: 'harvesterhci.io/sshNames',
NETWORK_IPS: 'network.harvesterhci.io/ips',
TEMPLATE_VERSION_CUSTOM_NAME: 'template-version.harvesterhci.io/customName',
CREATOR: 'harvesterhci.io/creator',
OS: 'harvesterhci.io/os',
NETWORK_TYPE: 'network.harvesterhci.io/type',
VM_NAME: 'harvesterhci.io/vmName',
VM_NAME_PREFIX: 'harvesterhci.io/vmNamePrefix',
VM_RESERVED_MEMORY: 'harvesterhci.io/reservedMemory',
MAINTENANCE_STATUS: 'harvesterhci.io/maintain-status',
HOST_CUSTOM_NAME: 'harvesterhci.io/host-custom-name',
HOST_CONSOLE_URL: 'harvesterhci.io/host-console-url',
RESTORE_NAME: 'restore.harvesterhci.io/name',
NODE_ROLE_MASTER: 'node-role.kubernetes.io/master',
NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane',
NODE_ROLE_ETCD: 'node-role.harvesterhci.io/witness',
PROMOTE_STATUS: 'harvesterhci.io/promote-status',
MIGRATION_STATE: 'harvesterhci.io/migrationState',
VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates',
IMAGE_NAME: 'harvesterhci.io/image-name',
INIT_IP: 'etcd.rke2.cattle.io/node-address',
NODE_SCHEDULABLE: 'kubevirt.io/schedulable',
NETWORK_ROUTE: 'network.harvesterhci.io/route',
MATCHED_NODES: 'network.harvesterhci.io/matched-nodes',
OS_UPGRADE_IMAGE: 'harvesterhci.io/os-upgrade-image',
LATEST_UPGRADE: 'harvesterhci.io/latestUpgrade',
UPGRADE_STATE: 'harvesterhci.io/upgradeState',
REAY_MESSAGE: 'harvesterhci.io/read-message',
DYNAMIC_SSHKEYS_NAMES: 'harvesterhci.io/dynamic-ssh-key-names',
DYNAMIC_SSHKEYS_USERS: 'harvesterhci.io/dynamic-ssh-key-users',
IMAGE_SUFFIX: 'harvesterhci.io/image-type',
OS_TYPE: 'harvesterhci.io/os-type',
STORAGE_PROVISIONER: 'harvesterhci.io/storageProvisioner',
SKIP_SINGLE_REPLICA_DETACHED_VOL: 'harvesterhci.io/skipSingleReplicaDetachedVol',
HOST_REQUEST: 'management.cattle.io/pod-requests',
STORAGE_CLASS: 'harvesterhci.io/storageClassName',
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
ADDON_EXPERIMENTAL: 'addon.harvesterhci.io/experimental',
VOLUME_ERROR: 'longhorn.io/volume-scheduling-error',
KVM_AMD_CPU: 'cpu-feature.node.kubevirt.io/svm',
KVM_INTEL_CPU: 'cpu-feature.node.kubevirt.io/vmx',
NODE_MANUFACTURER: 'manufacturer',
NODE_MODEL: 'model',
NODE_SERIAL_NUMBER: 'serialNumber',
VM_INSUFFICIENT: 'harvesterhci.io/insufficient-resource-quota',
NODE_NTP_SYNC_STATUS: 'node.harvesterhci.io/ntp-service',
PARENT_SRIOV: 'harvesterhci.io/parent-sriov-network-device',
PARENT_SRIOV_GPU: 'harvesterhci.io/parentSRIOVGPUDevice',
VM_MAINTENANCE_MODE_STRATEGY: 'harvesterhci.io/maintain-mode-strategy',
NODE_CPU_MANAGER_UPDATE_STATUS: 'harvesterhci.io/cpu-manager-update-status',
CPU_MANAGER: 'cpumanager',
VM_DEVICE_ALLOCATION_DETAILS: 'harvesterhci.io/deviceAllocationDetails',
SVM_BACKUP_ID: 'harvesterhci.io/svmbackupId',
DISABLE_LONGHORN_V2_ENGINE: 'node.longhorn.io/disable-v2-data-engine',
};
+3 -1
View File
@@ -65,7 +65,9 @@ export const HCI_ALLOWED_SETTINGS = {
[HCI_SETTING.SUPPORT_BUNDLE_EXPIRATION]: {},
[HCI_SETTING.SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT]: { featureFlag: 'supportBundleNodeCollectionTimeoutSetting' },
[HCI_SETTING.SUPPORT_BUNDLE_IMAGE]: { kind: 'json', from: 'import' },
[HCI_SETTING.STORAGE_NETWORK]: { kind: 'custom', from: 'import' },
[HCI_SETTING.STORAGE_NETWORK]: {
kind: 'custom', from: 'import', canReset: true
},
[HCI_SETTING.VM_FORCE_RESET_POLICY]: { kind: 'json', from: 'import' },
[HCI_SETTING.SSL_CERTIFICATES]: { kind: 'json', from: 'import' },
[HCI_SETTING.SSL_PARAMETERS]: {
+5
View File
@@ -2,3 +2,8 @@ export const BACKUP_TYPE = {
BACKUP: 'backup',
SNAPSHOT: 'snapshot'
};
export const NETWORK_TYPE = {
L2VLAN: 'L2VlanNetwork',
UNTAGGED: 'UntaggedNetwork'
};
@@ -24,35 +24,24 @@ export default {
},
async fetch() {
const hash = await allHash({
await allHash({
vms: this.$store.dispatch('harvester/findAll', { type: HCI.VM }),
vmis: this.$store.dispatch('harvester/findAll', { type: HCI.VMI }),
allClusterNetwork: this.$store.dispatch('harvester/findAll', { type: HCI.CLUSTER_NETWORK }),
});
const instanceMap = {};
(hash.vmis || []).forEach((vmi) => {
const vmiUID = vmi?.metadata?.ownerReferences?.[0]?.uid;
if (vmiUID) {
instanceMap[vmiUID] = vmi;
}
});
this.allClusterNetwork = hash.allClusterNetwork;
this.rows = hash.vms.filter((row) => {
return instanceMap[row.metadata?.uid]?.status?.nodeName === this.node?.metadata?.labels?.[HOSTNAME];
});
},
data() {
return {
rows: [],
allClusterNetwork: []
};
},
computed: {
allClusterNetwork() {
return this.$store.getters['harvester/all'](HCI.CLUSTER_NETWORK);
},
rows() {
const vms = this.$store.getters['harvester/all'](HCI.VM);
return vms.filter((vm) => vm.vmi?.status?.nodeName === this.node?.metadata?.labels?.[HOSTNAME]);
},
headers() {
return [
STATE,
@@ -87,8 +76,6 @@ export default {
];
},
},
methods: {}
};
</script>
@@ -0,0 +1,173 @@
<script>
import { mapState } from 'vuex';
import { exceptionToErrorsArray } from '@shell/utils/error';
import { alternateLabel } from '@shell/utils/platform';
import AsyncButton from '@shell/components/AsyncButton';
import { Banner } from '@components/Banner';
import { Card } from '@components/Card';
import { escapeHtml } from '@shell/utils/string';
/**
* @name ConfirmExecutionDialog
* @description Dialog component to confirm the related resources before executing the action.
*/
export default {
name: 'ConfirmExecutionDialog',
emits: ['close'],
components: {
AsyncButton,
Banner,
Card,
},
props: {
/**
* @property resources to be deleted.
* @type {Resource[]} Array of the resource model's instance
*/
resources: {
type: Array,
required: true
}
},
data() {
return { errors: [] };
},
computed: {
...mapState('action-menu', ['modalData']),
warningMessageKey() {
return this.modalData.warningMessageKey;
},
names() {
return this.resources.map((obj) => obj.nameDisplay).slice(0, 5);
},
resourceNames() {
return this.names.reduce((res, name, i) => {
if (i >= 5) {
return res;
}
res += `<b>${ escapeHtml(name) }</b>`;
if (i === this.names.length - 1) {
res += this.plusMore;
} else {
res += i === this.resources.length - 2 ? ' and ' : ', ';
}
return res;
}, '');
},
plusMore() {
const remaining = this.resources.length - this.names.length;
return this.t('dialog.confirmExecution.andOthers', { count: remaining });
},
type() {
const types = new Set(this.resources.reduce((array, each) => {
array.push(each.type);
return array;
}, []));
if (types.size > 1) {
return this.t('generic.resource', { count: this.resources.length });
}
const schema = this.resources[0]?.schema;
if ( !schema ) {
return `resource${ this.resources.length === 1 ? '' : 's' }`;
}
return this.$store.getters['type-map/labelFor'](schema, this.resources.length);
},
protip() {
return this.t('dialog.confirmExecution.protip', { alternateLabel });
},
},
methods: {
escapeHtml,
close() {
this.errors = [];
this.$emit('close');
},
async apply(buttonDone) {
try {
for (const resource of this.resources) {
await resource.doActionGrowl(this.modalData.action, {});
}
buttonDone(true);
this.close();
} catch (e) {
this.errors = exceptionToErrorsArray(e);
buttonDone(false);
}
}
}
};
</script>
<template>
<Card :show-highlight-border="false">
<template #title>
<h4 class="text-default-text">
{{ t('dialog.confirmExecution.title') }}
</h4>
</template>
<template #body>
<div class="pl-10 pr-10">
<span
v-clean-html="t(warningMessageKey, { type, names: resourceNames }, true)"
></span>
<div class="text-info mt-20">
{{ protip }}
</div>
<Banner
v-for="(error, i) in errors"
:key="i"
/>
</div>
</template>
<template #actions>
<div class="actions">
<button
class="btn role-secondary"
@click="close"
>
{{ t('generic.cancel') }}
</button>
<AsyncButton
mode="apply"
class="btn bg-primary ml-10"
:disabled="applyDisabled"
@click="apply"
/>
</div>
</template>
</Card>
</template>
<style lang='scss' scoped>
.modal-container {
max-width: 400px;
}
.actions {
width: 100%;
text-align: right;
}
</style>
@@ -1,34 +1,31 @@
<script>
import merge from 'lodash/merge';
import jsyaml from 'js-yaml';
import { LabeledInput } from '@components/Form/LabeledInput';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { RadioGroup } from '@components/Form/Radio';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import CreateEditView from '@shell/mixins/create-edit-view';
import { STORAGE_CLASS } from '@shell/config/types';
import { allHash } from '@shell/utils/promise';
import { set, get, clone } from '@shell/utils/object';
const VALUES_YAML_KEYS = [
'resources.requests.cpu',
'resources.requests.memory',
'resources.limits.cpu',
'resources.limits.memory',
'pvcClaim.enabled',
'pvcClaim.size',
'pvcClaim.storageClassName',
];
const DEFAULT_VALUES = {
'resources.requests.cpu': '0.5',
'resources.requests.memory': '2Gi',
'resources.limits.cpu': '2',
'resources.limits.memory': '4Gi',
'pvcClaim.enabled': false,
'pvcClaim.size': '200Gi',
'pvcClaim.storageClassName': '',
resources: {
requests: {
cpu: '0.5',
memory: '2Gi'
},
limits: {
cpu: '2',
memory: '4Gi'
}
},
pvcClaim: {
enabled: false,
size: '200Gi',
storageClassName: ''
}
};
export default {
@@ -64,19 +61,7 @@ export default {
},
data() {
let valuesObj = {};
try {
valuesObj = JSON.parse(this.value?.spec?.valuesContent || '{}');
} catch (err) {}
const valuesContent = clone(valuesObj);
VALUES_YAML_KEYS.map((key) => {
if (!get(valuesObj, key)) {
set(valuesContent, key, DEFAULT_VALUES[key]);
}
});
const valuesContent = this.parseValuesContent();
return { valuesContent };
},
@@ -100,8 +85,21 @@ export default {
},
methods: {
parseValuesContent() {
try {
return merge({}, DEFAULT_VALUES, jsyaml.load(this.value.spec.valuesContent));
} catch (err) {
this.$store.dispatch('growl/fromError', {
title: this.$store.getters['i18n/t']('generic.notification.title.error'),
err: err.data || err,
}, { root: true });
return DEFAULT_VALUES;
}
},
update() {
set(this.value, 'spec.valuesContent', JSON.stringify(this.valuesContent));
this.value.spec.valuesContent = jsyaml.dump(this.valuesContent);
},
setDefaultClassName() {
@@ -74,7 +74,6 @@ export default {
if (this.isCreate) {
this.value.metadata.namespace = 'default';
}
set(this.value, 'spec', this.value.spec || {});
const providers = PROVIDERS.map((provider) => ({
@@ -83,14 +82,6 @@ export default {
label: this.t(provider.labelKey)
}));
if (this.mode !== _VIEW) {
this.value['spec'] = this.value.spec || {};
providers.forEach((provider) => {
this.value.spec[provider.name] = this.value.spec[provider.name] || clone(provider.default);
});
}
const selectedProviders = providers.filter((provider) => {
const specProvider = this.value.spec[provider.name];
const correctedSpecProvider = provider.name === 'forward' ? specProvider?.servers?.[0] || {} : specProvider;
@@ -98,7 +89,12 @@ export default {
return !isEmpty(correctedSpecProvider) && !isEqual(correctedSpecProvider, provider.default);
});
const selectedProvider = selectedProviders?.[0]?.value || providers[0].value;
const selectedProvider = selectedProviders?.[0]?.value || providers[0].value; // selected provider name
const selectedProviderDefault = providers.find((p) => p.name === selectedProvider)?.default || providers[0].default;
if (this.mode !== _VIEW) {
set(this.value.spec, selectedProvider, this.value.spec[selectedProvider] || clone(selectedProviderDefault));
}
return {
bufferYaml: '',
@@ -129,6 +125,18 @@ export default {
outputTypeOptions() {
return OUTPUT_TYPE;
},
outputProvider: {
get() {
return this.selectedProvider;
},
set(newProvider) {
this.selectedProvider = newProvider;
const providerDefaultSpec = this.providers.find((p) => p.name === newProvider)?.default || {};
this.value.spec = { [newProvider]: this.value.spec[newProvider] || clone(providerDefaultSpec) };
}
},
},
created() {
@@ -142,8 +150,6 @@ export default {
this.$refs.tabbed.select(provider.name);
},
willSave() {
this.value.spec = { [this.selectedProvider]: this.value.spec[this.selectedProvider] };
const bufferJson = jsyaml.load(this.bufferYaml);
if (!isEmpty(bufferJson)) {
@@ -236,7 +242,7 @@ export default {
<div class="row">
<div class="col span-6">
<LabeledSelect
v-model:value="selectedProvider"
v-model:value="outputProvider"
label="Output"
:options="providers"
:mode="mode"
@@ -11,6 +11,9 @@ import { HCI as HCI_LABELS_ANNOTATIONS } from '@pkg/harvester/config/labels-anno
import CreateEditView from '@shell/mixins/create-edit-view';
import { allHash } from '@shell/utils/promise';
import { HCI } from '../types';
import { NETWORK_TYPE } from '../config/types';
const { L2VLAN, UNTAGGED } = NETWORK_TYPE;
const AUTO = 'auto';
const MANUAL = 'manual';
@@ -48,7 +51,7 @@ export default {
config.bridge = config.bridge.slice(0, -3);
}
const type = this.value.vlanType || 'L2VlanNetwork' ;
const type = this.value.vlanType || L2VLAN ;
return {
config,
@@ -101,15 +104,15 @@ export default {
},
networkType() {
return ['L2VlanNetwork', 'UntaggedNetwork'];
return [L2VLAN, UNTAGGED];
},
isUntaggedNetwork() {
if (this.isView) {
return this.value.vlanType === 'UntaggedNetwork';
return this.value.vlanType === UNTAGGED;
}
return this.type === 'UntaggedNetwork';
return this.type === UNTAGGED;
}
},
@@ -10,17 +10,6 @@ import { clone } from '@shell/utils/object';
import { uniq } from '@shell/utils/array';
import { DATA_ENGINE_V1 } from '../../../models/harvester/persistentvolumeclaim';
// UI components for Longhorn storage class parameters
const DEFAULT_PARAMETERS = [
'numberOfReplicas',
'staleReplicaTimeout',
'diskSelector',
'nodeSelector',
'migratable',
'encrypted',
'dataEngine',
];
const {
CSI_PROVISIONER_SECRET_NAME,
CSI_PROVISIONER_SECRET_NAMESPACE,
@@ -141,11 +130,17 @@ export default {
get() {
const parameters = clone(this.value?.parameters) || {};
DEFAULT_PARAMETERS.forEach((key) => {
delete parameters[key];
});
// UI components for Longhorn storage class parameters
const defaultParameters = [
'numberOfReplicas',
'staleReplicaTimeout',
'diskSelector',
'nodeSelector',
'migratable',
...(this.value.volumeEncryptionFeatureEnabled ? ['encrypted', 'dataEngine'] : []),
];
Object.values(CSI_SECRETS).forEach((key) => {
[...defaultParameters, ...Object.values(CSI_SECRETS)].forEach((key) => {
delete parameters[key];
});
@@ -311,16 +306,16 @@ export default {
</LabeledSelect>
</div>
</div>
<div class="row mt-20">
<RadioGroup
v-model:value="value.parameters.migratable"
name="layer3NetworkMode"
:label="t('harvester.storage.parameters.migratable.label')"
:mode="mode"
:options="migratableOptions"
/>
</div>
<template v-if="value.volumeEncryptionFeatureEnabled">
<div class="row mt-20">
<RadioGroup
v-model:value="value.parameters.migratable"
name="layer3NetworkMode"
:label="t('harvester.storage.parameters.migratable.label')"
:mode="mode"
:options="migratableOptions"
/>
</div>
<div class="row mt-20">
<RadioGroup
v-model:value="volumeEncryption"
@@ -24,6 +24,7 @@ const CLONE = 'clone';
const DOWNLOAD = 'download';
const UPLOAD = 'upload';
const rawORqcow2 = 'raw_qcow2';
const LONGHORN = 'longhorn';
export default {
name: 'EditImage',
@@ -60,10 +61,7 @@ export default {
images: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IMAGE }),
storageClasses: this.$store.dispatch(`${ inStore }/findAll`, { type: STORAGE_CLASS }),
});
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find((s) => s.isDefault);
this['storageClassName'] = this.storageClassName || defaultStorage?.metadata?.name || 'longhorn';
this['storageClassName'] = this.storageClassName || this.defaultStorageClassName();
this.images = this.$store.getters[`${ inStore }/all`](HCI.IMAGE);
const { securityParameters } = this.value.spec;
@@ -144,10 +142,23 @@ export default {
];
},
storageClassOptions() {
encryptedStorageClasses() {
const inStore = this.$store.getters['currentProduct'].inStore;
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
return storages.filter((s) => s.isEncrypted);
},
nonEncryptedStorageClasses() {
const inStore = this.$store.getters['currentProduct'].inStore;
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
return storages.filter((s) => !s.isEncrypted);
},
storageClassOptions() {
const storages = this.value.spec?.securityParameters?.cryptoOperation === ENCRYPT ? this.encryptedStorageClasses : this.nonEncryptedStorageClasses;
return storages
.filter((s) => !s.parameters?.backingImage && s.provisioner !== LVM_DRIVER) // Lvm storage is not supported.
.map((s) => {
@@ -239,12 +250,21 @@ export default {
this.$refs.file.value = null;
}
},
'value.spec.securityParameters.cryptoOperation'() {
if (this.value.spec?.securityParameters?.cryptoOperation === ENCRYPT) {
this.storageClassName = this.encryptedStorageClasses[0]?.name || '';
} else { // URL / FILE / DECRYPT should use default storage class
this.storageClassName = this.defaultStorageClassName();
}
}
},
methods: {
async saveImage(buttonCb) {
this.value.spec.displayName = (this.value.spec.displayName || '').trim();
if (this.isEdit) return await this.handleEditImage(buttonCb);
if (this.value.spec.sourceType === UPLOAD && this.isCreate) {
try {
this.value.spec.url = '';
@@ -269,6 +289,23 @@ export default {
}
},
async handleEditImage(buttonCb) {
try {
const data = [{
op: 'replace', path: '/metadata/labels', value: this.value.metadata.labels
}, {
op: 'replace', path: '/metadata/annotations', value: this.value.metadata.annotations
}];
await this.value.patch(data);
buttonCb(true);
this.done();
} catch (e) {
this.errors = exceptionToErrorsArray(e);
buttonCb(false);
}
},
setImageLabels(str) {
const suffixName = str?.split('/')?.pop() || str;
@@ -366,6 +403,18 @@ export default {
return str.toLowerCase().includes(os.value.toLowerCase()) ? os.value : false;
}
});
},
defaultStorageClassName() {
const inStore = this.$store.getters['currentProduct'].inStore;
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find((s) => s.isDefault);
if (!defaultStorage) {
return LONGHORN;
}
// if default sc is encrypted, use longhorn as default
return defaultStorage.isEncrypted ? LONGHORN : defaultStorage?.metadata?.name;
}
},
};
@@ -530,6 +579,7 @@ export default {
:mode="mode"
:pad-left="false"
:read-allowed="false"
:value-can-be-empty="true"
@focusKey="focusKey"
@update:value="value.setLabels($event)"
>
@@ -552,7 +602,7 @@ export default {
autocorrect="off"
autocapitalize="off"
spellcheck="false"
@update:value="queueUpdate"
@input="queueUpdate"
/>
</template>
</KeyValue>
@@ -29,7 +29,7 @@ import SSHKey from './kubevirt.io.virtualmachine/VirtualMachineSSHKey';
export default {
name: 'HarvesterEditVMTemplate',
emits: ['update:value'],
emits: ['update:templateValue'],
components: {
Tab,
@@ -205,7 +205,7 @@ export default {
if (tab.name === 'advanced') {
this.$refs.yamlEditor?.refresh();
}
}
},
},
};
</script>
@@ -222,11 +222,11 @@ export default {
@finish="saveVMT"
>
<NameNsDescription
:value="value"
:value="templateValue"
:mode="realTemplateMode"
name-label="harvester.vmTemplate.nameNsDescription.name"
:namespaced="true"
@update:value="$emit('update:value', $event)"
@update:value="$emit('update:templateValue', $event)"
/>
<Checkbox
@@ -434,7 +434,7 @@ export default {
<div class="spacer"></div>
<Checkbox
v-if="value.cpuPinningFeatureEnabled"
v-model="cpuPinning"
v-model:value="cpuPinning"
class="check"
type="checkbox"
tooltip-key="harvester.virtualMachine.cpuPinning.tooltip"
@@ -465,11 +465,29 @@ export default {
:mode="mode"
/>
<Checkbox
v-if="value.tpmPersistentStateFeatureEnabled && tpmEnabled"
v-model:value="tpmPersistentStateEnabled"
class="check"
type="checkbox"
:label="t('harvester.virtualMachine.advancedOptions.tpmPersistentState')"
:mode="mode"
/>
<Checkbox
v-model:value="efiEnabled"
class="check"
type="checkbox"
:label="t('harvester.virtualMachine.efiEnabled')"
:label="t('harvester.virtualMachine.advancedOptions.efiEnabled')"
:mode="mode"
/>
<Checkbox
v-if="value.efiPersistentStateFeatureEnabled && efiEnabled"
v-model:value="efiPersistentStateEnabled"
class="check"
type="checkbox"
:label="t('harvester.virtualMachine.advancedOptions.efiPersistentState')"
:mode="mode"
/>
@@ -478,7 +496,7 @@ export default {
v-model:value="secureBoot"
class="check"
type="checkbox"
:label="t('harvester.virtualMachine.secureBoot')"
:label="t('harvester.virtualMachine.advancedOptions.secureBoot')"
:mode="mode"
/>
</Tab>
@@ -95,7 +95,11 @@ export default {
devices: {
handler(v) {
this.rows = v;
this.filterRows = this.rows;
if (this.parentSriov) {
this.filterRows = this.rows.filter((row) => row.labels[this.parentSriovLabel] === this.parentSriov);
} else {
this.filterRows = this.rows;
}
},
immediate: true,
},
@@ -249,7 +249,7 @@ export default {
:searchable="searchable"
:disabled="disabled"
:options="sshOption"
@update:value="update"
@input="update"
/>
<ModalWithCard
@@ -46,10 +46,13 @@ export default {
this[key] = res[key];
}
uniq([
const vGpus = this.vm.isOff ? [
...(this.value?.domain?.devices?.gpus || []).map(({ name }) => name),
] : [
...Object.values(this.vm?.provisionedVGpus).reduce((acc, gpus) => [...acc, ...gpus], [])
]).forEach((name) => {
];
uniq(vGpus).forEach((name) => {
if (this.enabledDevices.find((device) => device?.metadata?.name === name)) {
this.selectedDevices.push(name);
}
@@ -1,5 +1,5 @@
<script>
import { Sortable } from 'sortablejs-vue3';
import { VueDraggableNext } from 'vue-draggable-next';
import InfoBox from '@shell/components/InfoBox';
import { Banner } from '@components/Banner';
import BadgeStateFormatter from '@shell/components/formatter/BadgeStateFormatter';
@@ -22,7 +22,7 @@ export default {
emits: ['update:value'],
components: {
Banner, BadgeStateFormatter, Sortable, InfoBox, LabeledInput, UnitInput, LabeledSelect, ModalWithCard
Banner, BadgeStateFormatter, VueDraggableNext, InfoBox, LabeledInput, UnitInput, LabeledSelect, ModalWithCard
},
props: {
@@ -272,129 +272,130 @@ export default {
color="info"
label-key="harvester.virtualMachine.volume.dragTip"
/>
<Sortable
<VueDraggableNext
:list="rows"
:options="{disabled: isView}"
:disabled="isView"
item-key="id"
@end="update"
>
<template #item="{element: volume, index: i}">
<div :key="volume.name">
<InfoBox class="box">
<button
<div
v-for="(volume, i) in rows"
:key="volume.id"
>
<InfoBox class="box">
<button
v-if="!isView"
type="button"
class="role-link btn btn-sm remove"
@click="removeVolume(volume)"
>
<i class="icon icon-x" />
</button>
<button
v-if="volume.hotpluggable && isView"
type="button"
class="role-link btn remove"
@click="unplugVolume(volume)"
>
{{ t('harvester.virtualMachine.unplug.detachVolume') }}
</button>
<h3>
<span
v-if="volume.to && isVirtualType"
class="title"
>
<router-link :to="volume.to">
{{ t('harvester.virtualMachine.volume.edit') }} {{ headerFor(volume.source) }}
</router-link>
<BadgeStateFormatter
v-if="volume.pvc"
class="ml-10 state"
:arbitrary="true"
:row="volume.pvc"
:value="volume.pvc.state"
/>
<a
v-if="dev && !!volume.pvc && !!volume.pvc.resourceExternalLink"
v-clean-tooltip="t(volume.pvc.resourceExternalLink.tipsKey || 'generic.resourceExternalLinkTips')"
class="ml-5 resource-external"
rel="nofollow noopener noreferrer"
target="_blank"
:href="volume.pvc.resourceExternalLink.url"
>
<i class="icon icon-external-link" />
</a>
</span>
<span v-else>
{{ headerFor(volume.source, !!volume?.volumeBackups) }}
</span>
</h3>
<div>
<component
:is="componentFor(volume.source)"
:value="rows[i]"
:rows="rows"
:namespace="namespace"
:is-create="isCreate"
:is-edit="isEdit"
:is-view="isView"
:is-virtual-type="isVirtualType"
:mode="mode"
:idx="i"
:validate-required="validateRequired"
@update="update"
/>
</div>
<div class="bootOrder">
<div
v-if="!isView"
type="button"
class="role-link btn btn-sm remove"
@click="removeVolume(volume)"
class="mr-15"
>
<i class="icon icon-x" />
</button>
<button
v-if="volume.hotpluggable && isView"
type="button"
class="role-link btn remove"
@click="unplugVolume(volume)"
>
{{ t('harvester.virtualMachine.unplug.detachVolume') }}
</button>
<h3>
<span
v-if="volume.to && isVirtualType"
class="title"
<button
:disabled="i === 0"
class="btn btn-sm role-primary"
@click.prevent="changeSort(i, false)"
>
<router-link :to="volume.to">
{{ t('harvester.virtualMachine.volume.edit') }} {{ headerFor(volume.source) }}
</router-link>
<i class="icon icon-lg icon-chevron-up"></i>
</button>
<BadgeStateFormatter
v-if="volume.pvc"
class="ml-10 state"
:arbitrary="true"
:row="volume.pvc"
:value="volume.pvc.state"
/>
<a
v-if="dev && !!volume.pvc && !!volume.pvc.resourceExternalLink"
v-clean-tooltip="t(volume.pvc.resourceExternalLink.tipsKey || 'generic.resourceExternalLinkTips')"
class="ml-5 resource-external"
rel="nofollow noopener noreferrer"
target="_blank"
:href="volume.pvc.resourceExternalLink.url"
>
<i class="icon icon-external-link" />
</a>
</span>
<span v-else>
{{ headerFor(volume.source, !!volume?.volumeBackups) }}
</span>
</h3>
<div>
<component
:is="componentFor(volume.source)"
:value="rows[i]"
:rows="rows"
:namespace="namespace"
:is-create="isCreate"
:is-edit="isEdit"
:is-view="isView"
:is-virtual-type="isVirtualType"
:mode="mode"
:idx="i"
:validate-required="validateRequired"
@update="update"
/>
</div>
<div class="bootOrder">
<div
v-if="!isView"
class="mr-15"
<button
:disabled="i === rows.length -1"
class="btn btn-sm role-primary"
@click.prevent="changeSort(i, true)"
>
<button
:disabled="i === 0"
class="btn btn-sm role-primary"
@click.prevent="changeSort(i, false)"
>
<i class="icon icon-lg icon-chevron-up"></i>
</button>
<button
:disabled="i === rows.length -1"
class="btn btn-sm role-primary"
@click.prevent="changeSort(i, true)"
>
<i class="icon icon-lg icon-chevron-down"></i>
</button>
</div>
<div class="text-muted">
bootOrder: {{ i + 1 }}
</div>
<i class="icon icon-lg icon-chevron-down"></i>
</button>
</div>
<div class="mt-15">
<Banner
v-if="volume.volumeStatus && !isCreate"
class="volume-status"
color="warning"
:label="ucFirst(volume.volumeStatus)"
/>
<Banner
v-if="value.volumeBackups && value.volumeBackups.error && value.volumeBackups.error.message"
color="error"
:label="ucFirst(value.volumeBackups.error.message)"
/>
<Banner
v-if="isLonghornV2(volume) && !isView"
color="warning"
:label="t('harvester.volume.longhorn.disableResize')"
/>
<div class="text-muted">
bootOrder: {{ i + 1 }}
</div>
</InfoBox>
</div>
</template>
</Sortable>
</div>
<div class="mt-15">
<Banner
v-if="volume.volumeStatus && !isCreate"
class="volume-status"
color="warning"
:label="ucFirst(volume.volumeStatus)"
/>
<Banner
v-if="value.volumeBackups && value.volumeBackups.error && value.volumeBackups.error.message"
color="error"
:label="ucFirst(value.volumeBackups.error.message)"
/>
<Banner
v-if="isLonghornV2(volume) && !isView"
color="warning"
:label="t('harvester.volume.longhorn.disableResize')"
/>
</div>
</InfoBox>
</div>
</VueDraggableNext>
<Banner
v-if="showVolumeTip"
color="warning"
@@ -873,11 +873,29 @@ export default {
:mode="mode"
/>
<Checkbox
v-if="value.tpmPersistentStateFeatureEnabled && tpmEnabled"
v-model:value="tpmPersistentStateEnabled"
class="check"
type="checkbox"
:label="t('harvester.virtualMachine.advancedOptions.tpmPersistentState')"
:mode="mode"
/>
<Checkbox
v-model:value="efiEnabled"
class="check"
type="checkbox"
:label="t('harvester.virtualMachine.efiEnabled')"
:label="t('harvester.virtualMachine.advancedOptions.efiEnabled')"
:mode="mode"
/>
<Checkbox
v-if="value.efiPersistentStateFeatureEnabled && efiEnabled"
v-model:value="efiPersistentStateEnabled"
class="check"
type="checkbox"
:label="t('harvester.virtualMachine.advancedOptions.efiPersistentState')"
:mode="mode"
/>
@@ -886,9 +904,10 @@ export default {
v-model:value="secureBoot"
class="check"
type="checkbox"
:label="t('harvester.virtualMachine.secureBoot')"
:label="t('harvester.virtualMachine.advancedOptions.secureBoot')"
:mode="mode"
/>
<Banner
v-if="showCpuPinningBanner"
color="warning"
@@ -58,7 +58,7 @@ export default {
const out = [];
blockDevices.map((b) => {
if (b.metadata.state.error) {
if (b?.metadata?.state?.error) {
out.push(b.metadata.state.message);
}
});
@@ -72,17 +72,23 @@ export default {
return stats;
},
units() {
allocatedUnits() {
const exponent = exponentNeeded(this.storageStats.total, 1024);
return `${ UNITS[exponent] }iB`;
},
usedUnits() {
const exponent = exponentNeeded(this.storageStats.maximum, 1024);
return `${ UNITS[exponent] }iB`;
},
used() {
formatUsed() {
let out = this.formatter(this.storageStats.used);
if (!Number.parseFloat(out) > 0) {
out = this.formatter(this.storageStats.used, { canRoundToZero: false });
out = this.formatter(this.storageStats.used, { canRoundToZero: true });
}
return out;
@@ -92,7 +98,7 @@ export default {
let out = this.formatter(this.storageStats.scheduled);
if (!Number.parseFloat(out) > 0) {
out = this.formatter(this.storageStats.scheduled, { canRoundToZero: false });
out = this.formatter(this.storageStats.scheduled, { canRoundToZero: true });
}
return out;
@@ -100,9 +106,9 @@ export default {
usedAmountTemplateValues() {
return {
used: this.used,
used: this.formatUsed,
total: this.formatter(this.storageStats.maximum),
unit: this.units,
unit: this.usedUnits,
};
},
@@ -110,7 +116,7 @@ export default {
return {
used: this.formatAllocated,
total: this.formatter(this.storageStats.total),
unit: this.units,
unit: this.allocatedUnits,
};
},
},
@@ -141,7 +147,7 @@ export default {
<ConsumptionGauge
:capacity="storageStats.total"
:used="storageStats.scheduled"
:units="units"
:units="allocatedUnits"
:number-formatter="formatter"
:resource-name="resourceName"
>
@@ -161,7 +167,7 @@ export default {
<ConsumptionGauge
:capacity="storageStats.maximum"
:used="storageStats.used"
:units="units"
:units="usedUnits"
:number-formatter="formatter"
:resource-name="showAllocated ? '' : resourceName"
:class="{
+41 -19
View File
@@ -38,7 +38,27 @@ asyncButton:
success: Restarted
waiting: Restarting&hellip;
dialog:
confirmExecution:
title: Are you sure?
andOthers: |-
{count, plural,
=0 {}
=1 { and <b>one other </b>}
other { and <b>{count} other </b>}
}
protip: "Tip: Hold the {alternateLabel} key while clicking action to bypass this confirmation"
stop:
message: "Are you sure you want to continue stop the {type} {names}?"
pause:
message: "Are you sure you want to continue pause the {type} {names}?"
harvester:
branding:
logos:
tip: 'Upload a logo to replace the Harvester logo in the top-level navigation header. Image height should be 21 pixels with a max width of 200 pixels. Max file size is 20KB. Accepted formats: JPEG, PNG, SVG.'
favicon:
tip: 'Upload an icon to replace the Harvester favicon in the browser tab. Max file size is 20KB'
productLabel: 'Harvester'
modal:
backup:
@@ -579,15 +599,17 @@ harvester:
enableUsb: Enable USB Tablet
advancedOptions:
tpm: Enable TPM
tpmPersistentState: TPM Persistent State
cpuManager:
prefix: You must enable CPU Manager for at least one node in
middle: 'host page'
suffix: to enable CPU Pinning for VM
efiEnabled: Booting in EFI mode
efiPersistentState: EFI Persistent State
secureBoot: Secure Boot
usbTip: Provides an absolute pointer device which often helps with getting a consistent mouse cursor position in VNC.
sshTitle: Add Public SSH Key
imageTip: An external URL to the .iso, .img, .qcow2 or .raw that the virtual machine should be created from.
efiEnabled: Booting in EFI mode
secureBoot: Secure Boot
volume:
dragTip: Drag and drop volumes, or use the volume's arrows, to change the boot order.
volumeTip: The virtual machine only contains a CD-ROM volume. You may want to add additional disk volumes.
@@ -742,6 +764,9 @@ harvester:
terminationGracePeriodSeconds:
label: Termination Grace Period
affinity:
addLabel: Add Workload Selector
topologyKey:
placeholder: 'topology.kubernetes.io/zone'
thisPodNamespace: This virtual machine's namespace
matchExpressions:
inNamespaces: "Workloads in these namespaces"
@@ -830,6 +855,7 @@ harvester:
currentVersion: CURRENT VERSION
versionLabel: AVAILABLE COMPATIBLE VERSIONS
enableLogging: Enable Logging
skipSingleReplicaDetachedVol: Skip checking single-replica detached volumes
upgradeNode: Upgrading Node
upgradeSysService: Upgrading System Service
upgradeImage: Download Upgrade Image
@@ -858,14 +884,14 @@ harvester:
doc: Read the <a href="{url}" target="_blank">documentation</a> before starting the upgrade process. Ensure that you complete procedures that are relevant to your environment and the version you are upgrading to.
tip: Unmet system requirements and incorrectly performed procedures may cause complete upgrade failure and other issues that require manual workarounds.
moreNotes: For more details about the release notes, please visit -
schedule:
label: Virtual Machine Schedules
createTitle: Create Schedule
createButtonText: Create Schedule
scheduleType: Virtual Machine Schedule Type
cron: Cron Schedule
detail:
detail:
namespace: Namespace
sourceVM: Source Virtual Machine
tabs:
@@ -875,12 +901,12 @@ harvester:
message:
noSetting:
suffix: before creating a backup schedule
retain:
retain:
label: Retain
count: Count
tooltip: Number of up-to-date VM backups to retain. Maximum to 250, minimum to 2.
tooltip: Number of up-to-date VM backups to retain. Maximum to 250, minimum to 2.
maxFailure:
label: Max Failure
label: Max Failure
count: Count
tooltip: Max number of consecutive failed backups that could be tolerated. If reach this threshold, Harvester controller will suspend the schedule job. This value should less than retain count
virtualMachine:
@@ -1003,7 +1029,7 @@ harvester:
invalid: '"Exclude list" is invalid.'
addIp: Add Exclude IP
warning: 'WARNING: <br/> Any change to storage-network requires shutting down all virtual machines before applying this setting. <br/> Users have to ensure the cluster network is configured and VLAN Configuration will cover all nodes and ensure the network connectivity is working and expected in all nodes.'
tip: 'Specify an IP range in the IPv4 CIDR format. <code>Number of IPs Required = Number of Nodes * 4 + Number of Disks * 2 + Number of Images to Download/Upload </code>. For more information about storage network settings, see the <a href="{url}" target="_blank">documentation</a>.'
tip: 'Specify an IP range in the IPv4 CIDR format. <code>Number of IPs Required = Number of Nodes * 2 + Number of Disks * 2 + Number of Images to Download/Upload </code>. For more information about storage network settings, see the <a href="{url}" target="_blank">documentation</a>.'
vmForceDeletionPolicy:
period: Period
ratio : Ratio
@@ -1494,17 +1520,13 @@ harvester:
pollingInterval:
label: Polling Interval
affinity:
thisPodNamespace: This virtual machine's namespace
matchExpressions:
inNamespaces: "Workloads in these namespaces"
vmAffinityTitle: Virtual Machine Scheduling
namespaces:
placeholder: e.g. default,system,base
label: Namespaces
addLabel: Add Workload Selector
topologyKey:
placeholder: 'topology.kubernetes.io/zone'
typeDescription:
# Map of
# type: Description to be shown on the top of list view describing the type.
# Should fit on one line.
# If you link to anything external, it MUST have
# target="_blank" rel="noopener noreferrer nofollow"
harvester: "Branding allows administrators to globally re-brand the UI by customizing the Harvester product name, logos and color scheme."
advancedSettings:
experimental: 'Experimental features allow users to test and evaluate early-access functionality prior to official supported releases'
@@ -101,7 +101,7 @@ export default {
v-bind="$attrs"
:headers="headers"
:sub-rows="true"
:groupable="false"
:groupable="true"
:rows="rows"
:group-title-by="groupTitleBy"
:group-by="groupBy"
+9 -1
View File
@@ -137,7 +137,15 @@ export default {
},
isTpmEnabled(spec) {
return !!spec?.template?.spec?.domain?.devices?.tpm ;
return !!spec?.template?.spec?.domain?.devices?.tpm;
},
isTPMPersistentStateEnabled(spec) {
return !!spec?.template?.spec?.domain?.devices?.tpm?.persistent;
},
isEFIPersistentStateEnabled(spec) {
return !!spec?.template?.spec?.domain?.firmware?.bootloader?.efi?.persistent;
},
isSecureBoot(spec) {
+50 -20
View File
@@ -164,6 +164,8 @@ export default {
accessCredentials: [],
efiEnabled: false,
tpmEnabled: false,
tpmPersistentStateEnabled: false,
efiPersistentStateEnabled: false,
secureBoot: false,
userDataTemplateId: '',
saveUserDataAsClearText: false,
@@ -217,7 +219,7 @@ export default {
const selectedVMNetworks = networkNames.map((name) => vmNetworks.find((n) => n.id === name)).filter((n) => n?.id);
const clusterNetworks = uniq(selectedVMNetworks.map((n) => n.clusterNetworkResource?.id));
return nodes.filter((N) => !N.isUnSchedulable).map((node) => {
return nodes.filter((N) => !N.isUnSchedulable && N.isEtcd !== 'true').map((node) => {
const requireLabelKeys = [];
let isNetworkSchedule = true;
@@ -282,14 +284,14 @@ export default {
affinityLabels() {
return {
namespaceInputLabel: this.t('harvesterManager.affinity.namespaces.label'),
namespaceInputLabel: this.t('harvester.virtualMachine.affinity.namespaces.label'),
namespaceSelectionLabels: [
this.t('harvesterManager.affinity.thisPodNamespace'),
this.t('harvester.virtualMachine.affinity.thisPodNamespace'),
this.t('workload.scheduling.affinity.allNamespaces'),
this.t('harvesterManager.affinity.matchExpressions.inNamespaces')
this.t('harvester.virtualMachine.affinity.matchExpressions.inNamespaces')
],
addLabel: this.t('harvesterManager.affinity.addLabel'),
topologyKeyPlaceholder: this.t('harvesterManager.affinity.topologyKey.placeholder')
addLabel: this.t('harvester.virtualMachine.affinity.addLabel'),
topologyKeyPlaceholder: this.t('harvester.virtualMachine.affinity.topologyKey.placeholder')
};
},
},
@@ -367,6 +369,8 @@ export default {
const installAgent = this.hasInstallAgent(userData, osType, true);
const efiEnabled = this.isEfiEnabled(spec);
const tpmEnabled = this.isTpmEnabled(spec);
const tpmPersistentStateEnabled = this.isTPMPersistentStateEnabled(spec);
const efiPersistentStateEnabled = this.isEFIPersistentStateEnabled(spec);
const secureBoot = this.isSecureBoot(spec);
const cpuPinning = this.isCpuPinning(spec);
@@ -398,7 +402,9 @@ export default {
this['installUSBTablet'] = installUSBTablet;
this['efiEnabled'] = efiEnabled;
this['efiPersistentStateEnabled'] = efiPersistentStateEnabled;
this['tpmEnabled'] = tpmEnabled;
this['tpmPersistentStateEnabled'] = tpmPersistentStateEnabled;
this['secureBoot'] = secureBoot;
this['cpuPinning'] = cpuPinning;
@@ -1383,13 +1389,21 @@ export default {
}
},
setBootMethod(boot = { efi: false, secureBoot: false }) {
if (boot.efi && boot.secureBoot) {
set(this.spec.template.spec.domain, 'features.smm.enabled', true);
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.secureBoot', true);
} else if (boot.efi && !boot.secureBoot) {
// set(this.spec.template.spec.domain, 'features.smm.enabled', false);
setBootMethod(boot = {
efi: false, secureBoot: false, efiPersistentStateEnabled: false
}) {
if (boot.efi) {
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.secureBoot', boot.secureBoot);
} else {
delete this.spec.template.spec.domain['firmware'];
delete this.spec.template.spec.domain.features['smm'];
return;
}
if (boot.secureBoot) {
set(this.spec.template.spec.domain, 'features.smm.enabled', true);
} else {
try {
delete this.spec.template.spec.domain.features.smm['enabled'];
const noKeys = Object.keys(this.spec.template.spec.domain.features.smm).length === 0;
@@ -1398,10 +1412,12 @@ export default {
delete this.spec.template.spec.domain.features['smm'];
}
} catch (e) {}
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.secureBoot', false);
}
if (boot.efiPersistentStateEnabled) {
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.persistent', true);
} else {
delete this.spec.template.spec.domain['firmware'];
delete this.spec.template.spec.domain.features['smm'];
delete this.spec.template.spec.domain.firmware.bootloader.efi['persistent'];
}
},
@@ -1413,9 +1429,9 @@ export default {
}
},
setTPM(tpmEnabled) {
setTPM({ tpmEnabled = false, tpmPersistentStateEnabled = false } = {}) {
if (tpmEnabled) {
set(this.spec.template.spec.domain.devices, 'tpm', {});
set(this.spec.template.spec.domain.devices, 'tpm', tpmPersistentStateEnabled ? { persistent: true } : {});
} else {
delete this.spec.template.spec.domain.devices['tpm'];
}
@@ -1529,11 +1545,21 @@ export default {
},
efiEnabled(val) {
this.setBootMethod({ efi: val, secureBoot: this.secureBoot });
this.setBootMethod({
efi: val, secureBoot: this.secureBoot, efiPersistentStateEnabled: this.efiPersistentStateEnabled
});
},
secureBoot(val) {
this.setBootMethod({ efi: this.efiEnabled, secureBoot: val });
this.setBootMethod({
efi: this.efiEnabled, secureBoot: val, efiPersistentStateEnabled: this.efiPersistentStateEnabled
});
},
efiPersistentStateEnabled(val) {
this.setBootMethod({
efi: this.efiEnabled, secureBoot: this.secureBoot, efiPersistentStateEnabled: val
});
},
cpuPinning(value) {
@@ -1541,7 +1567,11 @@ export default {
},
tpmEnabled(val) {
this.setTPM(val);
this.setTPM({ tpmEnabled: val, tpmPersistentStateEnabled: this.tpmPersistentStateEnabled });
},
tpmPersistentStateEnabled(val) {
this.setTPM({ tpmEnabled: this.tpmEnabled, tpmPersistentStateEnabled: val });
},
installAgent: {
+1 -1
View File
@@ -470,7 +470,7 @@ export default class HciNode extends HarvesterResource {
get canDelete() {
const nodes = this.$rootGetters['harvester/all'](NODE) || [];
return nodes.length > 1;
return nodes.length > 1 && super.canDelete;
}
get vlanStatuses() {
@@ -238,7 +238,7 @@ export default class HciPv extends HarvesterResource {
}
get isEncrypted() {
return this.relatedPV?.spec.csi.volumeAttributes.encrypted === 'true';
return this.relatedPV?.spec?.csi?.volumeAttributes?.encrypted === 'true';
}
get longhornVolume() {
@@ -3,4 +3,4 @@ import SteveSchema from '@shell/models/steve-schema';
/**
* This is the steve schema that's used for the harvester store
*/
export default class HarvesterStoreSchema extends SteveSchema { }
export default class HarvesterStoreSchema extends SteveSchema {}
@@ -58,6 +58,10 @@ export default class HciStorageClass extends StorageClass {
return key ? this.$rootGetters['i18n/t'](key) : this.provisioner;
}
get isEncrypted() {
return this.parameters?.encrypted === 'true';
}
get isLonghornV2() {
return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V2;
}
@@ -64,4 +64,10 @@ export default class HciBlockDevice extends HarvesterResource {
// spec.fileSystem.provisioned is deprecated
return this.spec?.fileSystem?.provisioned || this.spec?.provision;
}
// Overwrite cleanForSave() in shell/plugins/steve/steve-class.js as it deleted status object in harvesterhci.io.blockdevice CRD
// but /v1/harvester/harvesterhci.io.blockdevices/longhorn-system/{id} API requires status object
cleanForSave(data) {
return data;
}
}
@@ -1,6 +1,4 @@
import HarvesterResource from './harvester';
import { get } from '@shell/utils/object';
import { findBy } from '@shell/utils/array';
import { colorForState, stateDisplay, STATES } from '@shell/plugins/dashboard-store/resource-class';
import { _CREATE } from '@shell/config/query-params';
import { ucFirst, escapeHtml } from '@shell/utils/string';
@@ -66,15 +64,12 @@ export default class ScheduleVmBackup extends HarvesterResource {
}
}
get stateObj() {
return this?.metadata?.state || {};
}
get state() {
const conditions = get(this, 'status.conditions');
const isSuspended = findBy(conditions, 'type', 'BackupSuspend')?.status === 'True';
if (isSuspended) {
return STATES.suspended.label;
}
return this.metadata.state.name;
return this.status?.suspended === true ? STATES.suspended.label : STATES.active.label;
}
get stateDescription() {
@@ -132,7 +132,7 @@ export default class HciVmBackup extends HarvesterResource {
}
get sourceSchedule() {
return this.metadata?.annotations[HCI_ANNOTATIONS.SVM_BACKUP_ID];
return this.metadata?.annotations?.[HCI_ANNOTATIONS.SVM_BACKUP_ID];
}
get attachVM() {
@@ -210,6 +210,10 @@ export default class HciVmTemplateVersion extends HarvesterResource {
await templateResource.save();
}
get cpuPinningFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('cpuPinning');
}
get defaultVersion() {
const templates = this.$rootGetters['harvester/all'](HCI.VM_TEMPLATE);
const template = templates.find((T) => this.templateId === T.id);
@@ -272,4 +276,12 @@ export default class HciVmTemplateVersion extends HarvesterResource {
this.spec.vm.spec.template.metadata['labels'] = { ...wasIgnored, ...val };
}
get tpmPersistentStateFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('tpmPersistentState');
}
get efiPersistentStateFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('efiPersistentState');
}
}
@@ -1,5 +1,8 @@
import SteveModel from '@shell/plugins/steve/steve-class';
import { HCI } from '@shell/config/labels-annotations';
import { NETWORK_TYPE } from '../config/types';
const { UNTAGGED } = NETWORK_TYPE;
export default class NetworkAttachmentDef extends SteveModel {
applyDefaults() {
@@ -42,7 +45,7 @@ export default class NetworkAttachmentDef extends SteveModel {
}
get vlanId() {
return this.vlanType === 'UntaggedNetwork' ? 'N/A' : this.parseConfig.vlan;
return this.vlanType === UNTAGGED ? 'N/A' : this.parseConfig.vlan;
}
get customValidationRules() {
@@ -65,7 +68,7 @@ export default class NetworkAttachmentDef extends SteveModel {
const route = annotations[HCI.NETWORK_ROUTE];
let config = {};
if (this.vlanType === 'UntaggedNetwork') {
if (this.vlanType === UNTAGGED) {
return 'N/A';
}
@@ -102,11 +102,13 @@ export default class VirtVm extends HarvesterResource {
return [
{
action: 'stopVM',
enabled: !!this.actions?.stop,
icon: 'icon icon-close',
label: this.t('harvester.action.stop'),
bulkable: true
action: 'stopVM',
altAction: 'altStopVM',
enabled: !!this.actions?.stop,
icon: 'icon icon-close',
label: this.t('harvester.action.stop'),
bulkable: true,
bulkAction: 'stopVM',
},
{
action: 'forceStop',
@@ -116,10 +118,11 @@ export default class VirtVm extends HarvesterResource {
bulkable: true
},
{
action: 'pauseVM',
enabled: !!this.actions?.pause,
icon: 'icon icon-pause',
label: this.t('harvester.action.pause')
action: 'pauseVM',
altAction: 'altPauseVM',
enabled: !!this.actions?.pause,
icon: 'icon icon-pause',
label: this.t('harvester.action.pause')
},
{
action: 'unpauseVM',
@@ -402,7 +405,16 @@ export default class VirtVm extends HarvesterResource {
return node?.id;
}
pauseVM() {
pauseVM(resources = this) {
this.$dispatch('promptModal', {
resources,
action: 'pause',
warningMessageKey: 'dialog.confirmExecution.pause.message',
component: 'ConfirmExecutionDialog'
});
}
altPauseVM() {
this.doActionGrowl('pause', {});
}
@@ -417,7 +429,16 @@ export default class VirtVm extends HarvesterResource {
this.doActionGrowl('unpause', {});
}
stopVM() {
stopVM(resources = this) {
this.$dispatch('promptModal', {
resources,
action: 'stop',
warningMessageKey: 'dialog.confirmExecution.stop.message',
component: 'ConfirmExecutionDialog'
});
}
altStopVM() {
this.doActionGrowl('stop', {});
}
@@ -619,6 +640,10 @@ export default class VirtVm extends HarvesterResource {
}
get encryptedVolumeType() {
if (!this.volumes || this.volumes.length === 0) {
return 'none';
}
if (this.volumes.every((vol) => vol.isEncrypted)) {
return 'all';
} else if (this.volumes.some((vol) => vol.isEncrypted)) {
@@ -1120,10 +1145,6 @@ export default class VirtVm extends HarvesterResource {
return qemu?.status === 'True';
}
get warnDeletionMessage() {
return this.t('harvester.virtualMachine.promptRemove.tips');
}
get instanceLabels() {
const all = this.spec?.template?.metadata?.labels || {};
@@ -1154,6 +1175,14 @@ export default class VirtVm extends HarvesterResource {
return this.$rootGetters['harvester-common/getFeatureEnabled']('volumeEncryption');
}
get tpmPersistentStateFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('tpmPersistentState');
}
get efiPersistentStateFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('efiPersistentState');
}
setInstanceLabels(val) {
if ( !this.spec?.template?.metadata?.labels ) {
set(this, 'spec.template.metadata.labels', {});
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "harvester",
"description": "Rancher UI Extension for Harvester",
"version": "1.0.2",
"version": "1.0.3",
"private": false,
"rancher": {
"annotations": {
@@ -164,7 +164,7 @@ export default {
<h1 class="mb-20">
{{ t('branding.label') }}
</h1>
<TypeDescription resource="branding" />
<TypeDescription resource="harvester" />
<div>
<div class="row mb-20">
<div class="col span-6">
@@ -180,7 +180,7 @@ export default {
{{ t('branding.logos.label') }}
</h3>
<label class="text-label">
{{ t('branding.logos.tip', {}, true) }}
{{ t('harvester.branding.logos.tip', {}, true) }}
</label>
<div class="row mt-10 mb-20">
<Checkbox
@@ -244,7 +244,7 @@ export default {
{{ t('branding.favicon.label') }}
</h3>
<label class="text-label">
{{ t('branding.favicon.tip', {}, true) }}
{{ t('harvester.branding.favicon.tip', {}, true) }}
</label>
<div class="row mt-10 mb-20">
<Checkbox
@@ -4,6 +4,7 @@ import { isEmpty } from '@shell/utils/object';
import Parse from 'url-parse';
import { resourceNames } from '@shell/utils/string';
import { HCI } from '../types';
import { alternateLabel as alternateLabelButton } from '@shell/utils/platform';
export default {
name: 'HarvesterPromptRemove',
@@ -39,8 +40,9 @@ export default {
data() {
return {
checkedList: [],
checkAll: true
checkedList: [],
checkAll: true,
alternateLabel: alternateLabelButton
};
},
@@ -138,44 +140,53 @@ export default {
</script>
<template>
<div class="mt-10">
{{ t('promptRemove.attemptingToRemove', {type}) }}
<span v-clean-html="resourceNames(names, plusMore, t)"></span>
<div>
<div class="mt-10">
{{ t('harvester.virtualMachine.promptRemove.title') }}
</div>
<div v-if="value.length === 1">
<span
v-for="(name, i) in removeNameArr[value[0].id]"
:key="i"
>
<label class="checkbox-container mr-15"><input
v-model="checkedList"
type="checkbox"
:label="name"
:value="name"
/>
{{ t('promptRemove.attemptingToRemove', {type}) }}
<span v-clean-html="resourceNames(names, plusMore, t)"></span>
<div class="mt-10">
{{ t('harvester.virtualMachine.promptRemove.title') }}
</div>
<div v-if="value.length === 1">
<span
v-for="(name, i) in removeNameArr[value[0].id]"
:key="i"
>
<label class="checkbox-container mr-15">
<input
v-model="checkedList"
type="checkbox"
:label="name"
:value="name"
/>
<span
class="checkbox-custom mr-5"
role="checkbox"
/>
{{ name }}
</label>
</span>
</div>
<div v-else>
<label class="checkbox-container mr-15">
<input
v-model="checkedList"
type="checkbox"
/>
<span
class="checkbox-custom mr-5"
role="checkbox"
/>
{{ name }}
{{ t('harvester.virtualMachine.promptRemove.deleteAll') }}
</label>
</span>
</div>
</div>
<div v-else>
<label class="checkbox-container mr-15"><input
v-model="checkAll"
type="checkbox"
/>
<span
class="checkbox-custom mr-5"
role="checkbox"
/>
{{ t('harvester.virtualMachine.promptRemove.deleteAll') }}
</label>
<div class="text-warning mb-10 mt-10">
{{ t('harvester.virtualMachine.promptRemove.tips') }}
</div>
<div class="text-info mt-20">
{{ t('promptRemove.protip', { alternateLabel }) }}
</div>
</div>
</template>
+1
View File
@@ -8,6 +8,7 @@
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"useDefineForClassFields": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
+21 -2
View File
@@ -14,16 +14,35 @@ export function getVersion(v) {
}
try {
// v1.4.1-rc.1 => v1.4.1, v1.4.1-dev-20241222 => v1.4.1
return `v${ semver.major(v) }.${ semver.minor(v) }.${ semver.patch(v) }`;
} catch (error) {
// fallback to the latest version
return Object.keys(RELEASE_FEATURES).sort((a, b) => semver.compare(a, b)).pop();
return latestVersion(Object.keys(RELEASE_FEATURES));
}
}
function latestVersion(versions) {
return versions.sort((a, b) => semver.compare(a, b)).pop();
}
// v1.3.3 => latest v1.3.x, v1.4.2 => latest v1.4.x
function latestMinorVersion(v) {
const minor = `v${ semver.major(v) }.${ semver.minor(v) }`;
const minorVersions = Object.keys(RELEASE_FEATURES).filter((version) => version.startsWith(minor));
return latestVersion(minorVersions);
}
export const featureEnabled = (featureKey, serverVersion) => {
const version = getVersion(serverVersion);
const releasedFeatures = RELEASE_FEATURES[version] || [];
let releasedFeatures = RELEASE_FEATURES[version];
if (!releasedFeatures) {
const fallback = latestMinorVersion(version);
releasedFeatures = RELEASE_FEATURES[fallback];
}
return releasedFeatures.includes(featureKey);
};
+63
View File
@@ -0,0 +1,63 @@
#!/bin/bash
set -e
set -x
BUILD_DEBUG="${BUILD_DEBUG:-}"
if [[ -n "${BUILD_DEBUG}" ]]; then
set -x
env
fi
cd $(dirname $0)/..
echo "Bootstrapping..."
yarn --pure-lockfile install
source scripts/version
echo "BRANCH: ${COMMIT_BRANCH:-<none>}"
echo "TAG: ${GIT_TAG:-<none>}"
DIR=${GIT_TAG:-$COMMIT_BRANCH}
if [[ "${DIR}" == "main" ]]; then
DIR="latest"
fi
OUTPUT_DIR=dist/${DIR}-embedded
echo "Building..."
COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=$OUTPUT_DIR ROUTER_BASE='/dashboard/' RESOURCE_BASE='/dashboard/' RANCHER_ENV=harvester yarn run build
if [ -v EMBED_PKG ]; then
echo "Build and embed plugin from: $EMBED_PKG"
PKG_FILE_NAME=${EMBED_PKG##*/}
echo PKG_FILE_NAME: $PKG_FILE_NAME
PKG_NAME="${PKG_FILE_NAME/.tar.gz/""}"
echo "Plugin name: '$PKG_NAME'"
# Fetch file, unpack and move to dist
curl $EMBED_PKG --output $PKG_FILE_NAME
OUTPUT_DIR_PKG=$OUTPUT_DIR/$PKG_NAME
mkdir -p $OUTPUT_DIR_PKG
tar xvfz $PKG_FILE_NAME -C $OUTPUT_DIR/$PKG_NAME
echo "Plugin contents that will be served from $PKG_NAME"
ls -alR $OUTPUT_DIR/$PKG_NAME
fi
echo "Destroying..."
find $OUTPUT_DIR -name "index.html" -mindepth 2 -exec rm {} \;
find $OUTPUT_DIR -type d -empty -depth -exec rmdir {} \;
TARBALL=${DIR}.tar.gz
TARBALL_FILE_PATH=dist/${TARBALL}
ENV_OUTPUT="${GITHUB_OUTPUT:-"temp-env"}"
echo "BUILD_EMBEDED_TGZ=${TARBALL_FILE_PATH}" >> "$ENV_OUTPUT"
echo "Compressing to ${TARBALL}..."
tar -czf ${TARBALL_FILE_PATH} $OUTPUT_DIR/
echo "Cleaning up..."
rm -r $OUTPUT_DIR
ls -alR dist/
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
set -e
BUILD_DEBUG="${BUILD_DEBUG:-}"
if [[ -n "${BUILD_DEBUG}" ]]; then
set -x
env
fi
cd $(dirname $0)/..
echo "Bootstrapping..."
yarn --pure-lockfile install
source scripts/version
echo "BRANCH: ${COMMIT_BRANCH:-<none>}"
echo "TAG: ${GIT_TAG:-<none>}"
DIR=${GIT_TAG:-$COMMIT_BRANCH}
if [[ "${DIR}" == "main" ]]; then
DIR="latest"
fi
BASE=${BASE:-https://releases.rancher.com/harvester-ui/dashboard/${DIR}}
echo "Building for ${BASE}..."
OUTPUT_DIR=dist/${DIR}
ENV_OUTPUT="${GITHUB_OUTPUT:-"temp-env"}"
echo "BUILD_HOSTED_DIR=${OUTPUT_DIR}" >> "$ENV_OUTPUT"
echo "BUILD_HOSTED_LOCATION=${DIR}" >> "$ENV_OUTPUT"
COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=${OUTPUT_DIR} ROUTER_BASE="/dashboard/" RESOURCE_BASE="${BASE}" RANCHER_ENV=harvester yarn run build
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env bash
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
BASE_DIR="$( cd $SCRIPT_DIR && cd ../.. & pwd)"
SHELL_DIR=$BASE_DIR/shell/
EXIT_CODE=0
FORMATS="umd-min"
while getopts "df:" opt; do
case $opt in
d)
FORMATS="umd"
;;
f)
FORMATS=$OPTARG
;;
esac
done
shift $((OPTIND-1))
# Use shell folder in node modules when we have @rancher/shell installed as a node module
# rather than the use-case of the mono-repo with the shell folder at the top-level
if [ ! -d ${SHELL_DIR} ]; then
SHELL_DIR=$BASE_DIR/node_modules/@rancher/shell/
SHELL_DIR=$(cd -P ${SHELL_DIR} && pwd)
fi
CREATE_TARBALL=${2}
if [ -z "$VERSION" ]; then
VERSION=$(cd pkg/$1; node -p -e "require('./package.json').version")
fi
NAME=${1}-${VERSION}
PKG_DIST=${BASE_DIR}/dist-pkg/${NAME}
if [ -d "${BASE_DIR}/pkg/${1}" ]; then
echo "Building UI Package $1"
echo " Package name: ${NAME}"
echo " Package version: ${VERSION}"
echo " Output formats: ${FORMATS}"
echo " Output directory: ${PKG_DIST}"
rm -rf ${PKG_DIST}
mkdir -p ${PKG_DIST}
pushd pkg/${1}
# Check that the .shell link exists and points to the correct place
if [ -e ".shell" ]; then
LINK=$(readlink .shell)
if [ "${LINK}" != "${SHELL_DIR}" ]; then
echo ".shell symlink exists but does not point to expected location - please check and fix"
popd
exit -1
fi
else
ln -s ${SHELL_DIR} .shell
fi
FILE=index.js
if [ -f ./index.ts ]; then
FILE=index.ts
fi
if [ -n "$COMMIT" ]; then
echo ${COMMIT} > ${PKG_DIST}/version
fi
${BASE_DIR}/node_modules/.bin/vue-cli-service build --name ${NAME} --target lib ${FILE} --dest ${PKG_DIST} --formats ${FORMATS} --filename ${NAME}
EXIT_CODE=$?
cp -f ./package.json ${PKG_DIST}/package.json
node ${SCRIPT_DIR}/pkgfile.js ${PKG_DIST}/package.json
rm -rf ${PKG_DIST}/*.bak
rm .shell
popd
fi
if [ $EXIT_CODE -ne 0 ]; then
exit $EXIT_CODE
fi
if [ -n "${CREATE_TARBALL}" ]; then
echo $COMMIT $COMMIT_BRANCH > ${PKG_DIST}/version-commit.txt
TARBALL=${NAME}.tar.gz
pushd ${PKG_DIST}
rm -f ../$TARBALL
echo "Compressing to ${TARBALL}..."
tar -czf ../${TARBALL} .
popd
fi
exit $EXIT_CODE
+63
View File
@@ -0,0 +1,63 @@
#!/bin/bash
set -e
cd $(dirname $0)/..
BUILD_DEBUG="${BUILD_DEBUG:-}"
if [[ -n "${BUILD_DEBUG}" ]]; then
set -x
env
fi
if [[ -n "$CI_BUILD_TAG" ]]; then
echo "Build has been triggered by a tag. Skipping gate"
exit 0
fi
if [[ -z "$CI_BRANCH" ]]; then
echo "No branch detected. Skipping gate"
exit 0
fi
if [[ -z "$GIT_REPO" ]]; then
echo "No repository detected. Unable to gate"
exit 1
fi
if [[ -z "$GIT_COMMIT" ]]; then
echo "No commit detected. Unable to gate"
exit 1
fi
echo "Repo: $GIT_REPO"
echo "Branch: $CI_BRANCH"
echo "Build Commit: $GIT_COMMIT"
tmp=$(mktemp -u)
STATUS_CODE=$(curl -w "%{http_code}" -s -o $tmp https://api.github.com/repos/$GIT_REPO/branches/$CI_BRANCH)
if [ "$STATUS_CODE" == "403" ]; then
RATE_LIMIT_REMAINING=$(curl -s https://api.github.com/rate_limit | ./scripts/jq-nano - rate remaining)
echo "Remaining GITHUB requests available: $RATE_LIMIT_REMAINING"
RATE_LIMIT_REMAINING=${RATE_LIMIT_REMAINING:-0}
if ((RATE_LIMIT_REMAINING < 1)); then
echo "Github Rate Limit has been hit, skipping gate"
exit 0
fi
# Fall through to the normal path like any other failed status code
fi
LATEST_BRANCH_COMMIT=$(cat $tmp | ./scripts/jq-nano - commit sha)
rm $tmp
echo "Latest Branch Commit: $LATEST_BRANCH_COMMIT"
if [ -z "$LATEST_BRANCH_COMMIT" ]; then
echo "Unable to determine latest commit, failing gate"
exit 1
fi
if [ "$LATEST_BRANCH_COMMIT" == "$GIT_COMMIT" ]; then
echo "Build was created from latest commit, passed upload gate"
else
echo "Build was not created from latest commit, failing gate"
exit 1
fi
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
yarn --pure-lockfile install
source scripts/version
if [[ $COMMIT_BRANCH == "main" ]]; then
VERSION="latest"
else
VERSION=$COMMIT_BRANCH
fi
echo "CI_BUILD_TAG: ${CI_BUILD_TAG}"
echo "GIT_TAG: ${GIT_TAG}"
echo "TAG_VERSION: ${TAG_VERSION}"
echo "CI Build Args"
echo "COMMIT: ${COMMIT}"
echo "COMMIT_BRANCH: ${COMMIT_BRANCH}"
echo "VERSION: ${VERSION}"
if [ -n "$GIT_TAG" ]; then
COMMIT=$COMMIT COMMIT_BRANCH=$COMMIT_BRANCH VERSION=$CI_BUILD_TAG ./scripts/build-pkg.sh ${1} "true"
else
COMMIT=$COMMIT COMMIT_BRANCH=$COMMIT_BRANCH VERSION=$VERSION ./scripts/build-pkg.sh ${1} "true"
fi
EXIT_CODE=$?
if [ -n "$GIT_TAG" ]; then
export PKG_NAME=${1}-${CI_BUILD_TAG}
export PKG_TARBALL=${PKG_NAME}.tar.gz
else
export PKG_NAME=${1}-${VERSION} # harvester-release-harvester-v1.5 or harvester-latest
export PKG_TARBALL=${PKG_NAME}.tar.gz # harvester-release-harvester-v1.5.tar.gz or harvester-latest.tar.gz
fi
echo "CI Build Artifacts"
echo "Package Directory: ${PKG_NAME}"
echo "Package Tarball: ${PKG_TARBALL}"
ENV_OUTPUT="${GITHUB_OUTPUT:-"temp-env"}"
echo "PKG_TARBALL=${PKG_TARBALL}" >> "$ENV_OUTPUT"
echo "PKG_NAME=${PKG_NAME}" >> "$ENV_OUTPUT"
exit $EXIT_CODE
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env node
const fs = require('fs');
const readline = require('readline');
const args = process.argv.slice(2);
const file = args.shift();
let text;
if ( file === '-' ) {
process.stdin.resume();
text = fs.readFileSync(0, "utf8");
} else {
text = fs.readFileSync(file, "utf8");
}
const data = JSON.parse(text);
let out = data;
while ( args.length ) {
out = out[args.shift()];
}
console.log(out);
+24
View File
@@ -0,0 +1,24 @@
const fs = require('fs');
console.log('Updating package file'); // eslint-disable-line no-console
if (process.argv.length !== 3) {
console.error('No enough args'); // eslint-disable-line no-console
return;
}
const pkgFilepath = process.argv[process.argv.length - 1];
const pkg = require(pkgFilepath);
// Ensure we have the keys we need
pkg.files = ['**/*'];
pkg.rancher = true;
pkg.main = `${ pkg.name }-${ pkg.version }.umd.min.js`;
delete pkg.scripts;
delete pkg.browserslist;
fs.writeFileSync(pkgFilepath, JSON.stringify(pkg, null, 2));
+57
View File
@@ -0,0 +1,57 @@
#!/bin/bash
shopt -s extglob
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
DIRTY="-dirty"
fi
COMMIT=$(git rev-parse --short HEAD)
COMMIT_DATE=$(git --no-pager log -1 --format='%ct')
COMMIT_BRANCH=$(git rev-parse --abbrev-ref HEAD | sed -E 's/[^a-zA-Z0-9.-]+/-/g')
GIT_TAG=${CI_BUILD_TAG:-$(git tag -l --contains HEAD | head -n 1)}
LAST_TAG=${GIT_TAG:-'v0.0.0'}
if [[ -z "$DIRTY" && -n "$GIT_TAG" ]]; then
# human readable version used on rancher dashboard about page
export DASHBOARD_VERSION=$GIT_TAG
# computer readable version
VERSION=$GIT_TAG
else
# human readable version used on rancher dashboard about page
export DASHBOARD_VERSION="${COMMIT_BRANCH} ${COMMIT}"
# computer readable version
VERSION="${COMMIT}${DIRTY}"
fi
# Chart tag.
if [[ -z "${DIRTY}" && -n "${GIT_TAG}" ]]; then
CHART_VERSION="${GIT_TAG}"
elif [[ "$DIRTY" ]]; then
CHART_VERSION="${LAST_TAG}${DIRTY}.${COMMIT}"
else
CHART_VERSION="${LAST_TAG}-${COMMIT_DATE}.${COMMIT}.${COMMIT_BRANCH}"
fi
# Drop the v prefix for Chart Version to follow existing pattern.
CHART_VERSION="$(echo ${CHART_VERSION} | sed -E 's/^v//' | sed -e 's/^\(.\{55\}\).*/\1/')"
# Chart Repo - Target Repo for releases.
case $CHART_VERSION in
*-alpha*) # All alpha tags
CHART_REPO="alpha" ;;
*-rc*) # All rc tags
CHART_REPO="latest" ;;
+([0-9]).+([0-9]).+([0-9])) # All release tags
CHART_REPO="latest" ;;
*) # Anything else - Future use
CHART_REPO="dev" ;;
esac
if [ -z "$ARCH" ]; then
ARCH=amd64
fi
echo "ARCH: $ARCH"
echo "CHART_REPO: $CHART_REPO"
echo "CHART_VERSION: $CHART_VERSION"
echo "VERSION: $VERSION"
+1467 -1123
View File
File diff suppressed because it is too large Load Diff