Compare commits

...

107 Commits

Author SHA1 Message Date
Volcano Bot 4ac9bc0ffd
Merge pull request #4389 from JesseStutler/fix_4299
Delete secrets permission for volcano agent
2025-06-21 08:07:11 +08:00
Volcano Bot 1098a08122
Merge pull request #4377 from zhifei92/sopport_allocate_func_for_extender
Support the allocation callback function provided by the extender.
2025-06-20 14:23:10 +08:00
JesseStutler ddecb9a75a Delete secrets permission for volcano agent
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-06-20 11:12:18 +08:00
Volcano Bot aa439233cc
Merge pull request #4373 from LY-today/bug-fix
fix: add getNodeStatus
2025-06-18 16:14:09 +08:00
LY-today b858207728
fix: add isNodeUnschedulable and isNodeNotReady func
Signed-off-by: LY-today <724102053@qq.com>
2025-06-18 14:55:47 +08:00
zhangzhifei16 7c3720380e feat: support the allocate callback function for extender.
Signed-off-by: zhangzhifei16 <zhangzhifei16@jd.com>

fix: Correct log formatting.

Signed-off-by: zhangzhifei16 <zhangzhifei16@jd.com>
2025-06-18 09:35:07 +08:00
Volcano Bot 7febf4dff7
Merge pull request #4378 from ElectricFish7/bug-fix
Move InitCycleState from openSession to OpenSession
2025-06-17 17:09:08 +08:00
Yuqi Wu 3c52b43750 Move InitCycleState from openSession to OpenSession
Signed-off-by: Yuqi Wu <wuyuqi22@mails.ucas.ac.cn>
2025-06-16 20:01:23 +08:00
Volcano Bot 3aa260cf38
Merge pull request #4279 from bibibox/add_preempt_proposal
Support topology aware in the preempt action
2025-06-06 16:18:57 +08:00
Box Zhang e9040d33a3 Preempt action support topology
Signed-off-by: Box Zhang <wszwbsddbk@gmail.com>
2025-06-06 15:20:51 +08:00
Box Zhang 3506a7089b Proposal: Preempt action support topology
Signed-off-by: Box Zhang <wszwbsddbk@gmail.com>
2025-06-04 11:33:01 +08:00
Volcano Bot 6e2959db6b
Merge pull request #4339 from JesseStutler/v1.12-update
Revert "Bump image to v1.12.0" in mater branch/ Update api version/ Fix queue status update
2025-06-04 10:52:56 +08:00
JesseStutler a3d19d4bea Update volcano v1.12 Kubernetes compatibility
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-06-04 09:33:16 +08:00
JesseStutler 50895c2c36 Add -v for all e2e testing and set longer timeout
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-06-04 09:33:01 +08:00
Volcano Bot d940019b9c
Merge pull request #4316 from sailorvii/doc-update
Refine vgpu user guide
2025-06-03 10:40:55 +08:00
JesseStutler 88e22aab4e Upgrade api version to v1.12.1
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-06-03 10:13:24 +08:00
Monokaix c7efd55dd9 Fix queue update conflicts when upgrading to new version
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-06-03 10:13:12 +08:00
JesseStutler b505c1b310 Revert "Bump image to v1.12.0"
This reverts commit 284eaed827.

Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-06-03 10:12:54 +08:00
Volcano Bot adab22d06a
Merge pull request #4330 from JesseStutler/v1.12-update
Bump image to v1.12.0
2025-05-30 23:16:52 +08:00
JesseStutler fb07f675fd Add -v for printing e2e logs
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-05-30 17:24:28 +08:00
JesseStutler 284eaed827 Bump image to v1.12.0
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-05-30 15:49:02 +08:00
chenw66 57c2d11654 refine vgpu user guide
Signed-off-by: chenw66 <chenw66@chinaunicom.cn>
2025-05-29 21:20:18 +08:00
Volcano Bot d48064b933
Merge pull request #4122 from dongjiang1989/add-jobflow-validate
feat: add jobflow flow dag validate
2025-05-29 20:14:54 +08:00
Volcano Bot 2c43314183
Merge pull request #4169 from dongjiang1989/add-jobflow-metrics
feat: Add jobflow metrics
2025-05-29 20:02:51 +08:00
Volcano Bot d517da9d1a
Merge pull request #4329 from sailorvii/master
Refine the GPU mode process
2025-05-29 19:59:51 +08:00
william-wang ef541d333d
Merge pull request #4327 from Monokaix/hypernode-status
reconcile hypernode nodeCount status
2025-05-29 19:56:38 +08:00
Monokaix e59a75ccb1 reconcile hypernode nodeCount status
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-29 19:47:57 +08:00
chenw66 536b376f77 If the pod specify the GPU sharing mode, exactly match the node GPU sharing mode
Signed-off-by: chenw66 <chenw66@chinaunicom.cn>
2025-05-29 17:15:34 +08:00
dongjiang b82a72edfe
add jobflow flow dag validate
Signed-off-by: dongjiang <dongjiang1989@126.com>

default disable jobflows validate

add jobflow flow dag validate

Signed-off-by: dongjiang <dongjiang1989@126.com>

default disable jobflows validate

fix gofmt

Signed-off-by: dongjiang <dongjiang1989@126.com>

add jobflow flow dag validate

Signed-off-by: dongjiang <dongjiang1989@126.com>

default disable jobflows validate

fix gofmt

Signed-off-by: dongjiang <dongjiang1989@126.com>
2025-05-29 17:14:05 +08:00
dongjiang 5768676e33
add jobflow metrics
Signed-off-by: dongjiang <dongjiang1989@126.com>
2025-05-29 14:55:58 +08:00
Volcano Bot 60ad1da6d2
Merge pull request #4325 from Monokaix/nt-doc
Add NetworkTopology plugin score doc
2025-05-29 09:51:53 +08:00
Volcano Bot 02c604b329
Merge pull request #4322 from Monokaix/nt-discovery-new
Add hyperNode controller framework and IB discovery
2025-05-28 19:35:50 +08:00
Monokaix 902de0fc40 Add hyperNode controller framework and IB discovery
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-28 17:34:35 +08:00
wangbin c78df02ada Add NetworkTopology plugin score doc
Signed-off-by: wangbin <994903808@qq.com>
2025-05-28 15:03:52 +08:00
Volcano Bot 4855ae9e00
Merge pull request #4321 from MondayCha/master
Fix admission webhook with labelSelector for hyperNode
2025-05-27 19:23:50 +08:00
Yilong Li c19810df43
Fix admission webhook of labelSelector for hyperNode
Signed-off-by: Yilong Li <mondaycha@outlook.com>
2025-05-27 18:43:34 +08:00
Volcano Bot a27698cb1c
Merge pull request #4135 from dongjiang1989/fix-jobflow-status-when-vcjob-failed
fix: Fix jobflow status  from `running` to `failed` FSM
2025-05-27 16:42:49 +08:00
Volcano Bot c3cba84bf9
Merge pull request #3799 from JesseStutler/dra_dev
feature: Add dynamic resource allocation(DRA) plugin
2025-05-27 14:35:49 +08:00
Volcano Bot 379234ea2f
Merge pull request #4302 from mahdikhashan/add-citation
Add citation
2025-05-27 14:20:49 +08:00
Volcano Bot 73934c1975
Merge pull request #4290 from sailorvii/master
Add vgpu dynamic mig
2025-05-27 13:29:52 +08:00
chenw66 4b8409bf49 Add vgpu dynamic mig
Signed-off-by: chenw66 <chenw66@chinaunicom.cn>
2025-05-27 12:55:15 +08:00
mahdikhashan 985a672822 add citation as cff file and text in readme
Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>
2025-05-26 14:31:37 +02:00
JesseStutler 90d92512b4 feature: Add DRA Implementation
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-05-26 20:27:23 +08:00
Volcano Bot 48f2b4fd84
Merge pull request #4319 from Monokaix/api
fix go mod and informer resync
2025-05-26 19:04:48 +08:00
Monokaix 3e2c7885d3 fix go mod and informer resync
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-26 18:29:05 +08:00
Volcano Bot 77dca963cd
Merge pull request #4310 from Monokaix/network-to-master
Network Topology Aware Scheduling capability move to master
2025-05-26 16:14:51 +08:00
Volcano Bot 2ab7d08673
Merge pull request #4174 from Hcryw/feat/add-clusterrole-for-editor-and-viewer
 feat: add clusterrole for editor & viewer
2025-05-26 15:41:51 +08:00
Volcano Bot 8b52d78956
Merge pull request #4047 from sfc-gh-raravena/upstream/ricardo-fix
Add resync-period flag for k8s native informers
2025-05-26 14:15:51 +08:00
wangbin ba7df2546d Add network-topology-aware plugin and hyperNode score callback
Signed-off-by: wangbin <994903808@qq.com>
2025-05-26 11:28:56 +08:00
Volcano Bot cbc4ebb413
Merge pull request #4105 from dongjiang1989/fix-security
Fix: Incorrect conversion between integer types
2025-05-23 17:19:48 +08:00
haoriwei1 bde1e67490 feat: add cluster for editor & viewer
Signed-off-by: haoriwei1 <haoriwei1@sensetime.com>

feat: add common_labels

Signed-off-by: haoriwei1 <haoriwei1@sensetime.com>

 feat: drop priority fields

Signed-off-by: haoriwei1 <haoriwei1@sensetime.com>
2025-05-23 16:50:54 +08:00
wangbin 385ec23d6d HyperNode supports select Nodes By labels
Signed-off-by: wangbin <994903808@qq.com>
2025-05-23 11:25:22 +08:00
jessestutler 312de89d73 Add hypernodes validation webhook configuration yaml
Signed-off-by: jessestutler <chenzicong4@huawei.com>
2025-05-23 11:25:22 +08:00
ecosysbin b6a4a74bf1 Volcano scheduler: Supports network topology aware scheduling when pods rescheduled
Signed-off-by: ecosysbin <14729934+ecosysbin@user.noreply.gitee.com>
2025-05-23 11:25:22 +08:00
JesseStutler 0ed5387e8a Move GetAncestors as HyperNodeInfoMap method and provide GetLCAHyperNode
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-05-23 11:25:22 +08:00
weapons97 2d0bad9443 update for comment bugs
Signed-off-by: weapons97 <weapons97@gmail.com>
2025-05-23 11:25:22 +08:00
weipeng b28c6a8e47 admission(hypernode) validate memberSelector for issue #3878
Signed-off-by: weipeng <weapons97@gmail.com>
2025-05-23 11:25:22 +08:00
xuwentao 663e495560 webhook(hypernode): validate memberSelectorType
Signed-off-by: xuwentao <cutenear1993@yahoo.com>
2025-05-23 11:25:22 +08:00
Monokaix 06329c0165 add node event
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-23 11:25:22 +08:00
Monokaix d7c57329f8 move parent to hyperNodeInfo
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-23 11:25:22 +08:00
Monokaix fd677e4c32 Update: Populate hyperNode info
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-23 11:25:21 +08:00
penggu 269994f5d1 Populate hyperNode tree in cache
Signed-off-by: penggu <penggu@gmail.com>
2025-05-23 11:16:44 +08:00
Monokaix 557ac2a40b Add plugin for networkTopology and score logic
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-23 11:14:31 +08:00
Monokaix ef7aed7824 Network topology implements of scheduler
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-23 11:14:28 +08:00
Monokaix b6d6253afe Auto generate CRD yaml
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-23 11:02:33 +08:00
Volcano Bot 059f111487
Merge pull request #4152 from JesseStutler/volume_binding
Refactor volume binding and add prebind implementation
2025-05-23 09:28:48 +08:00
Volcano Bot 03bb7e28bc
Merge pull request #4298 from halcyon-r/master
Vcctl supports merging multiple kubeconfig to support context switching among multiple k8s clusters.
2025-05-22 18:11:48 +08:00
JesseStutler 363be11267 Add benchmark testing for allocate
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-05-22 16:36:42 +08:00
JesseStutler f2b4f660c4 Refactor volume binding and add prebind implementation
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-05-22 11:10:55 +08:00
Ricardo Aravena 47bbf023d2
Add resync-period flag for k8s native informers
This is to optinally help with cache inconsistencies

Signed-off-by: Ricardo Aravena <ricardo.aravena@snowflake.com>
2025-05-21 14:03:15 -07:00
hairuiyang 961ee64953 Vcctl suopports switching contexts among multiple clusters.
- Vcctl supports merging multiple kubeconfig.
- Refactor some duplicated code.
- Fix vcctl e2e tests after removing --kubeconfig option default value.

Signed-off-by: hairuiyang <hairuiyang@deeproute.ai>
2025-05-21 17:20:05 +08:00
Volcano Bot cd7774c174
Merge pull request #4305 from hwdef/update-jobflow-docs
cleanup: update jobflow example docs
2025-05-21 16:36:47 +08:00
hwdef d3f0ee72fc update jobflow example docs
Signed-off-by: hwdef <hwdefcom@outlook.com>
2025-05-21 16:08:04 +08:00
Volcano Bot 80a0ac39dd
Merge pull request #4307 from Monokaix/queue-not-open
Prevent pod scheduling when reclaim
2025-05-21 14:40:46 +08:00
Monokaix 2123494a54 Prevent pod scheduling when reclaim
Signed-off-by: Monokaix <changxuzheng@huawei.com>
2025-05-20 16:36:53 +08:00
Volcano Bot 3f864478a7
Merge pull request #4285 from fengruotj/graceful-shutdown
feat: add graceful shutdown server for webhook manager.
2025-05-19 21:46:42 +08:00
tanjie.master b039864d0e feat: add graceful shutdown server for webhook manager.
Signed-off-by: tanjie.master <tanjie.master@bytedance.com>
2025-05-19 20:59:32 +08:00
Volcano Bot 8b392ea0bf
Merge pull request #4300 from mahdikhashan/security-ci-artifact-debug
[ci] debug security score workflow artifact upload failure
2025-05-19 19:50:42 +08:00
mahdikhashan 412c069d92 rename artifact file
Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

change name

Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

increase artifact version

Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

revert name

Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>
2025-05-19 12:43:02 +02:00
Volcano Bot 94bc6c9857
Merge pull request #4266 from mahdikhashan/feature-stale-action
Feature stale action
2025-05-19 17:27:44 +08:00
mahdikhashan 3099c53927 add new stale workflow to run everynight midnight utc
Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

remove old stale bot

Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

remove comment

Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

update

Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

update latest commit and remove labels of issue from exempt

Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

revert issues labels

Signed-off-by: mahdikhashan <mahdikhashan1@gmail.com>

Fix panic while queues' total guarantee configuration exceed the total resources of the cluster when the node information is not fully synchronized or other situations.

Signed-off-by: hairuiyang <hairuiyang@deeproute.ai>

fix: add miss queue state check in allocatable action

Signed-off-by: googs1025 <googs1025@gmail.com>

fix controller panic for mpi job

Signed-off-by: g00673948 <guoqin10@huawei.com>

Bump golang.org/x/net from 0.36.0 to 0.38.0

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 11:00:21 +02:00
Volcano Bot 026df29e17
Merge pull request #4281 from murali1539/master
WORLD_SIZE calculation for PyTorch Jobs
2025-05-19 16:07:44 +08:00
Volcano Bot f54fa9cbeb
Merge pull request #4196 from volcano-sh/dependabot/go_modules/golang.org/x/net-0.38.0
Bump golang.org/x/net from 0.36.0 to 0.38.0
2025-05-15 17:38:41 +08:00
Volcano Bot 1c5ffeb0b3
Merge pull request #4272 from guoqinwill/fix-mpi
fix controller panic for mpi job
2025-05-15 11:34:38 +08:00
Volcano Bot 3dcbb1b0d0
Merge pull request #4274 from googs1025/fix_queue_close2
fix: add miss queue state check in allocatable action
2025-05-15 11:13:38 +08:00
Volcano Bot 97c5298854
Merge pull request #4273 from halcyon-r/master
Fix panic while queues' total guarantee  exceed the total resource of the cluster in some situations.
2025-05-15 11:10:41 +08:00
g00673948 b764a301bf fix controller panic for mpi job
Signed-off-by: g00673948 <guoqin10@huawei.com>
2025-05-15 10:23:49 +08:00
hairuiyang d02fda27f9 Fix panic while queues' total guarantee configuration exceed the total resources of the cluster when the node information is not fully synchronized or other situations.
Signed-off-by: hairuiyang <hairuiyang@deeproute.ai>
2025-05-14 10:59:07 +08:00
Volcano Bot d2c92115e4
Merge pull request #4282 from Poor12/fix-scheduler
fix: scheduler leader elect namespace not take effect
2025-05-13 19:22:40 +08:00
shentiecheng 4466ae426d fix: scheduler leader elect namespace not take effect
Signed-off-by: shentiecheng <shentiecheng@bytedance.com>
2025-05-13 17:03:49 +08:00
googs1025 e5ee5168be fix: add miss queue state check in allocatable action
Signed-off-by: googs1025 <googs1025@gmail.com>
2025-05-13 13:08:51 +08:00
Muralidhara Juluri ae092898e0 Include only master and worker replicas in WORLD_SIZE calculation for pytorch jobs
Signed-off-by: Muralidhara Juluri <mjuluri@linkedin.com>
2025-05-12 21:50:50 -07:00
jessestutler 3eb1c7223f Add volume binding plugin design doc and add prebind design doc
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
2025-05-13 12:31:55 +08:00
Volcano Bot f564e441a2
Merge pull request #4263 from googs1025/fix_queue_close
fix: prevent the scheduling of pods in noopen queues
2025-05-12 10:15:38 +08:00
Volcano Bot 3fdd845b39
Merge pull request #4264 from googs1025/refactor_util
chore: change BuildPodWithPreeemptionPolicy -> BuildPodWithPreemptionPolicy
2025-05-10 21:13:37 +08:00
googs1025 14940b5e15 chore: change BuildPodWithPreeemptionPolicy -> BuildPodWithPreemptionPolicy
Signed-off-by: googs1025 <googs1025@gmail.com>
2025-05-10 10:29:08 +08:00
googs1025 5756e5e70f fix: prevent the scheduling of pods in noopen queues
Signed-off-by: googs1025 <googs1025@gmail.com>
2025-05-10 09:52:29 +08:00
Volcano Bot cbc4990d75
Merge pull request #3946 from kingeasternsun/improve/capacity-deserved
in capacity plugin attr.deserved no need MinDimensionResource with attr.request
2025-05-09 10:31:33 +08:00
kingeasternsun 740da5f397 fix ut
Signed-off-by: kingeasternsun <kingeasternsun@gmail.com>
2025-05-09 01:48:19 +00:00
kingeasternsun aabc66defe 🐛 fix capacity plugin
Signed-off-by: kingeasternsun <kingeasternsun@gmail.com>
2025-05-08 13:49:58 +00:00
Volcano Bot 9da8c27fa3
Merge pull request #4222 from feyounger/4221
Optimize multiple 'if' statements in the code
2025-05-08 10:05:37 +08:00
dependabot[bot] a86857a31e
Bump golang.org/x/net from 0.36.0 to 0.38.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-08 01:17:44 +00:00
Volcano Bot 2a31823b0a
Merge pull request #4205 from AdamKorcz/fuzz1
Add fuzz tests for job controller
2025-05-08 09:16:35 +08:00
Adam Korczynski 008a39ec06 Add fuzz tests for job controller
Signed-off-by: Adam Korczynski <adam@adalogics.com>
2025-05-07 15:38:00 +01:00
feyounger e9e548d28f Optimize multiple 'if' statements in the code
Signed-off-by: feyounger <1477865250@qq.com>

Optimize multiple 'if' statements in the code

Signed-off-by: feyounger <1477865250@qq.com>

Optimize multiple 'if' statements in the code

Signed-off-by: feyounger <1477865250@qq.com>

Optimize multiple 'if' statements in the code

Signed-off-by: feyounger <1477865250@qq.com>

Optimize multiple 'if' statements in the code

Signed-off-by: feyounger <1477865250@qq.com>

Optimize multiple 'if' statements in the code

Signed-off-by: feyounger <1477865250@qq.com>
2025-04-25 18:16:35 +08:00
dongjiang 87d4b336c9 fix jobflow running to failed FSM
Signed-off-by: dongjiang <dongjiang1989@126.com>

add TODO for the if condition judgment is implemented
2025-03-24 20:41:30 +08:00
dongjiang c8e22193ab fix incorrect conversion
Signed-off-by: dongjiang <dongjiang1989@126.com>
2025-03-20 17:27:09 +08:00
211 changed files with 23484 additions and 1851 deletions

50
.github/stale.yml vendored
View File

@ -1,50 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable.
# We want stale bot to notify us that something is stale so we can revisit it.
# If one issue is marked as 'reminder' by the reminder bot, we don't mark it as 'stale' again.
exemptLabels:
# This label is hardcoded on remind bot (https://probot.github.io/apps/reminders/) and is used by remind bot when
# issue is being reminded.
- reminder
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false
# Label to use when marking as stale
staleLabel: lifecycle/stale
pull:
daysUntilClose: 90
daysUntilStale: 180
markComment: >
Hello 👋 Looks like there was no activity on this amazing PR for last 180 days.
**Do you mind updating us on the status?** Is there anything we can help with? If you plan to still work on it, just comment on this PR or push a commit. Thanks! 🤗
If there will be no activity for 90 days, this issue will be closed (we can always reopen a PR if you get back to this!).
#unmarkComment: No need for unmark comment.
closeComment: >
Closing for now as there was no activity for last 90 days after marked as stale, let us know if you need this to be reopened! 🤗
issues:
daysUntilClose: 90
daysUntilStale: 180
markComment: >
Hello 👋 Looks like there was no activity on this issue for last 180 days.
**Do you mind updating us on the status?** Is this still reproducible or needed? If yes, just comment on this PR or push a commit. Thanks! 🤗
If there will be no activity for 90 days, this issue will be closed (we can always reopen an issue if we need!).
#unmarkComment: No need for unmark comment.
closeComment: >
Closing for now as there was no activity for last 90 days after marked as stale, let us know if you need this to be reopened! 🤗
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30

49
.github/workflows/e2e_dra.yml vendored Normal file
View File

@ -0,0 +1,49 @@
name: E2E DRA
on:
push:
branches:
- master
tags:
pull_request:
jobs:
e2e_dra:
runs-on: ubuntu-24.04
name: E2E about DRA
timeout-minutes: 40
steps:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.23.x
- name: Install musl
run: |
wget http://musl.libc.org/releases/musl-1.2.1.tar.gz
tar -xf musl-1.2.1.tar.gz && cd musl-1.2.1
./configure
make && sudo make install
- uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Install dependences
run: |
GO111MODULE="on" go install sigs.k8s.io/kind@v0.26.0
curl -LO https://dl.k8s.io/release/v1.32.0/bin/linux/amd64/kubectl && sudo install kubectl /usr/local/bin/kubectl
- name: Checkout code
uses: actions/checkout@v3
- name: Run E2E Tests
run: |
export ARTIFACTS_PATH=${{ github.workspace }}/e2e-dra-logs
make e2e-test-dra CC=/usr/local/musl/bin/musl-gcc
- name: Upload e2e dra logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: volcano_e2e_dra_logs
path: ${{ github.workspace }}/e2e-dra-logs

View File

@ -11,7 +11,7 @@ jobs:
e2e_scheduling_actions:
runs-on: ubuntu-24.04
name: E2E about Scheduling Actions
timeout-minutes: 40
timeout-minutes: 50
steps:
- name: Install Go
uses: actions/setup-go@v5

View File

@ -11,7 +11,7 @@ jobs:
e2e_scheduling_basic:
runs-on: ubuntu-24.04
name: E2E about Basic Scheduling
timeout-minutes: 40
timeout-minutes: 50
steps:
- name: Install Go
uses: actions/setup-go@v5

View File

@ -11,7 +11,7 @@ jobs:
e2e_sequence:
runs-on: ubuntu-24.04
name: E2E about Sequence
timeout-minutes: 40
timeout-minutes: 50
steps:
- name: Install Go
uses: actions/setup-go@v5

View File

@ -11,7 +11,7 @@ jobs:
e2e_vcctl:
runs-on: ubuntu-24.04
name: E2E about Volcano CLI
timeout-minutes: 20
timeout-minutes: 50
steps:
- name: Install Go
uses: actions/setup-go@v5

View File

@ -55,7 +55,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20
uses: actions/upload-artifact@v4.6.2
with:
name: sarif_file
path: results.sarif

47
.github/workflows/stale.yaml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Mark and Close Stale Issues and PRs
on:
schedule:
- cron: '0 0 * * *' # runs every day at midnight UTC
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Mark and close stale issues and PRs
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 #v9.1.0
with:
remove-stale-when-updated: true
stale-issue-label: 'lifecycle/stale'
exempt-issue-labels: 'kind/feature, kind/bug, help wanted, good first issue'
exempt-pr-labels: ''
days-before-stale: 180
days-before-close: 90
stale-issue-message: >
Hello 👋 Looks like there was no activity on this issue for last 180 days.
**Do you mind updating us on the status?** Is this still reproducible or needed? If yes, just comment on this PR or push a commit. Thanks! 🤗
If there will be no activity for 90 days, this issue will be closed (we can always reopen an issue if we need!).
close-issue-message: >
Closing for now as there was no activity for last 90 days after marked as stale, let us know if you need this to be reopened! 🤗
stale-pr-message: >
Hello 👋 Looks like there was no activity on this amazing PR for last 180 days.
**Do you mind updating us on the status?** Is there anything we can help with? If you plan to still work on it, just comment on this PR or push a commit. Thanks! 🤗
If there will be no activity for 90 days, this issue will be closed (we can always reopen a PR if you get back to this!).
close-pr-message: >
Closing for now as there was no activity for last 90 days after marked as stale, let us know if you need this to be reopened! 🤗
exempt-all-milestones: false
exempt-all-assignees: false
operations-per-run: 30

24
CITATION.cff Normal file
View File

@ -0,0 +1,24 @@
cff-version: 1.2.0
message: "If you use Volcano in your work, please cite it using the information below. This helps us demonstrate the project's impact."
title: "Volcano: A Cloud Native Batch System"
authors:
- given-names: "Klaus"
family-names: "Ma"
- given-names: "Kevin"
family-names: "Wang"
- name: "The Volcano Community & Contributors"
type: entity
repository-code: "https://github.com/volcano-sh/volcano"
url: "https://volcano.sh/"
keywords:
- "batch system"
- "kubernetes"
- "scheduling"
- "cncf"
- "high-performance computing"
- "big data"
- "artificial intelligence"
- "machine learning"
license: "Apache-2.0"
notes: "For a specific version, please find the release tag or use the general project information. A comprehensive list of releases can be found at: https://github.com/volcano-sh/volcano/releases"

View File

@ -106,7 +106,7 @@ generate-code:
manifests: controller-gen
go mod vendor
# volcano crd base
$(CONTROLLER_GEN) $(CRD_OPTIONS) paths="./vendor/volcano.sh/apis/pkg/apis/scheduling/v1beta1;./vendor/volcano.sh/apis/pkg/apis/batch/v1alpha1;./vendor/volcano.sh/apis/pkg/apis/bus/v1alpha1;./vendor/volcano.sh/apis/pkg/apis/nodeinfo/v1alpha1" output:crd:artifacts:config=config/crd/volcano/bases
$(CONTROLLER_GEN) $(CRD_OPTIONS) paths="./vendor/volcano.sh/apis/pkg/apis/scheduling/v1beta1;./vendor/volcano.sh/apis/pkg/apis/batch/v1alpha1;./vendor/volcano.sh/apis/pkg/apis/bus/v1alpha1;./vendor/volcano.sh/apis/pkg/apis/nodeinfo/v1alpha1;./vendor/volcano.sh/apis/pkg/apis/topology/v1alpha1" output:crd:artifacts:config=config/crd/volcano/bases
# generate volcano job crd yaml without description to avoid yaml size limit when using `kubectl apply`
$(CONTROLLER_GEN) $(CRD_OPTIONS_EXCLUDE_DESCRIPTION) paths="./vendor/volcano.sh/apis/pkg/apis/batch/v1alpha1" output:crd:artifacts:config=config/crd/volcano/bases
# jobflow crd base
@ -143,6 +143,9 @@ e2e-test-vcctl: vcctl images
e2e-test-stress: images
E2E_TYPE=STRESS ./hack/run-e2e-kind.sh
e2e-test-dra: images
E2E_TYPE=DRA FEATURE_GATES="DynamicResourceAllocation=true" ./hack/run-e2e-kind.sh
generate-yaml: init manifests
./hack/generate-yaml.sh TAG=${RELEASE_VER} CRD_VERSION=${CRD_VERSION}

View File

@ -189,6 +189,7 @@ Please follow the guide [Volcano Dashboard](https://github.com/volcano-sh/dashbo
| Volcano v1.9 | - | - | - | - | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |- |_ |- |
| Volcano v1.10 | - | - | - | - | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |✓ |_ |- |
| Volcano v1.11 | - | - | - | - | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |✓ |✓ |- |
| Volcano v1.12 | - | - | - | - | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |✓ |✓ |✓ |
| Volcano HEAD (master) | - | - | - | - | - | - | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |✓ |✓ |✓ |
Key:
@ -197,6 +198,19 @@ Key:
* `-` The Kubernetes version has features or API objects that Volcano can't use.
## Citing Volcano
If Volcano helps your research, we appreciate your citations. Here is the BibTeX entry:
```bibtex
@misc{volcano2025,
title={Volcano: A Cloud Native Batch System},
author={Klaus Ma and Kevin Wang and others},
year={2025},
howpublished={\url{https://github.com/volcano-sh/volcano}},
}
```
## Meeting
Community weekly meeting for Asia: 15:00 - 16:00 (UTC+8) Friday. ([Convert to your timezone.](https://www.thetimezoneconverter.com/?t=10%3A00&tz=GMT%2B8&))

View File

@ -34,6 +34,7 @@ import (
"volcano.sh/volcano/cmd/controller-manager/app/options"
"volcano.sh/volcano/pkg/controllers/framework"
_ "volcano.sh/volcano/pkg/controllers/garbagecollector"
_ "volcano.sh/volcano/pkg/controllers/hypernode"
_ "volcano.sh/volcano/pkg/controllers/job"
_ "volcano.sh/volcano/pkg/controllers/jobflow"
_ "volcano.sh/volcano/pkg/controllers/jobtemplate"

View File

@ -32,6 +32,7 @@ import (
const (
defaultSchedulerName = "volcano"
defaultSchedulerPeriod = time.Second
defaultResyncPeriod = 0
defaultQueue = "default"
defaultListenAddress = ":8080"
defaultHealthzAddress = ":11251"
@ -60,6 +61,7 @@ type ServerOption struct {
SchedulerNames []string
SchedulerConf string
SchedulePeriod time.Duration
ResyncPeriod time.Duration
// leaderElection defines the configuration of leader election.
LeaderElection config.LeaderElectionConfiguration
// Deprecated: use ResourceNamespace instead.
@ -113,12 +115,13 @@ func (s *ServerOption) AddFlags(fs *pflag.FlagSet) {
"File containing the default x509 Certificate for HTTPS. (CA cert, if any, concatenated "+
"after server cert).")
fs.StringVar(&s.KeyFile, "tls-private-key-file", s.KeyFile, "File containing the default x509 private key matching --tls-cert-file.")
fs.StringVar(&s.LockObjectNamespace, "lock-object-namespace", defaultLockObjectNamespace, "Define the namespace of the lock object; it is volcano-system by default.")
fs.StringVar(&s.LockObjectNamespace, "lock-object-namespace", "", "Define the namespace of the lock object; it is volcano-system by default.")
fs.MarkDeprecated("lock-object-namespace", "This flag is deprecated and will be removed in a future release. Please use --leader-elect-resource-namespace instead.")
// volcano scheduler will ignore pods with scheduler names other than specified with the option
fs.StringArrayVar(&s.SchedulerNames, "scheduler-name", []string{defaultSchedulerName}, "vc-scheduler will handle pods whose .spec.SchedulerName is same as scheduler-name")
fs.StringVar(&s.SchedulerConf, "scheduler-conf", "", "The absolute path of scheduler configuration file")
fs.DurationVar(&s.SchedulePeriod, "schedule-period", defaultSchedulerPeriod, "The period between each scheduling cycle")
fs.DurationVar(&s.ResyncPeriod, "resync-period", defaultResyncPeriod, "The default resync period for k8s native informer factory")
fs.StringVar(&s.DefaultQueue, "default-queue", defaultQueue, "The default queue name of the job")
fs.BoolVar(&s.PrintVersion, "version", false, "Show version and quit")
fs.StringVar(&s.ListenAddress, "listen-address", defaultListenAddress, "The address to listen on for HTTP requests.")

View File

@ -45,6 +45,7 @@ func TestAddFlags(t *testing.T) {
args := []string{
"--schedule-period=5m",
"--resync-period=0",
"--priority-class=false",
"--cache-dumper=false",
"--leader-elect-lease-duration=60s",
@ -58,6 +59,7 @@ func TestAddFlags(t *testing.T) {
expected := &ServerOption{
SchedulerNames: []string{defaultSchedulerName},
SchedulePeriod: 5 * time.Minute,
ResyncPeriod: 0,
LeaderElection: config.LeaderElectionConfiguration{
LeaderElect: true,
LeaseDuration: metav1.Duration{Duration: 60 * time.Second},
@ -66,9 +68,8 @@ func TestAddFlags(t *testing.T) {
ResourceLock: resourcelock.LeasesResourceLock,
ResourceNamespace: defaultLockObjectNamespace,
},
LockObjectNamespace: defaultLockObjectNamespace,
DefaultQueue: defaultQueue,
ListenAddress: defaultListenAddress,
DefaultQueue: defaultQueue,
ListenAddress: defaultListenAddress,
KubeClientOptions: kube.ClientOptions{
Master: "",
KubeConfig: "",

View File

@ -19,6 +19,7 @@ package options
import (
"fmt"
"os"
"time"
"github.com/spf13/pflag"
@ -26,31 +27,33 @@ import (
)
const (
defaultSchedulerName = "volcano"
defaultQPS = 50.0
defaultBurst = 100
defaultEnabledAdmission = "/jobs/mutate,/jobs/validate,/podgroups/mutate,/pods/validate,/pods/mutate,/queues/mutate,/queues/validate"
defaultHealthzAddress = ":11251"
defaultSchedulerName = "volcano"
defaultQPS = 50.0
defaultBurst = 100
defaultEnabledAdmission = "/jobs/mutate,/jobs/validate,/podgroups/mutate,/pods/validate,/pods/mutate,/queues/mutate,/queues/validate"
defaultHealthzAddress = ":11251"
defaultGracefulShutdownTime = time.Second * 30
)
// Config admission-controller server config.
type Config struct {
KubeClientOptions kube.ClientOptions
CertFile string
KeyFile string
CaCertFile string
CertData []byte
KeyData []byte
CaCertData []byte
ListenAddress string
Port int
PrintVersion bool
WebhookName string
WebhookNamespace string
SchedulerNames []string
WebhookURL string
ConfigPath string
EnabledAdmission string
KubeClientOptions kube.ClientOptions
CertFile string
KeyFile string
CaCertFile string
CertData []byte
KeyData []byte
CaCertData []byte
ListenAddress string
Port int
PrintVersion bool
WebhookName string
WebhookNamespace string
SchedulerNames []string
WebhookURL string
ConfigPath string
EnabledAdmission string
GracefulShutdownTime time.Duration
EnableHealthz bool
// HealthzBindAddress is the IP address and port for the health check server to serve on
@ -88,6 +91,7 @@ func (c *Config) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&c.ConfigPath, "admission-conf", "", "The configmap file of this webhook")
fs.BoolVar(&c.EnableHealthz, "enable-healthz", false, "Enable the health check; it is false by default")
fs.StringVar(&c.HealthzBindAddress, "healthz-address", defaultHealthzAddress, "The address to listen on for the health check server.")
fs.DurationVar(&c.GracefulShutdownTime, "graceful-shutdown-time", defaultGracefulShutdownTime, "The duration to wait during graceful shutdown before forcing termination.")
}
// CheckPortOrDie check valid port range.

View File

@ -0,0 +1,66 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package options
import (
"testing"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/api/equality"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"volcano.sh/volcano/pkg/kube"
)
func TestAddFlags(t *testing.T) {
fs := pflag.NewFlagSet("addflagstest", pflag.ExitOnError)
s := NewConfig()
s.AddFlags(fs)
utilfeature.DefaultMutableFeatureGate.AddFlag(fs)
args := []string{
"--master=127.0.0.1",
"--kube-api-burst=200",
}
fs.Parse(args)
// This is a snapshot of expected options parsed by args.
expected := &Config{
KubeClientOptions: kube.ClientOptions{
Master: "127.0.0.1",
KubeConfig: "",
QPS: defaultQPS,
Burst: 200,
},
ListenAddress: "",
Port: 8443,
PrintVersion: false,
WebhookName: "",
WebhookNamespace: "",
SchedulerNames: []string{defaultSchedulerName},
WebhookURL: "",
ConfigPath: "",
EnabledAdmission: defaultEnabledAdmission,
GracefulShutdownTime: defaultGracefulShutdownTime,
EnableHealthz: false,
HealthzBindAddress: defaultHealthzAddress,
}
if !equality.Semantic.DeepEqual(expected, s) {
t.Errorf("Got different run options than expected.\nGot: %+v\nExpected: %+v\n", s, expected)
}
}

View File

@ -17,12 +17,11 @@ limitations under the License.
package app
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
@ -34,6 +33,7 @@ import (
informers "volcano.sh/apis/pkg/client/informers/externalversions"
"volcano.sh/volcano/cmd/webhook-manager/app/options"
"volcano.sh/volcano/pkg/kube"
"volcano.sh/volcano/pkg/signals"
commonutil "volcano.sh/volcano/pkg/util"
wkconfig "volcano.sh/volcano/pkg/webhooks/config"
"volcano.sh/volcano/pkg/webhooks/router"
@ -97,8 +97,7 @@ func Run(config *options.Config) error {
klog.V(3).Infof("Successfully added caCert for all webhooks")
webhookServeError := make(chan struct{})
stopChannel := make(chan os.Signal, 1)
signal.Notify(stopChannel, syscall.SIGTERM, syscall.SIGINT)
ctx := signals.SetupSignalContext()
factory.Start(webhookServeError)
for informerType, ok := range factory.WaitForCacheSync(webhookServeError) {
@ -116,21 +115,23 @@ func Run(config *options.Config) error {
}
go func() {
err = server.ListenAndServeTLS("", "")
if err != nil && err != http.ErrServerClosed {
if err != nil && !errors.Is(err, http.ErrServerClosed) {
klog.Fatalf("ListenAndServeTLS for admission webhook failed: %v", err)
close(webhookServeError)
}
klog.Info("Volcano Webhook manager started.")
klog.Info("Volcano Webhook manager stopped.")
}()
if config.ConfigPath != "" {
go wkconfig.WatchAdmissionConf(config.ConfigPath, stopChannel)
go wkconfig.WatchAdmissionConf(config.ConfigPath, ctx.Done())
}
select {
case <-stopChannel:
if err := server.Close(); err != nil {
case <-ctx.Done():
timeoutCtx, cancel := context.WithTimeout(context.Background(), config.GracefulShutdownTime)
defer cancel()
if err := server.Shutdown(timeoutCtx); err != nil {
return fmt.Errorf("close admission server failed: %v", err)
}
return nil

View File

@ -31,6 +31,8 @@ import (
"volcano.sh/volcano/cmd/webhook-manager/app"
"volcano.sh/volcano/cmd/webhook-manager/app/options"
"volcano.sh/volcano/pkg/version"
_ "volcano.sh/volcano/pkg/webhooks/admission/hypernodes/validate"
_ "volcano.sh/volcano/pkg/webhooks/admission/jobflows/validate"
_ "volcano.sh/volcano/pkg/webhooks/admission/jobs/mutate"
_ "volcano.sh/volcano/pkg/webhooks/admission/jobs/validate"
_ "volcano.sh/volcano/pkg/webhooks/admission/podgroups/mutate"

View File

@ -39,6 +39,18 @@ spec:
format: int32
minimum: 1
type: integer
networkTopology:
properties:
highestTierAllowed:
default: 1
type: integer
mode:
default: hard
enum:
- hard
- soft
type: string
type: object
plugins:
additionalProperties:
items:

View File

@ -57,6 +57,18 @@ spec:
format: int32
minimum: 1
type: integer
networkTopology:
properties:
highestTierAllowed:
default: 1
type: integer
mode:
default: hard
enum:
- hard
- soft
type: string
type: object
plugins:
additionalProperties:
items:

View File

@ -89,6 +89,24 @@ spec:
if there's not enough resources to start each task, the scheduler
will not start anyone.
type: object
networkTopology:
description: NetworkTopology defines the NetworkTopology config, this
field works in conjunction with network topology feature and hyperNode
CRD.
properties:
highestTierAllowed:
default: 1
description: HighestTierAllowed specifies the highest tier that
a job allowed to cross when scheduling.
type: integer
mode:
default: hard
description: Mode specifies the mode of the network topology constrain.
enum:
- hard
- soft
type: string
type: object
priorityClassName:
description: |-
If specified, indicates the PodGroup's priority. "system-node-critical" and

View File

@ -0,0 +1,225 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.0
name: hypernodes.topology.volcano.sh
spec:
group: topology.volcano.sh
names:
kind: HyperNode
listKind: HyperNodeList
plural: hypernodes
shortNames:
- hn
singular: hypernode
scope: Cluster
versions:
- additionalPrinterColumns:
- jsonPath: .spec.tier
name: Tier
type: string
- jsonPath: .status.nodeCount
name: NodeCount
type: integer
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: HyperNode represents a collection of nodes sharing similar network
topology or performance characteristics.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: Spec defines the desired configuration of the HyperNode.
properties:
members:
description: Members defines a list of node groups or individual nodes
included in the HyperNode.
items:
description: MemberSpec represents a specific node or a hyperNodes
in the hyperNode.
properties:
selector:
description: Selector defines the selection rules for this member.
properties:
exactMatch:
description: ExactMatch defines the exact match criteria.
properties:
name:
description: Name specifies the exact name of the node
to match.
type: string
type: object
labelMatch:
description: LabelMatch defines the labels match criteria
(only take effect when Member Type is "Node").
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
description: |-
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions, whose key field is "key", the
operator is "In", and the values array contains only "value". The requirements are ANDed.
type: object
type: object
x-kubernetes-map-type: atomic
regexMatch:
description: RegexMatch defines the regex match criteria.
properties:
pattern:
description: Pattern defines the regex pattern to match
node names.
type: string
type: object
type: object
x-kubernetes-validations:
- message: Either ExactMatch or RegexMatch or LabelMatch must
be specified
rule: has(self.exactMatch) || has(self.regexMatch) || has(self.labelMatch)
- message: Only one of ExactMatch, RegexMatch, or LabelMatch
can be specified
rule: '(has(self.exactMatch) ? 1 : 0) + (has(self.regexMatch)
? 1 : 0) + (has(self.labelMatch) ? 1 : 0) <= 1'
type:
description: Type specifies the member type.
enum:
- Node
- HyperNode
type: string
required:
- type
type: object
type: array
tier:
description: Tier categorizes the performance level of the HyperNode.
type: integer
required:
- tier
type: object
status:
description: Status provides the current state of the HyperNode.
properties:
conditions:
description: Conditions provide details about the current state of
the HyperNode.
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
nodeCount:
description: NodeCount is the total number of nodes currently in the
HyperNode.
format: int64
minimum: 0
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@ -565,13 +565,19 @@ Allocate resources for queue-\> Job \-\> Task.
Phase2:
Allocate resources for queue-\> hyperJob \-\> Job \-\> Task.
**plugin:** NetworkTopology
**plugin:** network-topology-aware
- AddJobGroupReadyFn: check whether hyperJob minAvailable is met.(phase 2)
- AddHyperNodeOrderFn: score for hyperNodes.(take effect in hard limit, closest tiers have higher score)
1. If a Job is being scheduled for the very first time, candidate hypernodes that need to be scored will get a score of 0 and then return right away. The name of the HyperNode where the Job eventually gets scheduled successfully will be recorded in the Job's annotations under the key JobAllocatedHyperNode.
2. If it is not the first scheduling of a job, calculate the LCAHyperNode (Lowest Common Ancestor HyperNode) between candidate hypernodes(In the Allocate process, the system will calculate whether the LCAHyperNode tier of the hyperNode meets the Hard limit. If it doesn't, the hyperNode will be filtered out.) that need to be scored and the `JobAllocatedHyperNode` of the job. The lower the tier of the calculated LCAHyperNode, the higher the score. If there is only one highest score, return the scoring result.
3. If there is more than one HyperNode with the highest score in the scoring result of step 2, calculate the distribution of the tasks that have been successfully scheduled for the job among these HyperNodes. The greater the distribution quantity, the higher the score.
4. The HyperNode that is successfully scheduled in the end in steps 2 and 3 will also be recorded as the `JobAllocatedHyperNode` attribute of the job.
- AddNodeOrderFn: score for nodes.(take effect in soft limit,take effect in )
- AddNodeOrderFn: score for nodes.(take effect in soft limit, closest tiers have higher score)
1. To score all nodes, we need to first obtain the HyperNode to which the node belongs and the `JobAllocatedHyperNode` of the job to which the task belongs.
2. The subsequent scoring logic is the same as that in the Hard mode. The score of the HyperNode to which it belongs is calculated as the score of the node.
### Webhook

View File

@ -0,0 +1,220 @@
# HyperNode Auto Discovery Design Document
## Introduction
This design document describes the design and implementation of the HyperNode network topology discovery feature in Volcano. This feature automatically discovers the network topology structure within the cluster and creates and maintains HyperNode custom resources (CRs) based on the discovered topology information. Consequently, the Volcano Scheduler will leverage these HyperNode CRs for scheduling decisions, eliminating the need for users to manually maintain HyperNode information.
## Design Goals
* **Automated Discovery**: Automatically discover network topology information from different data sources (such as UFM, RoCE, etc.).
* **Scalability**: Support multiple network topology discovery sources and easily extend new discovery methods.
* **Real-time**: Be able to reflect changes in network topology in a timely manner.
* **Fault Tolerance**: When a discovery source fails, it does not affect the normal operation of the system.
* **Security**: Securely manage authentication credentials using Kubernetes Secrets.
## Overall Design
### Component Architecture
The entire network topology discovery function consists of the following core components:
* **Config Loader**: Responsible for loading network topology discovery configuration information from ConfigMap.
* **Discovery Manager**: Responsible for managing and coordinating various network topology discoverers.
* **Discoverer**: A specific network topology discoverer, responsible for obtaining network topology information from a specific data source and converting it into `HyperNode` resources.
* **HyperNode Controller**: Responsible for listening to changes in `HyperNode` resources and creating, updating, or deleting `HyperNode` resources based on the discovered topology information.
### Process Flow
```
ConfigMap -> Config Loader -> Discovery Manager -> Discoverer -> HyperNode Controller -> HyperNode
```
1. **Configuration Loading**: `Config Loader` loads network topology discovery configuration information from ConfigMap, including enabled discovery sources, discovery intervals, data source addresses, etc.
2. **Discovery Management**: `Discovery Manager` creates and starts the corresponding `Discoverer` based on the configuration information.
3. **Topology Discovery**: `Discoverer` obtains network topology information from the specified data source and converts the topology information into `HyperNode` resources.
4. **Resource Synchronization**: `HyperNode Controller` receives the `HyperNode` resources discovered by `Discoverer`, compares them with the existing `HyperNode` resources, and then creates, updates, or deletes `HyperNode` resources to keep the `HyperNode` resources in the cluster consistent with the actual network topology.
## Detailed Design
### Config Loader
`Config Loader` is responsible for loading network topology discovery configuration information from ConfigMap.
* **Function**:
* Read configuration information from the specified ConfigMap.
* Parse configuration information and convert it into a `NetworkTopologyConfig` object.
* **Configuration Format**:
```yaml
# volcano-controller.conf file in ConfigMap
networkTopologyDiscovery:
- source: ufm
enabled: true
interval: 10m
credentials:
secretRef:
name: ufm-credentials
namespace: volcano-system
config:
[...]
- source: roce
enabled: false
interval: 15m
config:
[...]
- source: label
enabled: false
config:
# Configuration of the label discovery source
```
* **Code Example**:
```go
// NetworkTopologyConfig represents the configuration of the network topology
type NetworkTopologyConfig struct {
// NetworkTopologyDiscovery specifies the network topology to discover,
// Each discovery source has its own specific configuration
NetworkTopologyDiscovery []DiscoveryConfig `json:"networkTopologyDiscovery" yaml:"networkTopologyDiscovery"`
}
// SecretRef refers to a secret containing sensitive information
type SecretRef struct {
Name string `json:"name" yaml:"name"`
Namespace string `json:"namespace" yaml:"namespace"`
}
// Credentials specifies how to retrieve credentials
type Credentials struct {
SecretRef *SecretRef `json:"secretRef" yaml:"secretRef"`
}
// DiscoveryConfig contains configuration for a specific discovery source
type DiscoveryConfig struct {
// Source specifies the discover source (e.g., "ufm", "roce", etc.)
Source string `json:"source" yaml:"source"`
// Enabled determines if discovery for this source is active
Enabled bool `json:"enabled" yaml:"enabled"`
// Interval is the period between topology discovery operations
// If not specified, DefaultDiscoveryInterval will be used
Interval time.Duration `json:"interval" yaml:"interval"`
// Credentials specifies the username/password to access the discovery source
Credentials *Credentials `json:"credentials" yaml:"credentials"`
// Config contains specific configuration parameters for each discovery source
Config map[string]interface{} `json:"config" yaml:"config"`
}
```
### Discovery Manager
`Discovery Manager` is responsible for managing and coordinating various network topology discoverers.
* **Function**:
* Create and start the corresponding `Discoverer` based on the configuration information.
* Periodically obtain network topology information from `Discoverer`.
* Send network topology information to `HyperNode Controller`.
* **Key Code**:
```go
// RegisterDiscoverer registers the discoverer constructor for the specified source
func RegisterDiscoverer(source string, constructor DiscovererConstructor, kubeClient clientset.Interface) {
discovererRegistry[source] = constructor
}
```
### Discoverer
`Discoverer` is a specific network topology discoverer, responsible for obtaining network topology information from a specific data source and converting it into `HyperNode` resources.
* **Function**:
* Retrieve authentication credentials from Kubernetes Secrets.
* Obtain network topology information from the specified data source.
* Convert network topology information into `HyperNode` resources.
* **Interface Definition**:
```go
// Discoverer is the interface for network topology discovery
type Discoverer interface {
// Start begins the discovery process, sending discovered nodes through the provided channel
Start(outputCh chan<- []*topologyv1alpha1.HyperNode) error
// Stop halts the discovery process
Stop() error
// Name returns the discoverer identifier, this is used for labeling discovered hyperNodes for distinction.
Name() string
}
```
* **Implementation**:
* **UFM Discoverer**: Obtain network topology information from UFM (Unified Fabric Manager).
* **RoCE Discoverer**: Obtain network topology information from RoCE (RDMA over Converged Ethernet) devices.
* **Label Discoverer**: Discover network topology information based on the Label on the Node.
* **Credential Management**:
* Credentials are retrieved from Kubernetes Secrets specified in the configuration.
* The Secret reference includes both name and namespace parameters.
### HyperNode Controller
`HyperNode Controller` is responsible for listening to changes in `HyperNode` resources and creating, updating, or deleting `HyperNode` resources based on the discovered topology information.
* **Function**:
* Listen to change events of `HyperNode` resources.
* Create, update, or delete `HyperNode` resources based on the network topology information provided by `Discoverer`.
* **Key Code**:
```go
func (hn *hyperNodeController) reconcileTopology(source string, discoveredNodes []*topologyv1alpha1.HyperNode) {
klog.InfoS("Starting topology reconciliation", "source", source, "discoveredNodeCount", len(discoveredNodes))
existingNodes, err := hn.hyperNodeLister.List(labels.SelectorFromSet(labels.Set{
api.NetworkTopologySourceLabelKey: source,
}))
if err != nil {
klog.ErrorS(err, "Failed to list existing HyperNode resources")
return
}
existingNodeMap := make(map[string]*topologyv1alpha1.HyperNode)
for _, node := range existingNodes {
existingNodeMap[node.Name] = node
}
discoveredNodeMap := make(map[string]*topologyv1alpha1.HyperNode)
for _, node := range discoveredNodes {
if node.Labels == nil {
node.Labels = make(map[string]string)
}
node.Labels[api.NetworkTopologySourceLabelKey] = source
discoveredNodeMap[node.Name] = node
}
for name, node := range discoveredNodeMap {
if _, exists := existingNodeMap[name]; !exists {
[...]
} else {
[...]
}
delete(existingNodeMap, name)
}
for name := range existingNodeMap {
klog.InfoS("Deleting HyperNode", "name", name, "source", source)
if err := utils.DeleteHyperNode(hn.vcClient, name); err != nil {
klog.ErrorS(err, "Failed to delete HyperNode", "name", name)
}
}
klog.InfoS("Topology reconciliation completed",
"source", source,
"discovered", len(discoveredNodes),
"created/updated", len(discoveredNodeMap),
"deleted", len(existingNodeMap))
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

84
docs/design/prebind.md Normal file
View File

@ -0,0 +1,84 @@
# PreBind
## Backgrounds
Inspired by [#3618](https://github.com/volcano-sh/volcano/issues/3618). Volcano has introduced a lot of plugins from kube-scheduler,
such as `NodeAffinity`, `TaintToleration`, `PodTopologySpread`, `ImageLocality`, etc. These plugins implement the `PreFilter`, `Filter`,
`PreScore`, `Score` in the predicates and nodeorder plugins. However, plugins such as `VolumeBinding` and `DynamicResourceAllocation`
require the `PreBind` extension point. This extension point is executed before binding the Pod to a node and is used to bind additional resources,
such as `PVC` and `ResourceClaim`. If the `PreBind` operation fails, the pod cannot be bound to a node. Currently, volcano lacks the `PreBind` extension point.
## Motivation
Add `PreBind` extension point to volcano, enables the introduction of plugins such as `VolumeBinding` and `DynamicResourceAllocation`, to bind additional resources
## Design Details
A scheduling session is responsible for filtering nodes, scoring nodes, and pre-selecting a node for a pod.
While the cache is responsible for binding the Pod to a node. The session and cache execute in two different goroutines.
Since `Bind` is executed in the cache, `PreBind` also needs to be executed in the cache.
However, the current extension points are all registered in the session, such as `PrePredicateFn`, `PredicateFn`, `NodeOrderFn`, etc.
Adding an extension point like called `PreBindFn` in the session is meaningless because `PreBind` needs to be executed in the cache,
If we try to add `PreBindFn` to the session like adding other extension points, when the session dispatches the pod bind task to the cache,
it still needs to copy the plugins' `PreBind` execution func to the cache. If some plugins' `PreBind` logic contains references to the session,
this might also lead to the session not being released even after `CloseSession` has been called.
Therefore, it is best for the plugin to register `PreBind` directly to the cache.
### PreBinder interface
A new interface needs to be added to the cache, called the `PreBinder` interface:
```go
type PreBinder interface {
PreBind(ctx context.Context, bindCtx *BindContext) error
// PreBindRollBack is called when the pre-bind or bind fails.
PreBindRollBack(ctx context.Context, bindCtx *BindContext)
}
```
It contains two methods, one is `PreBind`, which is the method for the plugin to execute the main `PreBind` logic, and the other is `PreBindRollBack`.
When `PreBind` fails or `Bind` fails, the executed `PreBind` needs to be rolled back to unbind the additional bound resources. If the plugin wants to execute `PreBind`,
it needs to implement the `PreBinder` interface and call the newly added `RegisterPreBinder` method in session to register the plugin into the cache:
```go
// RegisterPreBinder registers the passed bind handler to the cache
func (ssn *Session) RegisterPreBinder(name string, preBinder interface{}) {
ssn.cache.RegisterPreBinder(name, preBinder)
}
```
### BindContext
For the parameters of `PreBind` and `PreBindRollBack`, `BindContext` is a newly defined structure:
```go
type BindContextExtension interface{}
type BindContext struct {
TaskInfo *schedulingapi.TaskInfo
// Extensions stores extra bind context information of each plugin
Extensions map[string]BindContextExtension
}
```
This structure contains the `TaskInfo` that needs to be bound, and a new field called `Extensions`. It's a map contains the bind context information of each plugin.
The `BindContextExtension` carries the information that the plugin needs to pass into the `PreBind` extension point, the `Bind` extension point,
and even the `PostBind` extension point that may need to be added in the future. The `BindContextExtension` is just a empty `interface{}` type.
```go
type BindContextHandler interface {
// SetupBindContextExtension allows the plugin to set up extension information in the bind context
SetupBindContextExtension(ssn *Session, bindCtx *cache.BindContext)
}
```
`BindContextHandler` is a new interface added to session. Plugin can choose whether to implement `BindContextHandler` interface. For example,
`VolumeBinding` needs to carry cycleState to `PreBind`, it can wrap cycleState and then set the additional information to be passed through `SetUpBindContextExtension` method.
```go
// bind context extension information
type bindContextExtension struct {
State *k8sframework.CycleState
}
func (pp *predicatesPlugin) SetupBindContextExtension(ssn *framework.Session, bindCtx *cache.BindContext) {
// ...
// set up bind context extension
bindCtx.Extensions[pp.Name()] = &bindContextExtension{State: ssn.GetCycleState(bindCtx.TaskInfo.UID)}
}
```
So to summarize, if a new plugin needs to call `PreBind`, the process involves:
1. Implement the `PreBinder` interface
2. Call `RegisterPreBinder` to register the plugin when in `OpenSession`
(Optional) If the plugin needs to pass additional information to `PreBind`, the process involves:
1. Implement the `SetupBindContextExtension` method of the `BindContextHandler` interface
2. In the `SetupBindContextExtension` implementation, set the additional information that the plugin needs to carry in the `Extensions` map of the `BindContext` parameter

View File

@ -0,0 +1,98 @@
# Preempt Action Support Topology
## Motivation
In cloud-native task scheduling scenarios, preemption is a key feature to ensure timely scheduling of high-priority tasks. Compared to the K8s scheduler, Volcano's current preemption implementation is relatively simple, especially in handling affinity judgments. To improve the accuracy and efficiency of the preemption mechanism, the existing implementation needs to be optimized, particularly in supporting topology awareness.
## In Scope
- Optimize Volcano's preemption mechanism to support affinity judgments
- Improve single Pod preemption process
- Implement simulation scheduling interface to ensure simulated addition and removal of pods won't cause topology changes
## Out of Scope
- Gang scheduling preemption scenario optimization
## User Stories
### Story 1
As a cluster administrator, I want the system to accurately judge Pod affinity constraints during preemption scheduling to avoid scheduling failures caused by topology changes.
### Story 2
As a user, I expect high-priority Pod preemption to minimize impact on existing Pods while maintaining consistency of affinity rules.
### Story 3
When topology-sensitive resources like GPUs exist, the preemption process needs to consider resource topology relationships to ensure resource allocation after preemption still satisfies original topology constraints.
For example, if a node has 2 GPUs (8GB each), Pod A and Pod B each use 4GB, and Pod C needs 8GB. When Pod C needs to be scheduled, it triggers the preemption mechanism. During the simulation scheduling process, the system will try to preempt Pod A and reschedule it. There are two possible scenarios:
1. If topology changes during simulation scheduling:
- System chooses to preempt Pod A
- The predicate function check successfully for Pod C
- When simulating the re-addition of Pod A, the binpack strategy causes Pod A to be scheduled to a different GPU
- After re-adding Pod A, the predicate function check still passes for Pod C
- This means Pod C can be scheduled without actually removing Pod A
- Therefore, the preemption is considered unnecessary and fails
2. If topology remains consistent during simulation scheduling:
- System chooses to preempt Pod A
- The predicate function check successfully for Pod C
- When simulating the re-addition of Pod A, the original topology relationship is maintained
- After re-adding Pod A, the predicate function check fails for Pod C
- This confirms that Pod A must be removed for Pod C to be scheduled
- Therefore, the preemption is considered necessary and succeeds
Therefore, when implementing the preemption mechanism, it's crucial to verify the necessity of preemption by checking if the topology changes during pod re-addition would affect the scheduling of the preempting pod.
![preempt-action-support-topology-1](images/preempt-action-support-topology/preempt-action-support-topology-1.png)
## Design Detail
### Preemption Process
![preempt-action-support-topology-2](images/preempt-action-support-topology/preempt-action-support-topology-2.png)
1. Execute Predicate on all nodes that are not UnschedulableAndUnresolvable to obtain candidate node list, and perform parallel simulation scheduling on all candidate nodes.
2. The simulation scheduling process for each node is as follows:
1. First consider Pods with lower priority as potential victims on the node
2. Sort the victim list (lower priority and non-PDB-violating victims come first)
3. Remove victims in order, add each removed one to eviction candidates, and observe if the verification function passes
4. Verification function: Try to add pods (pipelined) with higher priority targeting the current node, observe if they can pass predicate; then remove them and observe if they can pass predicate
5. If passed, try to add back the previous eviction candidates in PDB and priority order (to minimize impact), calling verification function after each addition; if verification fails, add to final eviction list
6. If final eviction list is not empty, return it
3. Sort filtered nodes using PreemptNodeOrderFn
4. Schedule Pod to the top-ranked node, evict victims list, and cancel nominatedNodeName of lower priority pods that had nominated this node, moving them from pipeline to pending schedule
### Key Function Modifications
- `SimulateRemoveTaskFn`: Simulate the removal of a task from a node, plugins implement this function to ensure the removal action does not cause topology changes
```go
type SimulateRemoveTaskFn func(ctx context.Context, state *k8sframework.CycleState, taskToSchedule *TaskInfo, taskInfoToRemove *TaskInfo, nodeInfo *NodeInfo) error
```
- `SimulateAddTaskFn`: Simulate the addition of a task to a node, plugins implement this function to ensure the addition action does not cause topology changes
```go
type SimulateAddTaskFn func(ctx context.Context, state *k8sframework.CycleState, taskToSchedule *TaskInfo, taskInfoToAdd *TaskInfo, nodeInfo *NodeInfo) error
```
- `SimulatePredicateFn`: Simulate the predicate check for a task on a node, plugins implement this function to verify if the task can be scheduled to the node while maintaining topology constraints
```go
type SimulatePredicateFn func(ctx context.Context, state *k8sframework.CycleState, task *TaskInfo, nodeInfo *NodeInfo) error
```
- `SimulateAllocatableFn`: Simulate the allocatable check for a node, plugins implement this function to verify if the queue has enough resources to schedule the task while maintaining topology constraints
```go
type SimulateAllocatableFn func(ctx context.Context, state *k8sframework.CycleState, queue *QueueInfo, task *TaskInfo) bool
```
### Limitations
- Current design focuses on single pod preemption scenarios. Does not handle complex topology changes in gang scheduling
- For complex combinations of affinity rules, multiple attempts may be needed to find the optimal solution. Performance impact of simulation scheduling needs to be evaluated in large-scale clusters

View File

@ -0,0 +1,39 @@
# Volume Binding
## Backgrounds
Currently, Volcano does not support the `WaitForFirstConsumer` mode for volume binding.
The current operations for binding volumes are intrusively modified into the scheduler cache, becoming part of the cache's interface.
The `VolumeBinding` plugin, as a plugin also introduced from kube-scheduler, should be introduced into the `predicates` or `nodeorder` plugins like other plugins,
volume binding functionality should not be intrusively modified into the cache.
## Design Details
`BindVolumes`, `AllocateVolumes`, and other intrusive changes to the scheduler cache as interfaces need to be removed,
which are originally part of the binder in the `VolumeBinding` plugin and should not be exposed to the cache.
The extension points involved in the `VolumeBinding` plugin are `PreFilter`, `Filter`, `PreScore`, `Score`, `Reserve`, `Unreserve`, and `PreBind`,
so it is most appropriate to introduce `VolumeBinding` into the `predicates` plugin, and `predicates` is the core plugin.
`VolumeBinding` should be integrated into `predicates` plugin as a default enabled feature. However,
predicates did not implement extension points such as `Score`, `Reserve`, `PreBind`, etc., so the predicates plugin can implement these interfaces as follows:
1. `Score`: For scoring, `predicates` can refer to and reuse the `AddBatchNodeOrderFn` logic of the
[nodeorder](https://github.com/volcano-sh/volcano/blob/7103c18de19821cd278f949fa24c13da350a8c5d/pkg/scheduler/plugins/nodeorder/nodeorder.go#L301-L335) plugin
2. `Reserve` and `Unreserve`: For `Reserve` and `Unreserve`, `predicates` plugin can implement them in event handler's `AllocateFunc` and `DeallocateFunc`, for example,
add following codes for `Reserve`:
```go
func (pp *predicatesPlugin) runReservePlugins(ssn *framework.Session, event *framework.Event) {
// Volume Binding Reserve
if pp.volumeBindingPlugin != nil {
status := pp.volumeBindingPlugin.Reserve(context.TODO(), state, event.Task.Pod, event.Task.Pod.Spec.NodeName)
if !status.IsSuccess() {
event.Err = status.AsError()
return
}
}
// other plugins' Reserve
}
```
3. `PreBind`: For `PreBind`, a new interface called `PreBinder` should be added and the predicates plugin should implement it, contains
two methods `PreBind` and `PreBindRollback`, and `PreBind` method directly calls the `PreBind` extension points of each plugin.
Because the plugin lifecycle is generally in the session, that is, to pre-allocate nodes to pods, while `PreBind` and `Bind` are in the cache,
which are two different goroutines, so if the plugin needs to pass additional information to the `PreBind` method,
it can be set through the `BindContext` parameter of the `PreBind` method, which is a new struct containing a map that can carry the information that the plugin needs to pass to `PreBind`.
The specific implementation details of `PreBind` can be referred to this doc: [PreBind](prebind.md).

View File

@ -0,0 +1,76 @@
# How to Enable Dynamic Resource Allocation (DRA) in Volcano Scheduler
This document describes the steps required to enable Dynamic Resource Allocation (DRA) support in the Volcano scheduler.
## Prerequisites
Before proceeding with the configuration steps, ensure your cluster meets the following prerequisites:
### Configure Cluster Nodes (Containerd)
For nodes running containerd as the container runtime, you must enable the Container Device Interface (CDI) feature.
This is crucial for containerd to properly interact with DRA drivers and inject dynamic resources into Pods.
Modify the containerd configuration file on each node (typically /etc/containerd/config.toml) to ensure the following setting is present:
```toml
# Enable CDI as described in
# https://tags.cncf.io/container-device-interface#containerd-configuration
[plugins."io.containerd.grpc.v1.cri"]
enable_cdi = true
cdi_spec_dirs = ["/etc/cdi", "/var/run/cdi"]
```
After modifying the configuration, restart the containerd service on each node for the changes to take effect. For example: `sudo systemctl restart containerd`
> If you are using other container runtimes, please refer to: [how-to-configure-cdi](https://github.com/cncf-tags/container-device-interface?tab=readme-ov-file#how-to-configure-cdi)
## 1. Configure Kube-apiserver
DRA-related APIs are k8s built-in resources instead of CRD resources, and these resources are not registered by default in v1.32,
so you need to set the startup parameters of kube-apiserver to manually register DRA-related APIs, add or ensure the following flag is present in your kube-apiserver manifest or configuration:
```yaml
--runtime-config=resource.k8s.io/v1beta1=true
```
## 2. Install Volcano With DRA feature gates enabled
When installing Volcano, you need to enable the DRA related feature gates, e.g., `DynamicResourceAllocation` must be enabled when you need to use DRA,
you can also choose to enable the `DRAAdminAccess` feature gate to manage devices as your need.
When you are using helm to install Volcano, you can use following command to install Volcano with DRA feature gates enabled:
```bash
helm install volcano volcano/volcano --namespace volcano-system --create-namespace \
--set custom.scheduler_feature_gates="DynamicResourceAllocation=true" \
# Add other necessary Helm values for your installation
```
When you directly use `kubectl apply -f` to install Volcano, you need to add or ensure the following flag is present in your volcano-scheduler manifest:
```yaml
--feature-gates=DynamicResourceAllocation=true
```
## 3. Configure Volcano Scheduler Plugins
After installing Volcano, you need to configure the Volcano scheduler's plugin configuration to enable the DRA plugin within the predicates plugin arguments.
Locate your Volcano scheduler configuration (A ConfigMap contains the configuration). Find the predicates plugin configuration and add or modify its arguments to enable DRA plugin.
An example snippet of the scheduler configuration (within the volcano-scheduler.conf key of the ConfigMap) might look like this:
```yaml
actions: "enqueue, allocate, backfill"
tiers:
- plugins:
- name: priority
- name: gang
- plugins:
- name: drf
- name: predicates
arguments:
predicate.DynamicResourceAllocationEnable: true
- name: proportion
- name: nodeorder
- name: binpack
```
## 4. Deploy a DRA Driver
To utilize Dynamic Resource Allocation, you need to deploy a DRA driver in your cluster. The driver is responsible for managing the lifecycle of dynamic resources.
For example, you can refer to the [kubernetes-sigs/dra-example-driver](https://github.com/kubernetes-sigs/dra-example-driver) to deploy a example DRA driver for testing.
For some DRA Drivers which have already been used in actual production, you can refer to:
- [NVIDIA/k8s-dra-driver-gpu](https://github.com/NVIDIA/k8s-dra-driver-gpu)
- [intel/intel-resource-drivers-for-kubernetes](https://github.com/intel/intel-resource-drivers-for-kubernetes)

View File

@ -0,0 +1,113 @@
# Usage Document
## Introduction
This document describes how to use the HyperNode network topology auto-discovery feature in Volcano. This feature automatically discovers the network topology within the cluster and creates and maintains HyperNode custom resources (CRs) based on the discovered information. The Volcano scheduler leverages these HyperNode CRs for scheduling decisions, eliminating the need for users to manually maintain HyperNode information.
## Prerequisites
Please [Install Volcano](https://github.com/volcano-sh/volcano/tree/master?tab=readme-ov-file#quick-start-guide) with version >= v1.12.0 first.
## Configuration
The HyperNode network topology discovery feature is configured via a ConfigMap. The ConfigMap contains the configuration for the discovery sources, such as UFM, RoCE, and label, you can modify the configuration according to your own cluster environments.
Please note that you should replace with your Volcano namespace if Volcano is not installed in the default namespace.
### Secret Configuration (Required First Step)
Before configuring the UFM discovery, you must first create a Kubernetes Secret to store your UFM credentials:
```bash
kubectl create secret generic ufm-credentials \
--from-literal=username='your-ufm-username' \
--from-literal=password='your-ufm-password' \
-n volcano-system
```
> Note: Replace your-ufm-username and your-ufm-password with your actual UFM credentials, and adjust the namespace if needed.
### Example ConfigMap
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: volcano-controller-configmap
namespace: volcano-system # Replace with your Volcano namespace if Volcano is not installed in the default namespace.
data:
volcano-controller.conf: |
networkTopologyDiscovery:
- source: ufm
enabled: true
interval: 10m
credentials:
secretRef:
name: ufm-credentials # Replace with the secret name that stores the UFM credentials.
namespace: volcano-system #Replace with the secret namespace that stores the UFM credentials.
config:
endpoint: https://ufm-server:8080
insecureSkipVerify: true
- source: roce
enabled: false
interval: 15m
config:
endpoint: https://roce-server:9090
- source: label
enabled: false
config: {}
```
### Configuration Options
* `source`: The discovery source. Supported values are `ufm`, `roce`, and `label`.
* `enabled`: Whether the discovery source is enabled.
* `interval`: The interval between discovery operations. If not specified, the default value is 1 hour.
* `config`: The configuration for the discovery source. The configuration options vary depending on the discovery source.
* `credentials`: The credentials configuration for accessing the discovery source.
* `secretRef`: Reference to a Kubernetes Secret containing credentials.
* `name`: The name of the Secret.
* `namespace`: The namespace of the Secret.
#### UFM Configuration Options
* `endpoint`: The UFM API endpoint.
* `insecureSkipVerify`: Whether to skip TLS certificate verification. This should only be used in development environments.
#### RoCE Configuration Options(Currently not supported)
* `endpoint`: The RoCE API endpoint.
* `token`: The RoCE API token.
#### Label Configuration Options(Currently not supported)
* No configuration options are currently supported for the label discovery source.
## Verification
1. Check the Volcano controller logs to ensure that the discovery sources are started successfully.
```bash
kubectl logs -n volcano-system -l app=volcano-controllers -c volcano-controllers | grep "Successfully started all network topology discoverers"
```
2. Check the created HyperNode resources.
```bash
kubectl get hypernodes -l volcano.sh/network-topology-source=<source>
```
Replace `<source>` with the discovery source you configured, such as `ufm`.
## Troubleshooting
* If the discovery sources are not started successfully, check the Volcano controller logs for errors.
* If the HyperNode resources are not created, check the discovery source configuration and ensure that the discovery source is able to connect to the network topology data source.
## Best Practices
* Volcano uses Kubernetes-standard Secrets to store sensitive credential information (username/password or token). For more stringent key encryption requirements, users should consider additional mechanisms like [Encrypting Secret Data at Rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).
* The credential Secrets can be placed in a specified namespace for better isolation.
* For UFM discoverer, the controller only needs read access to the specific Secret containing credentials.
* When deploying in production environments, proper RBAC policies should be configured to limit access to Secrets.
* TLS certificate verification should be enabled in production environments to prevent MITM attacks.
* Monitor the Volcano controller logs for errors.
* Set a reasonable discovery interval to avoid overloading the network topology data source.

View File

@ -1,25 +1,63 @@
# Volcano vGPU User guide
# Volcano vGPU User Guide
## Prerequisites
## Background Knowledge of GPU Sharing Modes in Volcano
The list of prerequisites for running the Volcano device plugin is described below:
* NVIDIA drivers > 440
* nvidia-docker version > 2.0 (see how to [install](https://github.com/NVIDIA/nvidia-docker) and it's [prerequisites](https://github.com/nvidia/nvidia-docker/wiki/Installation-\(version-2.0\)#prerequisites))
* docker configured with nvidia as the [default runtime](https://github.com/NVIDIA/nvidia-docker/wiki/Advanced-topics#default-runtime).
* Kubernetes version >= 1.16
* Volcano verison >= 1.9
Volcano supports **two GPU sharing modes** for virtual GPU (vGPU) scheduling:
## Environment setup
### 1. HAMI-core (Software-based vGPU)
### Install volcano
**Description**:
Leverages **VCUDA**, a CUDA API hijacking technique to enforce GPU core and memory usage limits, enabling **software-level virtual GPU slicing**.
Refer to [Install Guide](../../installer/README.md) to install volcano.
**Use case**:
Ideal for environments requiring **fine-grained GPU sharing**. Compatible with all GPU types.
After installed, update the scheduler configuration:
---
```shell script
kubectl edit cm -n volcano-system volcano-scheduler-configmap
### 2. Dynamic MIG (Hardware-level GPU Slicing)
**Description**:
Utilizes **NVIDIA's MIG (Multi-Instance GPU)** technology to partition a physical GPU into isolated instances with **hardware-level performance guarantees**.
**Use case**:
Best for **performance-sensitive** workloads. Requires **MIG-capable GPUs** (e.g., A100, H100).
---
GPU Sharing mode is a node configuration. Volcano supports heterogeneous cluster(i.e a part of node uses HAMi-core while another part uses dynamic MIG), See [volcano-vgpu-device-plugin](https://github.com/Project-HAMi/volcano-vgpu-device-plugin) for configuration and details.
## Installation
To enable vGPU scheduling, the following components must be set up based on the selected mode:
### Common Requirements
* **Prerequisites**:
* NVIDIA driver > 440
* nvidia-docker > 2.0
* Docker configured with `nvidia` as the default runtime
* Kubernetes >= 1.16
* Volcano >= 1.9
* **Install Volcano**:
* Follow instructions in [Volcano Installer Guide](https://github.com/volcano-sh/volcano?tab=readme-ov-file#quick-start-guide)
* **Install Device Plugin**:
* Deploy [`volcano-vgpu-device-plugin`](https://github.com/Project-HAMi/volcano-vgpu-device-plugin)
**Note:** the [vgpu device plugin yaml](https://github.com/Project-HAMi/volcano-vgpu-device-plugin/blob/main/volcano-vgpu-device-plugin.yml) also includes the ***Node GPU mode*** and the ***MIG geometry*** configuration. Please refer to the [vgpu device plugin config](https://github.com/Project-HAMi/volcano-vgpu-device-plugin/blob/main/doc/config.md).
* **Validate Setup**:
Ensure node allocatable resources include:
```yaml
volcano.sh/vgpu-memory: "89424"
volcano.sh/vgpu-number: "8"
```
* **Scheduler Config Update**:
```yaml
kind: ConfigMap
@ -32,106 +70,121 @@ data:
actions: "enqueue, allocate, backfill"
tiers:
- plugins:
- name: priority
- name: gang
- name: conformance
- plugins:
- name: drf
- name: predicates
- name: deviceshare
arguments:
deviceshare.VGPUEnable: true # enable vgpu
- name: predicates
- name: proportion
- name: nodeorder
- name: binpack
deviceshare.VGPUEnable: true # enable vgpu plugin
deviceshare.SchedulePolicy: binpack # scheduling policy. binpack / spread
```
### Install Volcano device plugin
Check with:
Please refer to [volcano vgpu device plugin](https://github.com/Project-HAMi/volcano-vgpu-device-plugin?tab=readme-ov-file#enabling-gpu-support-in-kubernetes)
### Verify environment is ready
Check the node status, it is ok if `volcano.sh/vgpu-memory` and `volcano.sh/vgpu-number` are included in the allocatable resources.
```shell script
$ kubectl get node {node name} -oyaml
...
status:
addresses:
- address: 172.17.0.3
type: InternalIP
- address: volcano-control-plane
type: Hostname
allocatable:
cpu: "4"
ephemeral-storage: 123722704Ki
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 8174332Ki
pods: "110"
volcano.sh/vgpu-memory: "89424"
volcano.sh/vgpu-number: "8" # GPU resource
capacity:
cpu: "4"
ephemeral-storage: 123722704Ki
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 8174332Ki
pods: "110"
volcano.sh/vgpu-memory: "89424"
volcano.sh/vgpu-number: "8" # GPU resource
```bash
kubectl get node {node-name} -o yaml
```
### Running GPU Sharing Jobs
---
VGPU can be requested by both set "volcano.sh/vgpu-number" , "volcano.sh/vgpu-cores" and "volcano.sh/vgpu-memory" in resource.limit
### HAMI-core Usage
```shell script
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
* **Pod Spec**:
```yaml
metadata:
name: gpu-pod1
name: hami-pod
annotations:
volcano.sh/vgpu-mode: "hami-core"
spec:
schedulerName: volcano
containers:
- name: cuda-container
image: nvidia/cuda:9.0-devel
command: ["sleep"]
args: ["100000"]
resources:
limits:
volcano.sh/vgpu-number: 2 # requesting 2 gpu cards
volcano.sh/vgpu-memory: 3000 # (optinal)each vGPU uses 3G device memory
volcano.sh/vgpu-cores: 50 # (optional)each vGPU uses 50% core
EOF
- name: cuda-container
image: nvidia/cuda:9.0-devel
resources:
limits:
volcano.sh/vgpu-number: 1 # requesting 1 gpu cards
volcano.sh/vgpu-cores: 50 # (optional)each vGPU uses 50%
volcano.sh/vgpu-memory: 3000 # (optional)each vGPU uses 3G GPU memory
```
You can validate device memory using nvidia-smi inside container:
---
![img](https://github.com/Project-HAMi/volcano-vgpu-device-plugin/blob/main/doc/hard_limit.jpg)
### Dynamic MIG Usage
> **WARNING:** *if you don't request GPUs when using the device plugin with NVIDIA images all
> the GPUs on the machine will be exposed inside your container.
> The number of vgpu used by a container can not exceed the number of gpus on that node.*
* **Enable MIG Mode**:
### Monitor
If you need to use MIG (Multi-Instance GPU), you must run the following command on the GPU node.
volcano-scheduler-metrics records every GPU usage and limitation, visit the following address to get these metrics.
```
curl {volcano scheduler cluster ip}:8080/metrics
```bash
sudo nvidia-smi -mig 1
```
You can also collect the **GPU utilization**, **GPU memory usage**, **pods' GPU memory limitations** and **pods' GPU memory usage** metrics on nodes by visiting the following addresses:
* **Geometry Config (Optional)**:
The volcano-vgpu-device-plugin automatically generates an initial MIG configuration, which is stored in the `volcano-vgpu-device-config` ConfigMap under the `kube-system` namespace. You can customize this configuration as needed. For more details, refer to the [vgpu device plugin yaml](https://github.com/Project-HAMi/volcano-vgpu-device-plugin/blob/main/volcano-vgpu-device-plugin.yml).
* **Pod Spec with MIG Annotation**:
```yaml
metadata:
name: mig-pod
annotations:
volcano.sh/vgpu-mode: "mig"
spec:
schedulerName: volcano
containers:
- name: cuda-container
image: nvidia/cuda:9.0-devel
resources:
limits:
volcano.sh/vgpu-number: 1
volcano.sh/vgpu-memory: 3000
```
curl {volcano device plugin pod ip}:9394/metrics
Note: Actual memory allocated depends on best-fit MIG slice (e.g., request 3GB → 5GB slice used).
---
## Scheduler Mode Selection
* **Explicit Mode**:
* Use annotation `volcano.sh/vgpu-mode` to force hami-core or MIG mode.
* If annotation is absent, scheduler selects mode based on resource fit and policy.
* **Scheduling Policy**:
* Modes like `binpack` or `spread` influence node selection.
---
## Summary Table
| Mode | Isolation | MIG GPU Required | Annotation | Core/Memory Control | Recommended For |
| ----------- | ---------------- | ---------------- | ---------- | ------------------- | -------------------------- |
| HAMI-core | Software (VCUDA) | No | No | Yes | General workloads |
| Dynamic MIG | Hardware | Yes | Yes | MIG-controlled | Performance-sensitive jobs |
---
## Monitoring
* **Scheduler Metrics**:
```bash
curl http://<volcano-scheduler-ip>:8080/metrics
```
![img](https://github.com/Project-HAMi/volcano-vgpu-device-plugin/blob/main/doc/vgpu_device_plugin_metrics.png)
# Issues and Contributing
* **Device Plugin Metrics**:
```bash
curl http://<plugin-pod-ip>:9394/metrics
```
Metrics include GPU utilization, pod memory usage, and limits.
---
## Issues and Contributions
* File bugs: [Volcano Issues](https://github.com/volcano-sh/volcano/issues)
* Contribute: [Pull Requests Guide](https://help.github.com/articles/using-pull-requests/)
* You can report a bug by [filing a new issue](https://github.com/volcano-sh/volcano/issues)
* You can contribute by opening a [pull request](https://help.github.com/articles/using-pull-requests/)

View File

@ -5,15 +5,6 @@ metadata:
spec:
minAvailable: 1
schedulerName: volcano
priorityClassName: high-priority
policies:
- event: PodEvicted
action: RestartJob
plugins:
ssh: []
env: []
svc: []
maxRetry: 5
queue: default
tasks:
- replicas: 1
@ -42,15 +33,6 @@ metadata:
spec:
minAvailable: 1
schedulerName: volcano
priorityClassName: high-priority
policies:
- event: PodEvicted
action: RestartJob
plugins:
ssh: []
env: []
svc: []
maxRetry: 5
queue: default
tasks:
- replicas: 1
@ -79,15 +61,6 @@ metadata:
spec:
minAvailable: 1
schedulerName: volcano
priorityClassName: high-priority
policies:
- event: PodEvicted
action: RestartJob
plugins:
ssh: []
env: []
svc: []
maxRetry: 5
queue: default
tasks:
- replicas: 1
@ -116,15 +89,6 @@ metadata:
spec:
minAvailable: 1
schedulerName: volcano
priorityClassName: high-priority
policies:
- event: PodEvicted
action: RestartJob
plugins:
ssh: []
env: []
svc: []
maxRetry: 5
queue: default
tasks:
- replicas: 1
@ -153,15 +117,6 @@ metadata:
spec:
minAvailable: 1
schedulerName: volcano
priorityClassName: high-priority
policies:
- event: PodEvicted
action: RestartJob
plugins:
ssh: []
env: []
svc: []
maxRetry: 5
queue: default
tasks:
- replicas: 1

View File

@ -8,53 +8,16 @@ read design at [here](../../docs/design/jobflow).
### Prerequisites
- docker: `18.06`
- Kubernetes: >`1.17`
## startup steps
build image from local
```bash
# get volcano and jobflow source code from github
git clone http://github.com/volcano-sh/volcano.git
git clone https://github.com/BoCloud/JobFlow.git
# build image beyondcent/jobflow:v0.0.1 from local
cd JobFlow
make
make docker-build
```
##### deploy JobFlow from [here](https://github.com/BoCloud/JobFlow#deploy)
```bash
kubectl apply -f https://raw.githubusercontent.com/BoCloud/JobFlow/main/deploy/jobflow.yaml
```
##### deploy Volcano from [here](https://volcano.sh/en/docs/installation/#install-with-yaml-files)
```bash
kubectl apply -f https://raw.githubusercontent.com/volcano-sh/volcano/master/installer/volcano-development.yaml
```
if cert of `jobflow-webhook-service.kube-system.svc` has expired, generate one to replace it.
```bash
# delete expired cert in secrets
kubectl delete secret jobflow-webhook-server-cert -nkube-system
# use gen-admission-secret.sh register new secret
cd volcano
./installer/dockerfile/webhook-manager/gen-admission-secret.sh --service jobflow-webhook-service --namespace kube-system --secret jobflow-webhook-server-cert
# restart jobflow-controller-manager
kubectl delete pod/jobflow-controller-manager-67847d59dd-j8dmc -nkube-system
```
##### run jobflow example
### run jobflow example
```bash
# deploy jobTemplate first
cd volcano
kubectl apply -f example/jobflow/JobTemplate.yaml
kubectl apply -f JobTemplate.yaml
# deploy jobFlow second
kubectl apply -f example/jobflow/JobFlow.yaml
kubectl apply -f JobFlow.yaml
# check them
kubectl get jt

28
go.mod
View File

@ -3,8 +3,9 @@ module volcano.sh/volcano
go 1.23.0
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6
github.com/agiledragon/gomonkey/v2 v2.11.0
github.com/cilium/ebpf v0.9.3
github.com/cilium/ebpf v0.16.0
github.com/containernetworking/cni v1.1.2
github.com/containernetworking/plugins v1.1.1
github.com/elastic/go-elasticsearch/v7 v7.17.7
@ -40,25 +41,41 @@ require (
k8s.io/component-helpers v0.32.2
k8s.io/csi-translation-lib v0.32.2
k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.0.0
k8s.io/kubernetes v1.32.2
k8s.io/metrics v0.0.0
k8s.io/metrics v0.32.2
k8s.io/pod-security-admission v0.0.0
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
sigs.k8s.io/controller-runtime v0.13.0
sigs.k8s.io/yaml v1.4.0
stathat.com/c/consistent v1.0.0
volcano.sh/apis v1.11.1-0.20250423093821-61bafe877200
volcano.sh/apis v1.12.1
)
require (
cel.dev/expr v0.18.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/bits-and-blooms/bitset v1.2.0 // indirect
github.com/container-storage-interface/spec v1.9.0 // indirect
github.com/containerd/containerd/api v1.7.19 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/ttrpc v1.2.5 // indirect
github.com/cyphar/filepath-securejoin v0.3.4 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/euank/go-kmsg-parser v2.0.0+incompatible // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/karrick/godirwalk v1.17.0 // indirect
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
@ -130,7 +147,7 @@ require (
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/term v0.31.0 // indirect
@ -159,7 +176,6 @@ require (
replace (
cloud.google.com/go => cloud.google.com/go v0.100.2
github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.3
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
google.golang.org/grpc => google.golang.org/grpc v1.57.0
k8s.io/api => k8s.io/api v0.32.2

105
go.sum
View File

@ -1,6 +1,11 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab h1:UKkYhof1njT1/xq4SEg5z+VpTgjmNeHwPGRQl7takDI=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@ -9,39 +14,47 @@ github.com/agiledragon/gomonkey/v2 v2.11.0 h1:5oxSgA+tC1xuGsrIorR+sYiziYltmJyEZ9
github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.9.3 h1:5KtxXZU+scyERvkJMEm16TbScVvuuMrlhPly78ZMbSc=
github.com/cilium/ebpf v0.9.3/go.mod h1:w27N4UjpaQ9X/DGrSugxUG+H+NhgntDuPb5lCzxCn8A=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok=
github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE=
github.com/container-storage-interface/spec v1.9.0 h1:zKtX4STsq31Knz3gciCYCi1SXtO2HJDecIjDVboYavY=
github.com/container-storage-interface/spec v1.9.0/go.mod h1:ZfDu+3ZRyeVqxZM0Ds19MVLkN2d1XJ5MAfi1L3VjlT0=
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU=
github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso=
github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g=
github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ=
github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw=
github.com/containernetworking/plugins v1.1.1 h1:+AGfFigZ5TiQH00vhR8qPeSatj53eNGz0C1d3wVYlHE=
github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -50,20 +63,24 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU=
github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elastic/go-elasticsearch/v7 v7.17.7 h1:pcYNfITNPusl+cLwLN6OLmVT+F73Els0nbaWOmYachs=
github.com/elastic/go-elasticsearch/v7 v7.17.7/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/euank/go-kmsg-parser v2.0.0+incompatible h1:cHD53+PLQuuQyLZeriD1V/esuG4MuU0Pjs5y6iknohY=
github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
@ -85,6 +102,8 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
@ -104,7 +123,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@ -120,7 +138,6 @@ github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYu
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@ -167,6 +184,8 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI=
github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@ -178,23 +197,31 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@ -213,12 +240,12 @@ github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k=
github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runc v1.2.1 h1:mQkmeFSUxqFaVmvIn1VQPeQIKpHFya5R07aJw0DKQa8=
github.com/opencontainers/runc v1.2.1/go.mod h1:/PXzF0h531HTMsYQnmxXkBD7YaGShm/2zcRB79dksUc=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -241,12 +268,7 @@ github.com/prometheus/prometheus v0.39.1 h1:abZM6A+sKAv2eKTbRIaHq4amM/nT07MuxRm0
github.com/prometheus/prometheus v0.39.1/go.mod h1:GjQjgLhHMc0oo4Ko7qt/yBSJMY4hUoiAZwsYQgjaePA=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@ -262,7 +284,8 @@ github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8w
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -271,14 +294,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.3.1-0.20240905180732-b1ce50cfa9be h1:xdCMvyhnKzaepIUgVpUmTJo/+H1AQ7HuFYn1hv7/Neo=
github.com/vishvananda/netlink v1.3.1-0.20240905180732-b1ce50cfa9be/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@ -349,11 +368,10 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -366,23 +384,18 @@ golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -489,6 +502,8 @@ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJ
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/kube-scheduler v0.32.2 h1:vBm6iIjWaD10OPmtkt/503LTKvrN8dWVceeBcpKj/ns=
k8s.io/kube-scheduler v0.32.2/go.mod h1:dD5yuYpnsCfgZmzvncUNPdvXGJXA1hw3gXq7DH3+aCQ=
k8s.io/kubectl v0.32.2 h1:TAkag6+XfSBgkqK9I7ZvwtF0WVtUAvK8ZqTt+5zi1Us=
k8s.io/kubectl v0.32.2/go.mod h1:+h/NQFSPxiDZYX/WZaWw9fwYezGLISP0ud8nQKg+3g8=
k8s.io/kubelet v0.32.2 h1:WFTSYdt3BB1aTApDuKNI16x/4MYqqX8WBBBBh3KupDg=
k8s.io/kubelet v0.32.2/go.mod h1:cC1ms5RS+lu0ckVr6AviCQXHLSPKEBC3D5oaCBdTGkI=
k8s.io/kubernetes v1.32.2 h1:mShetlA102UpjRVSGzB+5vjJwy8oPy8FMWrkTH5f37o=
@ -497,6 +512,8 @@ k8s.io/metrics v0.32.2 h1:7t/rZzTHFrGa9f94XcgLlm3ToAuJtdlHANcJEHlYl9g=
k8s.io/metrics v0.32.2/go.mod h1:VL3nJpzcgB6L5nSljkkzoE0nilZhVgcjCfNRgoylaIQ=
k8s.io/mount-utils v0.32.2 h1:aDwp+ucWiVnDr/LpRg88/dsXf/vm6gI1VZkYH3+3+Vw=
k8s.io/mount-utils v0.32.2/go.mod h1:Kun5c2svjAPx0nnvJKYQWhfeNW+O0EpzHgRhDcYoSY0=
k8s.io/pod-security-admission v0.32.2 h1:zDfAb/t0LbNU3z0ZMHtCb1zp8x05gWCGhmBYpUptm9A=
k8s.io/pod-security-admission v0.32.2/go.mod h1:yxMPB3i1pGMLfxbe4BiWMuowMD7cdHR32y4nCj4wH+s=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo=
@ -511,5 +528,5 @@ sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
stathat.com/c/consistent v1.0.0 h1:ezyc51EGcRPJUxfHGSgJjWzJdj3NiMU9pNfLNGiXV0c=
stathat.com/c/consistent v1.0.0/go.mod h1:QkzMWzcbB+yQBL2AttO6sgsQS/JSTapcDISJalmCDS0=
volcano.sh/apis v1.11.1-0.20250423093821-61bafe877200 h1:O5khbEoLxwLBcoQRE6jsax8kAVMSNj1qPDLdPoEcaek=
volcano.sh/apis v1.11.1-0.20250423093821-61bafe877200/go.mod h1:0XNNnIOevJSYNiXRmwhXUrYCcCcWcBeTY0nxrlkk03A=
volcano.sh/apis v1.12.1 h1:yq5dVj/g21vnWObCIKsJKPhMoThpzDrHDD/GMouYVxk=
volcano.sh/apis v1.12.1/go.mod h1:0XNNnIOevJSYNiXRmwhXUrYCcCcWcBeTY0nxrlkk03A=

View File

@ -1,15 +1,29 @@
# this config file contains all config fields with comments
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
DynamicResourceAllocation: true
DRAResourceClaimDeviceStatus: true
containerdConfigPatches:
# Enable CDI as described in
# https://tags.cncf.io/container-device-interface#containerd-configuration
- |-
[plugins."io.containerd.grpc.v1.cri"]
enable_cdi = true
# 1 control plane node and 4 workers
nodes:
# the control plane node config
- role: control-plane
kubeadmConfigPatches:
- |
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
containerLogMaxSize: "50Mi"
- |
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
containerLogMaxSize: "50Mi"
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
runtime-config: "resource.k8s.io/v1beta1=true"
# the four workers
- role: worker
- role: worker

View File

@ -93,6 +93,7 @@ tail -n +2 ${VOLCANO_CRD_DIR}/bases/bus.volcano.sh_commands.yaml > ${HELM_VOLCAN
tail -n +2 ${VOLCANO_CRD_DIR}/bases/scheduling.volcano.sh_podgroups.yaml > ${HELM_VOLCANO_CRD_DIR}/bases/scheduling.volcano.sh_podgroups.yaml
tail -n +2 ${VOLCANO_CRD_DIR}/bases/scheduling.volcano.sh_queues.yaml > ${HELM_VOLCANO_CRD_DIR}/bases/scheduling.volcano.sh_queues.yaml
tail -n +2 ${VOLCANO_CRD_DIR}/bases/nodeinfo.volcano.sh_numatopologies.yaml > ${HELM_VOLCANO_CRD_DIR}/bases/nodeinfo.volcano.sh_numatopologies.yaml
tail -n +2 ${VOLCANO_CRD_DIR}/bases/topology.volcano.sh_hypernodes.yaml > ${HELM_VOLCANO_CRD_DIR}/bases/topology.volcano.sh_hypernodes.yaml
# sync jobflow bases
tail -n +2 ${JOBFLOW_CRD_DIR}/bases/flow.volcano.sh_jobflows.yaml > ${HELM_JOBFLOW_CRD_DIR}/bases/flow.volcano.sh_jobflows.yaml
@ -136,6 +137,7 @@ ${HELM_BIN_DIR}/helm template ${VK_ROOT}/installer/helm/chart/volcano --namespac
-s templates/scheduling_v1beta1_podgroup.yaml \
-s templates/scheduling_v1beta1_queue.yaml \
-s templates/nodeinfo_v1alpha1_numatopologies.yaml \
-s templates/topology_v1alpha1_hypernodes.yaml \
-s templates/webhooks.yaml \
>> ${DEPLOYMENT_FILE}

6
hack/oss-fuzz-build.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash -eu
printf "package job\nimport _ \"github.com/AdamKorcz/go-118-fuzz-build/testing\"\n" >"$SRC"/volcano/pkg/controllers/job/register.go
go mod tidy
compile_native_go_fuzzer volcano.sh/volcano/pkg/controllers/job FuzzApplyPolicies FuzzApplyPolicies
compile_native_go_fuzzer volcano.sh/volcano/pkg/controllers/job FuzzCreateJobPod FuzzCreateJobPod

View File

@ -86,6 +86,7 @@ custom:
effect: "NoSchedule"
default_ns:
node-role.kubernetes.io/control-plane: ""
scheduler_feature_gates: ${FEATURE_GATES}
EOF
}
@ -151,30 +152,35 @@ case ${E2E_TYPE} in
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress ./test/e2e/schedulingbase/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress ./test/e2e/schedulingaction/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress ./test/e2e/vcctl/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress --focus="DRA E2E Test" ./test/e2e/dra/
;;
"JOBP")
echo "Running parallel job e2e suite..."
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --nodes=4 --compilers=4 --randomize-all --randomize-suites --fail-on-pending --cover --trace --race --slow-spec-threshold='30s' --progress ./test/e2e/jobp/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -v -r --nodes=4 --compilers=4 --randomize-all --randomize-suites --fail-on-pending --cover --trace --race --slow-spec-threshold='30s' --progress ./test/e2e/jobp/
;;
"JOBSEQ")
echo "Running sequence job e2e suite..."
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress ./test/e2e/jobseq/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -v -r --slow-spec-threshold='30s' --progress ./test/e2e/jobseq/
;;
"SCHEDULINGBASE")
echo "Running scheduling base e2e suite..."
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress ./test/e2e/schedulingbase/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -v -r --slow-spec-threshold='30s' --progress ./test/e2e/schedulingbase/
;;
"SCHEDULINGACTION")
echo "Running scheduling action e2e suite..."
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress ./test/e2e/schedulingaction/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -v -r --slow-spec-threshold='30s' --progress ./test/e2e/schedulingaction/
;;
"VCCTL")
echo "Running vcctl e2e suite..."
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress ./test/e2e/vcctl/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -v -r --slow-spec-threshold='30s' --progress ./test/e2e/vcctl/
;;
"STRESS")
echo "Running stress e2e suite..."
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -r --slow-spec-threshold='30s' --progress ./test/e2e/stress/
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -v -r --slow-spec-threshold='30s' --progress ./test/e2e/stress/
;;
"DRA")
echo "Running dra e2e suite..."
KUBECONFIG=${KUBECONFIG} GOOS=${OS} ginkgo -v -r --slow-spec-threshold='30s' --progress --focus="DRA E2E Test" ./test/e2e/dra/
;;
esac

View File

@ -100,6 +100,7 @@ The following are the list configurable parameters of Volcano Chart and their de
|`custom.leader_elect_enable`|Whether to Enable leader elect|`false`|
|`custom.admission_config_override`|Override admission configmap|`~`|
|`custom.scheduler_config_override`|Override scheduler configmap|`~`|
| `custom.controller_config_override`| Override controller configmap|`~`|
|`custom.default_affinity`|Default affinity for Admission/Controller/Scheduler pods|`~`|
|`custom.admission_affinity`|Affinity for Admission pods|`~`|
|`custom.controller_affinity`|Affinity for Controller pods|`~`|

View File

@ -38,6 +38,18 @@ spec:
format: int32
minimum: 1
type: integer
networkTopology:
properties:
highestTierAllowed:
default: 1
type: integer
mode:
default: hard
enum:
- hard
- soft
type: string
type: object
plugins:
additionalProperties:
items:

View File

@ -11,6 +11,8 @@ tiers:
- name: drf
enablePreemptable: false
- name: predicates
arguments:
predicate.DynamicResourceAllocationEnable: true
- name: proportion
- name: nodeorder
- name: binpack

View File

@ -56,6 +56,18 @@ spec:
format: int32
minimum: 1
type: integer
networkTopology:
properties:
highestTierAllowed:
default: 1
type: integer
mode:
default: hard
enum:
- hard
- soft
type: string
type: object
plugins:
additionalProperties:
items:

View File

@ -88,6 +88,24 @@ spec:
if there's not enough resources to start each task, the scheduler
will not start anyone.
type: object
networkTopology:
description: NetworkTopology defines the NetworkTopology config, this
field works in conjunction with network topology feature and hyperNode
CRD.
properties:
highestTierAllowed:
default: 1
description: HighestTierAllowed specifies the highest tier that
a job allowed to cross when scheduling.
type: integer
mode:
default: hard
description: Mode specifies the mode of the network topology constrain.
enum:
- hard
- soft
type: string
type: object
priorityClassName:
description: |-
If specified, indicates the PodGroup's priority. "system-node-critical" and

View File

@ -0,0 +1,224 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.0
name: hypernodes.topology.volcano.sh
spec:
group: topology.volcano.sh
names:
kind: HyperNode
listKind: HyperNodeList
plural: hypernodes
shortNames:
- hn
singular: hypernode
scope: Cluster
versions:
- additionalPrinterColumns:
- jsonPath: .spec.tier
name: Tier
type: string
- jsonPath: .status.nodeCount
name: NodeCount
type: integer
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: HyperNode represents a collection of nodes sharing similar network
topology or performance characteristics.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: Spec defines the desired configuration of the HyperNode.
properties:
members:
description: Members defines a list of node groups or individual nodes
included in the HyperNode.
items:
description: MemberSpec represents a specific node or a hyperNodes
in the hyperNode.
properties:
selector:
description: Selector defines the selection rules for this member.
properties:
exactMatch:
description: ExactMatch defines the exact match criteria.
properties:
name:
description: Name specifies the exact name of the node
to match.
type: string
type: object
labelMatch:
description: LabelMatch defines the labels match criteria
(only take effect when Member Type is "Node").
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
description: |-
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions, whose key field is "key", the
operator is "In", and the values array contains only "value". The requirements are ANDed.
type: object
type: object
x-kubernetes-map-type: atomic
regexMatch:
description: RegexMatch defines the regex match criteria.
properties:
pattern:
description: Pattern defines the regex pattern to match
node names.
type: string
type: object
type: object
x-kubernetes-validations:
- message: Either ExactMatch or RegexMatch or LabelMatch must
be specified
rule: has(self.exactMatch) || has(self.regexMatch) || has(self.labelMatch)
- message: Only one of ExactMatch, RegexMatch, or LabelMatch
can be specified
rule: '(has(self.exactMatch) ? 1 : 0) + (has(self.regexMatch)
? 1 : 0) + (has(self.labelMatch) ? 1 : 0) <= 1'
type:
description: Type specifies the member type.
enum:
- Node
- HyperNode
type: string
required:
- type
type: object
type: array
tier:
description: Tier categorizes the performance level of the HyperNode.
type: integer
required:
- tier
type: object
status:
description: Status provides the current state of the HyperNode.
properties:
conditions:
description: Conditions provide details about the current state of
the HyperNode.
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
nodeCount:
description: NodeCount is the total number of nodes currently in the
HyperNode.
format: int64
minimum: 0
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@ -210,9 +210,6 @@ rules:
- apiGroups: [""]
resources: [ "nodes", "nodes/status" ]
verbs: [ "get", "list", "watch", "update", "patch" ]
- apiGroups: [ "" ]
resources: [ "secrets" ]
verbs: [ "get", "list", "watch" ]
- apiGroups: [ "" ]
resources: [ "configmaps" ]
verbs: [ "get", "list", "watch", "create", "update" ]

View File

@ -85,6 +85,9 @@ rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "create", "update", "watch"]
- apiGroups: ["topology.volcano.sh"]
resources: ["hypernodes", "hypernodes/status"]
verbs: ["list", "watch", "get", "create", "delete", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@ -189,6 +192,11 @@ spec:
securityContext:
{{- toYaml $controller_main_csc | nindent 14 }}
{{- end }}
env:
- name: KUBE_POD_NAMESPACE
value: {{ .Release.Namespace }}
- name: HELM_RELEASE_NAME
value: {{ .Release.Name }}
---
apiVersion: v1
kind: Service
@ -213,4 +221,20 @@ spec:
selector:
app: volcano-controller
type: ClusterIP
{{- end }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-controller-configmap
namespace: {{ .Release.Namespace }}
{{- if .Values.custom.common_labels }}
labels:
{{- toYaml .Values.custom.common_labels | nindent 4 }}
{{- end }}
data:
volcano-controller.conf: |
{{- if .Values.custom.controller_config_override }}
{{ toYaml .Values.custom.controller_config_override | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -91,13 +91,16 @@ rules:
verbs: ["get", "list", "watch", "create", "delete", "update"]
- apiGroups: ["scheduling.incubator.k8s.io", "scheduling.volcano.sh"]
resources: ["queues/status"]
verbs: ["patch"]
verbs: ["update"]
- apiGroups: ["scheduling.incubator.k8s.io", "scheduling.volcano.sh"]
resources: ["podgroups"]
verbs: ["list", "watch", "update"]
- apiGroups: ["nodeinfo.volcano.sh"]
resources: ["numatopologies"]
verbs: ["get", "list", "watch", "delete"]
- apiGroups: ["topology.volcano.sh"]
resources: ["hypernodes", "hypernodes/status"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "create", "delete", "update"]
@ -107,6 +110,15 @@ rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "create", "update", "watch"]
- apiGroups: ["resource.k8s.io"]
resources: ["resourceclaims"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: ["resource.k8s.io"]
resources: ["resourceclaims/status"]
verbs: ["update"]
- apiGroups: ["resource.k8s.io"]
resources: ["deviceclasses","resourceslices"]
verbs: ["get", "list", "watch", "create"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@ -208,6 +220,9 @@ spec:
{{- if .Values.custom.scheduler_plugins_dir }}
- --plugins-dir={{ .Values.custom.scheduler_plugins_dir }}
{{- end }}
{{- if .Values.custom.scheduler_feature_gates }}
- --feature-gates={{ .Values.custom.scheduler_feature_gates }}
{{- end }}
- -v={{.Values.custom.scheduler_log_level}}
- 2>&1
env:

View File

@ -0,0 +1 @@
{{- tpl ($.Files.Get (printf "crd/%s/topology.volcano.sh_hypernodes.yaml" (include "crd_version" .))) . }}

View File

@ -0,0 +1,21 @@
# permissions for end users to edit vcjobs.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: vcjob-editor-role
{{- if or .Values.custom.aggregationRule_labels .Values.custom.common_labels }}
labels:
{{- if .Values.custom.aggregationRule_labels }}
{{- toYaml .Values.custom.aggregationRule_labels | nindent 4 }}
{{- end }}
{{- if .Values.custom.common_labels }}
{{- toYaml .Values.custom.common_labels | nindent 4 }}
{{- end }}
{{- end }}
rules:
- apiGroups: ["batch.volcano.sh"]
resources: ["jobs"]
verbs: ["create", "get", "list", "update", "delete"]
- apiGroups: ["bus.volcano.sh"]
resources: ["commands"]
verbs: ["create", "get", "list"]

View File

@ -0,0 +1,18 @@
# permissions for end users to view vcjobs.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: vcjob-viewer-role
{{- if or .Values.custom.aggregationRule_labels .Values.custom.common_labels }}
labels:
{{- if .Values.custom.aggregationRule_labels }}
{{- toYaml .Values.custom.aggregationRule_labels | nindent 4 }}
{{- end }}
{{- if .Values.custom.common_labels }}
{{- toYaml .Values.custom.common_labels | nindent 4 }}
{{- end }}
{{- end }}
rules:
- apiGroups: ["batch.volcano.sh"]
resources: ["jobs"]
verbs: ["get", "list"]

View File

@ -241,6 +241,54 @@ webhooks:
timeoutSeconds: 10
{{- end }}
{{- if .Values.custom.enabled_admissions | regexMatch "/jobflows/validate" }}
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: volcano-admission-service-jobflows-validate
{{- if .Values.custom.common_labels }}
labels:
{{- toYaml .Values.custom.common_labels | nindent 4 }}
{{- end }}
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: {{ .Release.Name }}-admission-service
namespace: {{ .Release.Namespace }}
path: /jobflows/validate
port: 443
failurePolicy: Fail
matchPolicy: Equivalent
name: validatejob.volcano.sh
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values:
- {{ .Release.Namespace }}
- kube-system
{{- if .Values.custom.webhooks_namespace_selector_expressions }}
{{- toYaml .Values.custom.webhooks_namespace_selector_expressions | nindent 8 }}
{{- end }}
objectSelector: {}
rules:
- apiGroups:
- flow.volcano.sh
apiVersions:
- v1alpha1
operations:
- CREATE
- UPDATE
resources:
- jobflows
scope: '*'
sideEffects: NoneOnDryRun
timeoutSeconds: 10
{{- end }}
{{- if .Values.custom.enabled_admissions | regexMatch "/pods/validate" }}
---
@ -385,4 +433,39 @@ webhooks:
timeoutSeconds: 10
{{- end }}
{{- if .Values.custom.enabled_admissions | regexMatch "/hypernodes/validate" }}
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: volcano-admission-service-hypernodes-validate
{{- if .Values.custom.common_labels }}
labels:
{{- toYaml .Values.custom.common_labels | nindent 4 }}
{{- end }}
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: {{ .Release.Name }}-admission-service
namespace: {{ .Release.Namespace }}
path: /hypernodes/validate
port: 443
failurePolicy: Fail
matchPolicy: Equivalent
name: validatehypernodes.volcano.sh
rules:
- apiGroups:
- topology.volcano.sh
apiVersions:
- v1alpha1
operations:
- CREATE
- UPDATE
resources:
- hypernodes
sideEffects: None
timeoutSeconds: 10
{{- end }}
{{- end }}

View File

@ -34,10 +34,10 @@ custom:
scheduler_kube_api_burst: 2000
scheduler_schedule_period: 1s
scheduler_node_worker_threads: 20
enabled_admissions: "/jobs/mutate,/jobs/validate,/podgroups/validate,/queues/mutate,/queues/validate"
enabled_admissions: "/jobs/mutate,/jobs/validate,/podgroups/validate,/queues/mutate,/queues/validate,/hypernodes/validate"
colocation_enable: false
# Override the configuration for admission or scheduler.
# Override the configuration for admission, scheduler or scheduler.
# For example:
#
# scheduler_config_override: |
@ -56,8 +56,22 @@ custom:
# - name: proportion
# - name: nodeorder
# - name: binpack
# controller_config_override: |
# networkTopologyDiscovery:
# - source: ufm
# enabled: true
# interval: 10m
# credentials:
# secretRef:
# name: ufm-credentials
# namespace: volcano-system
# config:
# endpoint: https://ufm-server:8080
# insecureSkipVerify: true
admission_config_override: ~
scheduler_config_override: ~
controller_config_override: ~
# Specify affinity for all main Volcano components or per component.
# For example:
@ -136,6 +150,8 @@ custom:
# Specify labels for all Volcano helm chart objects except for CRDs
common_labels: ~
# Specify labels for aggregationRule
aggregationRule_labels: ~
# Specify resources for Volcano main component deployments and pods
# For example:
@ -210,6 +226,9 @@ custom:
# Specify agent cni config path.
agent_cni_config_path: /etc/cni/net.d/cni.conflist
# Specify feature gates for components
scheduler_feature_gates: ~
service:
# @param service.ipFamilyPolicy [string], support SingleStack, PreferDualStack and RequireDualStack
#

View File

@ -19,9 +19,6 @@ rules:
- apiGroups: [""]
resources: [ "nodes", "nodes/status" ]
verbs: [ "get", "list", "watch", "update", "patch" ]
- apiGroups: [ "" ]
resources: [ "secrets" ]
verbs: [ "get", "list", "watch" ]
- apiGroups: [ "" ]
resources: [ "configmaps" ]
verbs: [ "get", "list", "watch", "create", "update" ]

View File

@ -140,7 +140,7 @@ spec:
priorityClassName: system-cluster-critical
containers:
- args:
- --enabled-admission=/jobs/mutate,/jobs/validate,/podgroups/validate,/queues/mutate,/queues/validate
- --enabled-admission=/jobs/mutate,/jobs/validate,/podgroups/validate,/queues/mutate,/queues/validate,/hypernodes/validate
- --tls-cert-file=/admission.local.config/certificates/tls.crt
- --tls-private-key-file=/admission.local.config/certificates/tls.key
- --ca-cert-file=/admission.local.config/certificates/ca.crt
@ -323,6 +323,18 @@ spec:
format: int32
minimum: 1
type: integer
networkTopology:
properties:
highestTierAllowed:
default: 1
type: integer
mode:
default: hard
enum:
- hard
- soft
type: string
type: object
plugins:
additionalProperties:
items:
@ -4396,6 +4408,15 @@ metadata:
namespace: volcano-system
---
# Source: volcano/templates/controllers.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: volcano-controller-configmap
namespace: volcano-system
data:
volcano-controller.conf: |
---
# Source: volcano/templates/controllers.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
@ -4461,6 +4482,9 @@ rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "create", "update", "watch"]
- apiGroups: ["topology.volcano.sh"]
resources: ["hypernodes", "hypernodes/status"]
verbs: ["list", "watch", "get", "create", "delete", "update", "patch"]
---
# Source: volcano/templates/controllers.yaml
kind: ClusterRoleBinding
@ -4548,6 +4572,11 @@ spec:
- ALL
runAsNonRoot: true
runAsUser: 1000
env:
- name: KUBE_POD_NAMESPACE
value: volcano-system
- name: HELM_RELEASE_NAME
value: volcano
---
# Source: volcano/templates/scheduler.yaml
apiVersion: v1
@ -4636,13 +4665,16 @@ rules:
verbs: ["get", "list", "watch", "create", "delete", "update"]
- apiGroups: ["scheduling.incubator.k8s.io", "scheduling.volcano.sh"]
resources: ["queues/status"]
verbs: ["patch"]
verbs: ["update"]
- apiGroups: ["scheduling.incubator.k8s.io", "scheduling.volcano.sh"]
resources: ["podgroups"]
verbs: ["list", "watch", "update"]
- apiGroups: ["nodeinfo.volcano.sh"]
resources: ["numatopologies"]
verbs: ["get", "list", "watch", "delete"]
- apiGroups: ["topology.volcano.sh"]
resources: ["hypernodes", "hypernodes/status"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "create", "delete", "update"]
@ -4652,6 +4684,15 @@ rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "create", "update", "watch"]
- apiGroups: ["resource.k8s.io"]
resources: ["resourceclaims"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: ["resource.k8s.io"]
resources: ["resourceclaims/status"]
verbs: ["update"]
- apiGroups: ["resource.k8s.io"]
resources: ["deviceclasses","resourceslices"]
verbs: ["get", "list", "watch", "create"]
---
# Source: volcano/templates/scheduler.yaml
kind: ClusterRoleBinding
@ -4845,6 +4886,24 @@ spec:
if there's not enough resources to start each task, the scheduler
will not start anyone.
type: object
networkTopology:
description: NetworkTopology defines the NetworkTopology config, this
field works in conjunction with network topology feature and hyperNode
CRD.
properties:
highestTierAllowed:
default: 1
description: HighestTierAllowed specifies the highest tier that
a job allowed to cross when scheduling.
type: integer
mode:
default: hard
description: Mode specifies the mode of the network topology constrain.
enum:
- hard
- soft
type: string
type: object
priorityClassName:
description: |-
If specified, indicates the PodGroup's priority. "system-node-critical" and
@ -5232,6 +5291,232 @@ spec:
served: true
storage: true
---
# Source: volcano/templates/topology_v1alpha1_hypernodes.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.0
name: hypernodes.topology.volcano.sh
spec:
group: topology.volcano.sh
names:
kind: HyperNode
listKind: HyperNodeList
plural: hypernodes
shortNames:
- hn
singular: hypernode
scope: Cluster
versions:
- additionalPrinterColumns:
- jsonPath: .spec.tier
name: Tier
type: string
- jsonPath: .status.nodeCount
name: NodeCount
type: integer
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: HyperNode represents a collection of nodes sharing similar network
topology or performance characteristics.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: Spec defines the desired configuration of the HyperNode.
properties:
members:
description: Members defines a list of node groups or individual nodes
included in the HyperNode.
items:
description: MemberSpec represents a specific node or a hyperNodes
in the hyperNode.
properties:
selector:
description: Selector defines the selection rules for this member.
properties:
exactMatch:
description: ExactMatch defines the exact match criteria.
properties:
name:
description: Name specifies the exact name of the node
to match.
type: string
type: object
labelMatch:
description: LabelMatch defines the labels match criteria
(only take effect when Member Type is "Node").
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
description: |-
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions, whose key field is "key", the
operator is "In", and the values array contains only "value". The requirements are ANDed.
type: object
type: object
x-kubernetes-map-type: atomic
regexMatch:
description: RegexMatch defines the regex match criteria.
properties:
pattern:
description: Pattern defines the regex pattern to match
node names.
type: string
type: object
type: object
x-kubernetes-validations:
- message: Either ExactMatch or RegexMatch or LabelMatch must
be specified
rule: has(self.exactMatch) || has(self.regexMatch) || has(self.labelMatch)
- message: Only one of ExactMatch, RegexMatch, or LabelMatch
can be specified
rule: '(has(self.exactMatch) ? 1 : 0) + (has(self.regexMatch)
? 1 : 0) + (has(self.labelMatch) ? 1 : 0) <= 1'
type:
description: Type specifies the member type.
enum:
- Node
- HyperNode
type: string
required:
- type
type: object
type: array
tier:
description: Tier categorizes the performance level of the HyperNode.
type: integer
required:
- tier
type: object
status:
description: Status provides the current state of the HyperNode.
properties:
conditions:
description: Conditions provide details about the current state of
the HyperNode.
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
nodeCount:
description: NodeCount is the total number of nodes currently in the
HyperNode.
format: int64
minimum: 0
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}
---
# Source: volcano/templates/webhooks.yaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
@ -5427,6 +5712,36 @@ webhooks:
sideEffects: NoneOnDryRun
timeoutSeconds: 10
---
# Source: volcano/templates/webhooks.yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: volcano-admission-service-hypernodes-validate
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: volcano-admission-service
namespace: volcano-system
path: /hypernodes/validate
port: 443
failurePolicy: Fail
matchPolicy: Equivalent
name: validatehypernodes.volcano.sh
rules:
- apiGroups:
- topology.volcano.sh
apiVersions:
- v1alpha1
operations:
- CREATE
- UPDATE
resources:
- hypernodes
sideEffects: None
timeoutSeconds: 10
---
# Source: jobflow/templates/flow_v1alpha1_jobflows.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
@ -5468,6 +5783,18 @@ spec:
format: int32
minimum: 1
type: integer
networkTopology:
properties:
highestTierAllowed:
default: 1
type: integer
mode:
default: hard
enum:
- hard
- soft
type: string
type: object
plugins:
additionalProperties:
items:

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright The containerd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright The containerd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright The containerd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2015 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,22 @@
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2017, Karrick McDermott
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2014, OmniTI Computer Consulting, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2013-2018 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,18 +1,20 @@
Copyright (c) 2014 Will Fitzgerald. All rights reserved.
Copyright (c) 2014 The Go-FlowRate Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
* Neither the name of the go-flowrate project nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,45 +0,0 @@
/*
Copyright 2019 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package queue
import (
"os"
"path/filepath"
"github.com/spf13/cobra"
"volcano.sh/volcano/pkg/cli/util"
)
type commonFlags struct {
Master string
Kubeconfig string
SchedulerName string
}
func initFlags(cmd *cobra.Command, cf *commonFlags) {
cmd.Flags().StringVarP(&cf.SchedulerName, "scheduler", "", "volcano", "the scheduler for this job")
cmd.Flags().StringVarP(&cf.Master, "master", "s", "", "the address of apiserver")
kubeConfFile := os.Getenv("KUBECONFIG")
if kubeConfFile == "" {
if home := util.HomeDir(); home != "" {
kubeConfFile = filepath.Join(home, ".kube", "config")
}
}
cmd.Flags().StringVarP(&cf.Kubeconfig, "kubeconfig", "k", kubeConfFile, "(optional) absolute path to the kubeconfig file")
}

View File

@ -25,10 +25,11 @@ import (
schedulingv1beta1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
"volcano.sh/apis/pkg/client/clientset/versioned"
"volcano.sh/volcano/pkg/cli/util"
)
type createFlags struct {
commonFlags
util.CommonFlags
Name string
Weight int32
@ -40,7 +41,7 @@ var createQueueFlags = &createFlags{}
// InitCreateFlags is used to init all flags during queue creating.
func InitCreateFlags(cmd *cobra.Command) {
initFlags(cmd, &createQueueFlags.commonFlags)
util.InitFlags(cmd, &createQueueFlags.CommonFlags)
cmd.Flags().StringVarP(&createQueueFlags.Name, "name", "n", "test", "the name of queue")
cmd.Flags().Int32VarP(&createQueueFlags.Weight, "weight", "w", 1, "the weight of the queue")
@ -50,7 +51,7 @@ func InitCreateFlags(cmd *cobra.Command) {
// CreateQueue create queue.
func CreateQueue(ctx context.Context) error {
config, err := buildConfig(createQueueFlags.Master, createQueueFlags.Kubeconfig)
config, err := util.BuildConfig(createQueueFlags.Master, createQueueFlags.Kubeconfig)
if err != nil {
return err
}

View File

@ -20,15 +20,16 @@ import (
"context"
"fmt"
"volcano.sh/apis/pkg/client/clientset/versioned"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"volcano.sh/apis/pkg/client/clientset/versioned"
"volcano.sh/volcano/pkg/cli/util"
)
type deleteFlags struct {
commonFlags
util.CommonFlags
// Name is name of queue
Name string
@ -38,14 +39,14 @@ var deleteQueueFlags = &deleteFlags{}
// InitDeleteFlags is used to init all flags during queue deleting.
func InitDeleteFlags(cmd *cobra.Command) {
initFlags(cmd, &deleteQueueFlags.commonFlags)
util.InitFlags(cmd, &deleteQueueFlags.CommonFlags)
cmd.Flags().StringVarP(&deleteQueueFlags.Name, "name", "n", "", "the name of queue")
}
// DeleteQueue delete queue.
func DeleteQueue(ctx context.Context) error {
config, err := buildConfig(deleteQueueFlags.Master, deleteQueueFlags.Kubeconfig)
config, err := util.BuildConfig(deleteQueueFlags.Master, deleteQueueFlags.Kubeconfig)
if err != nil {
return err
}

View File

@ -29,10 +29,11 @@ import (
"volcano.sh/apis/pkg/apis/scheduling/v1beta1"
"volcano.sh/apis/pkg/client/clientset/versioned"
"volcano.sh/volcano/pkg/cli/podgroup"
"volcano.sh/volcano/pkg/cli/util"
)
type getFlags struct {
commonFlags
util.CommonFlags
Name string
}
@ -41,14 +42,14 @@ var getQueueFlags = &getFlags{}
// InitGetFlags is used to init all flags.
func InitGetFlags(cmd *cobra.Command) {
initFlags(cmd, &getQueueFlags.commonFlags)
util.InitFlags(cmd, &getQueueFlags.CommonFlags)
cmd.Flags().StringVarP(&getQueueFlags.Name, "name", "n", "", "the name of queue")
}
// GetQueue gets a queue.
func GetQueue(ctx context.Context) error {
config, err := buildConfig(getQueueFlags.Master, getQueueFlags.Kubeconfig)
config, err := util.BuildConfig(getQueueFlags.Master, getQueueFlags.Kubeconfig)
if err != nil {
return err
}

View File

@ -29,10 +29,11 @@ import (
"volcano.sh/apis/pkg/apis/scheduling/v1beta1"
"volcano.sh/apis/pkg/client/clientset/versioned"
"volcano.sh/volcano/pkg/cli/podgroup"
"volcano.sh/volcano/pkg/cli/util"
)
type listFlags struct {
commonFlags
util.CommonFlags
}
const (
@ -68,12 +69,12 @@ var listQueueFlags = &listFlags{}
// InitListFlags inits all flags.
func InitListFlags(cmd *cobra.Command) {
initFlags(cmd, &listQueueFlags.commonFlags)
util.InitFlags(cmd, &listQueueFlags.CommonFlags)
}
// ListQueue lists all the queue.
func ListQueue(ctx context.Context) error {
config, err := buildConfig(listQueueFlags.Master, listQueueFlags.Kubeconfig)
config, err := util.BuildConfig(listQueueFlags.Master, listQueueFlags.Kubeconfig)
if err != nil {
return err
}

View File

@ -27,6 +27,7 @@ import (
"volcano.sh/apis/pkg/apis/bus/v1alpha1"
"volcano.sh/apis/pkg/client/clientset/versioned"
"volcano.sh/volcano/pkg/cli/util"
)
const (
@ -39,7 +40,7 @@ const (
)
type operateFlags struct {
commonFlags
util.CommonFlags
// Name is name of queue
Name string
@ -53,7 +54,7 @@ var operateQueueFlags = &operateFlags{}
// InitOperateFlags is used to init all flags during queue operating
func InitOperateFlags(cmd *cobra.Command) {
initFlags(cmd, &operateQueueFlags.commonFlags)
util.InitFlags(cmd, &operateQueueFlags.CommonFlags)
cmd.Flags().StringVarP(&operateQueueFlags.Name, "name", "n", "", "the name of queue")
cmd.Flags().Int32VarP(&operateQueueFlags.Weight, "weight", "w", 0, "the weight of the queue")
@ -63,7 +64,7 @@ func InitOperateFlags(cmd *cobra.Command) {
// OperateQueue operates queue
func OperateQueue(ctx context.Context) error {
config, err := buildConfig(operateQueueFlags.Master, operateQueueFlags.Kubeconfig)
config, err := util.BuildConfig(operateQueueFlags.Master, operateQueueFlags.Kubeconfig)
if err != nil {
return err
}

View File

@ -31,6 +31,7 @@ import (
"volcano.sh/apis/pkg/apis/scheduling/v1beta1"
"volcano.sh/volcano/pkg/cli/podgroup"
"volcano.sh/volcano/pkg/cli/util"
)
func getTestQueueHTTPServer(t *testing.T) *httptest.Server {
@ -75,8 +76,8 @@ func getTestQueueListHTTPServer(t *testing.T) *httptest.Server {
return httptest.NewServer(handler)
}
func getCommonFlags(master string) commonFlags {
return commonFlags{
func getCommonFlags(master string) util.CommonFlags {
return util.CommonFlags{
Master: master,
}
}
@ -86,7 +87,7 @@ func TestCreateQueue(t *testing.T) {
server := getTestQueueHTTPServer(t)
defer server.Close()
createQueueFlags.commonFlags = getCommonFlags(server.URL)
createQueueFlags.CommonFlags = getCommonFlags(server.URL)
createQueueFlags.Name = "testQueue"
createQueueFlags.Weight = int32(2)
@ -112,7 +113,7 @@ func TestGetQueue(t *testing.T) {
server := getTestQueueHTTPServer(t)
defer server.Close()
getQueueFlags.commonFlags = getCommonFlags(server.URL)
getQueueFlags.CommonFlags = getCommonFlags(server.URL)
testCases := []struct {
Name string
@ -144,7 +145,7 @@ func TestGetQueue_empty(t *testing.T) {
server := getTestQueueHTTPServer(t)
defer server.Close()
listQueueFlags.commonFlags = getCommonFlags(server.URL)
listQueueFlags.CommonFlags = getCommonFlags(server.URL)
testCases := []struct {
Name string
@ -169,7 +170,7 @@ func TestGetQueue_nonempty(t *testing.T) {
server := getTestQueueListHTTPServer(t)
defer server.Close()
listQueueFlags.commonFlags = getCommonFlags(server.URL)
listQueueFlags.CommonFlags = getCommonFlags(server.URL)
testCases := []struct {
Name string

View File

@ -25,17 +25,12 @@ import (
// Initialize client auth plugin.
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
busv1alpha1 "volcano.sh/apis/pkg/apis/bus/v1alpha1"
"volcano.sh/apis/pkg/apis/helpers"
"volcano.sh/apis/pkg/client/clientset/versioned"
)
func buildConfig(master, kubeconfig string) (*rest.Config, error) {
return clientcmd.BuildConfigFromFlags(master, kubeconfig)
}
func createQueueCommand(ctx context.Context, config *rest.Config, action busv1alpha1.Action) error {
queueClient := versioned.NewForConfigOrDie(config)
queue, err := queueClient.SchedulingV1beta1().Queues().Get(ctx, operateQueueFlags.Name, metav1.GetOptions{})

View File

@ -24,7 +24,6 @@ import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"time"
@ -50,27 +49,21 @@ type CommonFlags struct {
// InitFlags initializes the common flags for most command lines.
func InitFlags(cmd *cobra.Command, cf *CommonFlags) {
cmd.Flags().StringVarP(&cf.Master, "master", "s", "", "the address of apiserver")
kubeConfFile := os.Getenv("KUBECONFIG")
if kubeConfFile == "" {
if home := HomeDir(); home != "" {
kubeConfFile = filepath.Join(home, ".kube", "config")
}
}
cmd.Flags().StringVarP(&cf.Kubeconfig, "kubeconfig", "k", kubeConfFile, "(optional) absolute path to the kubeconfig file")
}
// HomeDir gets the env $HOME.
func HomeDir() string {
if h := os.Getenv("HOME"); h != "" {
return h
}
return os.Getenv("USERPROFILE") // windows
cmd.Flags().StringVarP(&cf.Kubeconfig, "kubeconfig", "k", "", "(optional) absolute path to the kubeconfig file")
}
// BuildConfig builds the configuration file for command lines.
func BuildConfig(master, kubeconfig string) (*rest.Config, error) {
return clientcmd.BuildConfigFromFlags(master, kubeconfig)
// This will automatically load KUBECONFIG environment variable.
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
if kubeconfig != "" {
loadingRules.ExplicitPath = kubeconfig
}
overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}
if master != "" {
overrides.ClusterInfo.Server = master
}
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides).ClientConfig()
}
// PopulateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName2>=<value2> and returns ResourceList.

View File

@ -0,0 +1,21 @@
# This is an example configuration file of configMap for the Hypernode auto discovery.
networkTopologyDiscovery:
- source: ufm
enabled: true
interval: 10m
credentials:
secretRef:
name: ufm-credentials
namespace: volcano-system
config:
endpoint: https://ufm-server:8080
insecureSkipVerify: true
- source: roce
enabled: false
interval: 15m
config:
endpoint: https://roce-server:9090
token: token
- source: label
enabled: false
config:

View File

@ -0,0 +1,67 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api
import (
"fmt"
"sync"
clientset "k8s.io/client-go/kubernetes"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
)
// Discoverer is the interface for network topology discovery,
// every discoverer should implement this interface and return the discovered hyperNodes
type Discoverer interface {
// Start begins the discovery process, sending discovered nodes through the provided channel
Start() (chan []*topologyv1alpha1.HyperNode, error)
// Stop halts the discovery process
Stop() error
// Name returns the discoverer identifier, this is used for labeling discovered hyperNodes for distinction.
Name() string
}
// DiscovererConstructor is a function type used to create instances of specific discoverer source
type DiscovererConstructor func(cfg DiscoveryConfig, kubeClient clientset.Interface) Discoverer
var (
mutex sync.Mutex
discovererRegistry = make(map[string]DiscovererConstructor)
)
// RegisterDiscoverer registers a discoverer constructor for a given source
func RegisterDiscoverer(source string, constructor DiscovererConstructor) {
mutex.Lock()
defer mutex.Unlock()
discovererRegistry[source] = constructor
}
// NewDiscoverer creates a new discoverer instance based on source
func NewDiscoverer(cfg DiscoveryConfig, kubeClient clientset.Interface) (Discoverer, error) {
mutex.Lock()
defer mutex.Unlock()
constructor, exists := discovererRegistry[cfg.Source]
if !exists {
return nil, fmt.Errorf("unsupported discoverer type: %s", cfg.Source)
}
return constructor(cfg, kubeClient), nil
}

View File

@ -0,0 +1,90 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api
import (
"time"
)
const (
NetworkTopologySourceLabelKey = "volcano.sh/network-topology-source"
DefaultDiscoveryInterval = time.Hour
)
// NetworkTopologyConfig represents the configuration for network topology
type NetworkTopologyConfig struct {
// NetworkTopologyDiscovery specifies the network topology to discover,
// each discovery source has its own specific configuration
NetworkTopologyDiscovery []DiscoveryConfig `json:"networkTopologyDiscovery" yaml:"networkTopologyDiscovery"`
}
// SecretRef refers to a secret containing sensitive information
type SecretRef struct {
Name string `json:"name" yaml:"name"`
Namespace string `json:"namespace" yaml:"namespace"`
}
// Credentials specifies how to retrieve credentials
type Credentials struct {
SecretRef *SecretRef `json:"secretRef" yaml:"secretRef"`
}
// DiscoveryConfig contains configuration for a specific discovery source
type DiscoveryConfig struct {
// Source specifies the discover source (e.g., "ufm", "roce", etc.)
Source string `json:"source" yaml:"source"`
// Enabled determines if discovery for this source is active
Enabled bool `json:"enabled" yaml:"enabled"`
// Interval is the period between topology discovery operations
// If not specified, DefaultDiscoveryInterval will be used
Interval time.Duration `json:"interval" yaml:"interval"`
// Credentials specifies the username/password to access the discovery source
Credentials *Credentials `json:"credentials" yaml:"credentials"`
// Config contains specific configuration parameters for each discovery source
Config map[string]interface{} `json:"config" yaml:"config"`
}
// GetDiscoveryConfig returns the configuration for a specific discovery source
// Returns nil if the discovery source is not found or not enabled
func (c *NetworkTopologyConfig) GetDiscoveryConfig(source string) *DiscoveryConfig {
for i := range c.NetworkTopologyDiscovery {
if c.NetworkTopologyDiscovery[i].Source == source && c.NetworkTopologyDiscovery[i].Enabled {
// Create a copy to avoid modifying original data
config := c.NetworkTopologyDiscovery[i]
if config.Interval <= 0 {
config.Interval = DefaultDiscoveryInterval
}
return &config
}
}
return nil
}
// GetEnabledDiscoverySources returns a list of enabled discovery sources
func (c *NetworkTopologyConfig) GetEnabledDiscoverySources() []string {
sources := make([]string, 0, len(c.NetworkTopologyDiscovery))
for _, dc := range c.NetworkTopologyDiscovery {
if dc.Enabled {
sources = append(sources, dc.Source)
}
}
return sources
}

View File

@ -0,0 +1,31 @@
package config
import (
"sync"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
)
type FakeLoader struct {
mu sync.Mutex
Config *api.NetworkTopologyConfig
}
func (m *FakeLoader) LoadConfig() (*api.NetworkTopologyConfig, error) {
m.mu.Lock()
defer m.mu.Unlock()
return m.Config, nil
}
func (m *FakeLoader) SetConfig(cfg *api.NetworkTopologyConfig) {
m.mu.Lock()
defer m.mu.Unlock()
m.Config = cfg
}
func NewFakeLoader(cfg *api.NetworkTopologyConfig) *FakeLoader {
return &FakeLoader{
Config: cfg,
}
}

View File

@ -0,0 +1,78 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"fmt"
"gopkg.in/yaml.v2"
v1 "k8s.io/client-go/listers/core/v1"
"k8s.io/klog/v2"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
)
type Loader interface {
LoadConfig() (*api.NetworkTopologyConfig, error)
}
const (
NamespaceEnvKey = "KUBE_POD_NAMESPACE"
ReleaseNameEnvKey = "RELEASE_NAME"
DefaultReleaseName = "volcano"
DefaultNamespace = "volcano-system"
DefaultConfigKey = "volcano-controller.conf"
)
// loader handles loading discovery configuration from ConfigMap
type loader struct {
configMapLister v1.ConfigMapLister
configMapNamespace string
configMapName string
configKey string
}
// NewConfigLoader creates a new config loader
func NewConfigLoader(configMapLister v1.ConfigMapLister, namespace, name string) Loader {
return &loader{
configMapLister: configMapLister,
configMapNamespace: namespace,
configMapName: name,
configKey: DefaultConfigKey,
}
}
// LoadConfig loads the configuration from ConfigMap
func (l *loader) LoadConfig() (*api.NetworkTopologyConfig, error) {
klog.InfoS("Loading config from ConfigMap", "namespace", l.configMapNamespace, "name", l.configMapName)
cm, err := l.configMapLister.ConfigMaps(l.configMapNamespace).Get(l.configMapName)
if err != nil {
return nil, err
}
configYaml, ok := cm.Data[l.configKey]
if !ok {
return nil, fmt.Errorf("config key not found in ConfigMap: %s", l.configKey)
}
config := &api.NetworkTopologyConfig{}
if err = yaml.Unmarshal([]byte(configYaml), config); err != nil {
return nil, fmt.Errorf("failed to parse config: %v", err)
}
return config, nil
}

View File

@ -0,0 +1,136 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"errors"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
v1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
)
func TestLoadConfig(t *testing.T) {
tests := []struct {
name string
namespace string
configMapName string
configMaps *corev1.ConfigMap
expectedConfig *api.NetworkTopologyConfig
expectedErr error
}{
{
name: "successfully load config",
namespace: "test-ns",
configMapName: "test-cm",
configMaps: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cm",
Namespace: "test-ns",
},
Data: map[string]string{
"volcano-controller.conf": `
networkTopologyDiscovery:
- source: ufm
config:
url: "http://ufm-host:8080"
token: "test-token"
`,
},
},
expectedConfig: &api.NetworkTopologyConfig{
NetworkTopologyDiscovery: []api.DiscoveryConfig{
{
Source: "ufm",
Config: map[string]interface{}{
"url": "http://ufm-host:8080",
"token": "test-token",
},
},
},
},
},
{
name: "configmap not found",
namespace: "test-ns",
configMapName: "non-existent-cm",
configMaps: &corev1.ConfigMap{},
expectedErr: apierrors.NewNotFound(schema.GroupResource{Resource: "configmap"}, "non-existent-cm"),
},
{
name: "config key not found",
namespace: "test-ns",
configMapName: "test-cm",
configMaps: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cm",
Namespace: "test-ns",
},
Data: map[string]string{
"not-exists-key": "content",
},
},
expectedErr: errors.New(`config key not found in ConfigMap: volcano-controller.conf`),
},
{
name: "yaml parsing error",
namespace: "test-ns",
configMapName: "test-cm",
configMaps: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cm",
Namespace: "test-ns",
},
Data: map[string]string{
"volcano-controller.conf": "invalid yaml: :",
},
},
expectedErr: errors.New(`failed to parse config: yaml: mapping values are not allowed in this context`),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
indexer := cache.NewIndexer(
cache.MetaNamespaceKeyFunc,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
)
if err := indexer.Add(test.configMaps); err != nil {
t.Fatalf("failed to add object to indexer: %v", err)
}
configMapLister := v1.NewConfigMapLister(indexer)
loader := NewConfigLoader(configMapLister, test.namespace, test.configMapName)
config, err := loader.LoadConfig()
assert.Equal(t, test.expectedErr, err)
if !reflect.DeepEqual(config, test.expectedConfig) {
t.Errorf("config mismatch\nexpected: %+v\nactual: %+v", test.expectedConfig, config)
}
})
}
}

View File

@ -0,0 +1,115 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hypernode
import (
"fmt"
"os"
"time"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
coreinformers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
"volcano.sh/volcano/pkg/controllers/hypernode/config"
)
func (hn *hyperNodeController) setConfigMapNamespaceAndName() {
namespace := os.Getenv(config.NamespaceEnvKey)
if namespace == "" {
namespace = config.DefaultNamespace
}
releaseName := os.Getenv(config.ReleaseNameEnvKey)
if releaseName == "" {
releaseName = config.DefaultReleaseName
}
hn.configMapNamespace = namespace
hn.configMapName = releaseName + "-controller-configmap"
}
func (hn *hyperNodeController) setupConfigMapInformer() {
// Only list/watch one ConfigMap
filteredInformer := coreinformers.NewFilteredConfigMapInformer(
hn.kubeClient,
hn.configMapNamespace,
0,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
func(options *metav1.ListOptions) {
options.FieldSelector = fmt.Sprintf("metadata.name=%s", hn.configMapName)
},
)
hn.informerFactory.InformerFor(&v1.ConfigMap{}, func(kubernetes.Interface, time.Duration) cache.SharedIndexInformer {
return filteredInformer
})
hn.configMapInformer = hn.informerFactory.Core().V1().ConfigMaps()
// TODO: Only trigger handler when networkTopologyDiscovery config changed.
hn.configMapInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: hn.addConfigMap,
UpdateFunc: hn.updateConfigMap,
DeleteFunc: hn.deleteConfigMap,
})
}
func (hn *hyperNodeController) addConfigMap(obj interface{}) {
cm, ok := obj.(*v1.ConfigMap)
if !ok {
klog.ErrorS(nil, "Cannot convert to *v1.ConfigMap", "obj", obj)
return
}
klog.V(3).InfoS("Add ConfigMap", "namespace", cm.Namespace, "name", cm.Name)
hn.enqueueConfigMap(cm)
}
func (hn *hyperNodeController) updateConfigMap(oldObj, newObj interface{}) {
cm, ok := newObj.(*v1.ConfigMap)
if !ok {
klog.ErrorS(nil, "Cannot convert to *v1.ConfigMap", "obj", newObj)
return
}
klog.V(3).InfoS("Update ConfigMap", "namespace", cm.Namespace, "name", cm.Name)
hn.enqueueConfigMap(cm)
}
func (hn *hyperNodeController) deleteConfigMap(obj interface{}) {
cm, ok := obj.(*v1.ConfigMap)
if !ok {
tombstone, isTombstone := obj.(cache.DeletedFinalStateUnknown)
if !isTombstone {
klog.ErrorS(nil, "Cannot convert to *v1.ConfigMap", "obj", obj)
return
}
cm, ok = tombstone.Obj.(*v1.ConfigMap)
if !ok {
klog.ErrorS(nil, "Cannot convert tombstone to *v1.ConfigMap", "obj", tombstone.Obj)
return
}
}
klog.V(3).InfoS("Delete ConfigMap", "namespace", cm.Namespace, "name", cm.Name)
hn.enqueueConfigMap(cm)
}
func (hn *hyperNodeController) enqueueConfigMap(cm *v1.ConfigMap) {
key, err := cache.MetaNamespaceKeyFunc(cm)
if err != nil {
klog.ErrorS(err, "Failed to get key for ConfigMap", "namespace", cm.Namespace, "name", cm.Name)
return
}
hn.configMapQueue.Add(key)
}

View File

@ -0,0 +1,269 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hypernode
import (
"context"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
fakevcclientset "volcano.sh/apis/pkg/client/clientset/versioned/fake"
vcinformer "volcano.sh/apis/pkg/client/informers/externalversions"
"volcano.sh/volcano/pkg/controllers/hypernode/config"
)
// newFakeConfigMapController creates a test controller with fake clients
func newFakeConfigMapController() *hyperNodeController {
vcClient := fakevcclientset.NewSimpleClientset()
kubeClient := fake.NewSimpleClientset()
vcInformerFactory := vcinformer.NewSharedInformerFactory(vcClient, 0)
informerFactory := informers.NewSharedInformerFactory(kubeClient, 0)
controller := &hyperNodeController{
vcClient: vcClient,
kubeClient: kubeClient,
vcInformerFactory: vcInformerFactory,
informerFactory: informerFactory,
configMapQueue: workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[string]()),
}
return controller
}
func TestSetConfigMapNamespaceAndName(t *testing.T) {
testCases := []struct {
name string
setupEnv func()
expectedNamespace string
expectedName string
}{
{
name: "With environment variables set",
setupEnv: func() {
os.Setenv(config.NamespaceEnvKey, "test-namespace")
os.Setenv(config.ReleaseNameEnvKey, "test-release")
},
expectedNamespace: "test-namespace",
expectedName: "test-release-controller-configmap",
},
{
name: "With empty environment variables",
setupEnv: func() {
os.Unsetenv(config.NamespaceEnvKey)
os.Unsetenv(config.ReleaseNameEnvKey)
},
expectedNamespace: config.DefaultNamespace,
expectedName: config.DefaultReleaseName + "-controller-configmap",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.setupEnv()
controller := newFakeConfigMapController()
controller.setConfigMapNamespaceAndName()
assert.Equal(t, tc.expectedNamespace, controller.configMapNamespace)
assert.Equal(t, tc.expectedName, controller.configMapName)
})
}
}
func TestSetupConfigMapInformer(t *testing.T) {
controller := newFakeConfigMapController()
controller.configMapNamespace = "test-namespace"
controller.configMapName = "test-configmap"
controller.setupConfigMapInformer()
assert.NotNil(t, controller.configMapInformer)
controller.informerFactory.Start(nil)
synced := controller.informerFactory.WaitForCacheSync(nil)
for informerType, ok := range synced {
assert.True(t, ok, "Failed to sync informer: %v", informerType)
}
}
func TestAddConfigMap(t *testing.T) {
controller := newFakeConfigMapController()
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Namespace: "test-namespace",
},
}
controller.addConfigMap(configMap)
assert.Equal(t, 1, controller.configMapQueue.Len())
// Test invalid object (non-ConfigMap)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
}
controller.addConfigMap(pod)
assert.Equal(t, 1, controller.configMapQueue.Len())
}
func TestUpdateConfigMap(t *testing.T) {
controller := newFakeConfigMapController()
oldConfigMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Namespace: "test-namespace",
},
Data: map[string]string{
"key1": "value1",
},
}
newConfigMap := oldConfigMap.DeepCopy()
newConfigMap.Data["key2"] = "value2"
controller.updateConfigMap(oldConfigMap, newConfigMap)
// Verify the ConfigMap was added to the queue
assert.Equal(t, 1, controller.configMapQueue.Len())
// Test invalid object (non-ConfigMap)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
}
controller.updateConfigMap(oldConfigMap, pod)
assert.Equal(t, 1, controller.configMapQueue.Len())
}
func TestDeleteConfigMap(t *testing.T) {
controller := newFakeConfigMapController()
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Namespace: "test-namespace",
},
}
controller.deleteConfigMap(configMap)
assert.Equal(t, 1, controller.configMapQueue.Len())
// Test with DeletedFinalStateUnknown
tombstone := cache.DeletedFinalStateUnknown{
Key: "test-namespace/test-configmap-2",
Obj: &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap-2",
Namespace: "test-namespace",
},
},
}
controller.deleteConfigMap(tombstone)
assert.Equal(t, 2, controller.configMapQueue.Len())
// Test invalid object (non-ConfigMap)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
}
controller.deleteConfigMap(pod)
assert.Equal(t, 2, controller.configMapQueue.Len())
// Test with DeletedFinalStateUnknown and invalid object
invalidTombstone := cache.DeletedFinalStateUnknown{
Key: "test-namespace/test-pod",
Obj: pod,
}
controller.deleteConfigMap(invalidTombstone)
assert.Equal(t, 2, controller.configMapQueue.Len())
}
func TestEnqueueConfigMap(t *testing.T) {
controller := newFakeConfigMapController()
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Namespace: "test-namespace",
},
}
controller.enqueueConfigMap(configMap)
assert.Equal(t, 1, controller.configMapQueue.Len())
key, shutdown := controller.configMapQueue.Get()
assert.False(t, shutdown)
assert.Equal(t, "test-namespace/test-configmap", key)
}
func TestIntegrationConfigMapHandling(t *testing.T) {
controller := newFakeConfigMapController()
controller.configMapNamespace = "test-namespace"
controller.configMapName = "test-configmap"
controller.setupConfigMapInformer()
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Namespace: "test-namespace",
},
Data: map[string]string{
"config": "initial",
},
}
_, err := controller.kubeClient.CoreV1().ConfigMaps("test-namespace").Create(
context.Background(), configMap, metav1.CreateOptions{})
assert.NoError(t, err)
controller.informerFactory.Start(nil)
synced := controller.informerFactory.WaitForCacheSync(nil)
for informerType, ok := range synced {
assert.True(t, ok, "Failed to sync informer: %v", informerType)
}
assert.Equal(t, 1, controller.configMapQueue.Len())
// Process the queue
key, _ := controller.configMapQueue.Get()
controller.configMapQueue.Done(key)
updatedConfigMap := configMap.DeepCopy()
updatedConfigMap.Data["config"] = "updated"
_, err = controller.kubeClient.CoreV1().ConfigMaps("test-namespace").Update(
context.Background(), updatedConfigMap, metav1.UpdateOptions{})
assert.NoError(t, err)
time.Sleep(1 * time.Second)
assert.Equal(t, 1, controller.configMapQueue.Len())
}

View File

@ -0,0 +1,51 @@
package fake
import (
"sync"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
)
type fakeDiscoverer struct {
startCalled bool
stopCalled bool
hyperNodes []*topologyv1alpha1.HyperNode
stopCh chan struct{}
wg sync.WaitGroup
}
func (f *fakeDiscoverer) Name() string {
return "FakeDiscoverer"
}
func NewFakeDiscoverer(hyperNodes []*topologyv1alpha1.HyperNode, cfg api.DiscoveryConfig) api.Discoverer {
return &fakeDiscoverer{
hyperNodes: hyperNodes,
stopCh: make(chan struct{}),
}
}
func (f *fakeDiscoverer) Start() (chan []*topologyv1alpha1.HyperNode, error) {
f.startCalled = true
f.wg.Add(1)
ch := make(chan []*topologyv1alpha1.HyperNode)
go func() {
defer f.wg.Done()
select {
case ch <- f.hyperNodes:
case <-f.stopCh:
return
}
}()
return ch, nil
}
func (f *fakeDiscoverer) Stop() error {
f.stopCalled = true
close(f.stopCh)
f.wg.Wait()
return nil
}

View File

@ -0,0 +1,52 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package label
import (
clientset "k8s.io/client-go/kubernetes"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
)
func init() {
api.RegisterDiscoverer("label", NewLabelDiscoverer)
}
type labelDiscoverer struct {
config api.DiscoveryConfig
}
func (l labelDiscoverer) Start() (chan []*topologyv1alpha1.HyperNode, error) {
//TODO implement me
panic("implement me")
}
func (l labelDiscoverer) Stop() error {
//TODO implement me
panic("implement me")
}
func (l labelDiscoverer) Name() string {
return "label"
}
func NewLabelDiscoverer(cfg api.DiscoveryConfig, kubeClient clientset.Interface) api.Discoverer {
return &labelDiscoverer{
config: cfg,
}
}

View File

@ -0,0 +1,284 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package discovery
import (
"fmt"
"sync"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog/v2"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
"volcano.sh/volcano/pkg/controllers/hypernode/config"
_ "volcano.sh/volcano/pkg/controllers/hypernode/discovery/ufm"
)
type Result struct {
// HyperNodes contains the discovered hypernodes
HyperNodes []*topologyv1alpha1.HyperNode
// Source indicates the source of the discovery
Source string
}
// Manager is the interface for managing network topology discovery
type Manager interface {
// Start initializes and starts the topology discovery manager
Start() error
// Stop halts all discovery processes
Stop()
// ResultChannel returns a channel for receiving discovery results
ResultChannel() <-chan Result
}
// manager manages network topology discovery processes
type manager struct {
mutex sync.Mutex
configLoader config.Loader
config *api.NetworkTopologyConfig
discoverers map[string]api.Discoverer
workQueue workqueue.TypedRateLimitingInterface[string]
stopCh chan struct{}
kubeClient clientset.Interface
resultCh chan Result
}
// NewManager create a new network topology discovery manager
func NewManager(configLoader config.Loader, queue workqueue.TypedRateLimitingInterface[string], kubeClient clientset.Interface) Manager {
return &manager{
configLoader: configLoader,
discoverers: make(map[string]api.Discoverer),
resultCh: make(chan Result),
stopCh: make(chan struct{}),
workQueue: queue,
kubeClient: kubeClient,
}
}
// Start initializes and starts the topology discovery manager
func (m *manager) Start() error {
var err error
cfg, err := m.configLoader.LoadConfig()
if err != nil {
klog.ErrorS(err, "Failed to load config")
// Initialize with an empty config to avoid nil pointer dereference.
m.config = &api.NetworkTopologyConfig{}
// Do not return an error here, in case of configMap is updated correctly later.
} else {
m.config = cfg
}
go m.worker()
klog.InfoS("Network topology discovery manager started")
return nil
}
// Stop halts all discovery processes
func (m *manager) Stop() {
close(m.stopCh)
m.stopAllDiscoverers()
klog.InfoS("Network topology discovery manager stopped")
}
func (m *manager) ResultChannel() <-chan Result {
return m.resultCh
}
// startSingleDiscoverer start a single network topology discoverer.
func (m *manager) startSingleDiscoverer(source string) error {
cfg, err := m.configLoader.LoadConfig()
if err != nil {
return fmt.Errorf("failed to load config: %v", err)
}
discoveryCfg := cfg.GetDiscoveryConfig(source)
if discoveryCfg == nil {
return fmt.Errorf("configuration not found for network topology discovery source: %s", source)
}
discoverer, err := api.NewDiscoverer(*discoveryCfg, m.kubeClient)
if err != nil {
return fmt.Errorf("failed to create discoverer: %v", err)
}
m.discoverers[source] = discoverer
outputCh, err := discoverer.Start()
if err != nil {
return fmt.Errorf("failed to start discoverer: %v", err)
}
go m.processTopology(source, outputCh)
klog.InfoS("Started network topology discoverer", "source", source)
return nil
}
func (m *manager) stopAllDiscoverers() {
m.mutex.Lock()
defer m.mutex.Unlock()
for source := range m.discoverers {
if err := m.stopSingleDiscoverer(source); err != nil {
klog.ErrorS(err, "Failed to stop discoverer", "source", source)
}
}
m.discoverers = make(map[string]api.Discoverer)
}
func (m *manager) stopSingleDiscoverer(source string) error {
discoverer, exists := m.discoverers[source]
if !exists {
klog.InfoS("No need to stop discoverer as it may not start yet", "source", source)
return nil
}
if err := discoverer.Stop(); err != nil {
return err
}
delete(m.discoverers, source)
return nil
}
func (m *manager) worker() {
for m.processNext() {
}
}
// processNext handles a single workQueue item
func (m *manager) processNext() bool {
key, shutdown := m.workQueue.Get()
if shutdown {
return false
}
defer m.workQueue.Done(key)
if err := m.syncHandler(key); err != nil {
m.workQueue.AddRateLimited(key)
klog.ErrorS(err, "Failed to process network topology discoverer", "key", key)
return true
}
m.workQueue.Forget(key)
return true
}
// parseConfig loads and parses the configuration from ConfigMap
func (m *manager) parseConfig(key string) (*api.NetworkTopologyConfig, error) {
_, _, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
return nil, err
}
newConfig, err := m.configLoader.LoadConfig()
if err != nil {
if !errors.IsNotFound(err) {
return nil, err
}
// set an empty config and should not return err because we should handle configMap deletion event.
newConfig = &api.NetworkTopologyConfig{}
}
return newConfig, nil
}
// syncHandler handles the configuration update event.
func (m *manager) syncHandler(key string) error {
klog.InfoS("Received configuration update")
newConfig, err := m.parseConfig(key)
if err != nil {
return err
}
m.mutex.Lock()
defer m.mutex.Unlock()
err = m.handleRemovedSources(newConfig)
if err != nil {
return err
}
// TODO: Only restart changed discoverers.
for _, source := range newConfig.GetEnabledDiscoverySources() {
klog.InfoS("Restarting network discovery", "source", source)
if err = m.stopSingleDiscoverer(source); err != nil {
return err
}
if err = m.startSingleDiscoverer(source); err != nil {
return err
}
}
// update the config for next compare.
m.config = newConfig
return nil
}
// handleRemovedSources stops discoverers sources that are no longer enabled
func (m *manager) handleRemovedSources(config *api.NetworkTopologyConfig) error {
oldConfig := m.config
oldSources := sets.Set[string]{}
for _, source := range oldConfig.GetEnabledDiscoverySources() {
oldSources.Insert(source)
}
newSources := sets.Set[string]{}
for _, source := range config.GetEnabledDiscoverySources() {
newSources.Insert(source)
}
for source := range oldSources.Difference(newSources) {
klog.InfoS("Stopping network discovery", "source", source)
if err := m.stopSingleDiscoverer(source); err != nil {
return err
}
}
return nil
}
// processTopology processes the topology data received from the discoverer
func (m *manager) processTopology(source string, topologyCh <-chan []*topologyv1alpha1.HyperNode) {
for {
select {
case hyperNodes, ok := <-topologyCh:
if !ok {
klog.InfoS("Topology channel closed, stopping processor", "source", source)
return
}
m.resultCh <- Result{
HyperNodes: hyperNodes,
Source: source,
}
klog.V(3).InfoS("Forwarded discovery results to unified channel",
"source", source,
"nodeCount", len(hyperNodes))
case <-m.stopCh:
return
}
}
}

View File

@ -0,0 +1,169 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package discovery
import (
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"testing"
"time"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/workqueue"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
"volcano.sh/volcano/pkg/controllers/hypernode/config"
fakedisc "volcano.sh/volcano/pkg/controllers/hypernode/discovery/fake"
)
func TestManager_StartMultipleDiscoverers(t *testing.T) {
// Prepare test data
hyperNodesA := []*topologyv1alpha1.HyperNode{
{ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "ha1"}},
{ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "ha2"}},
}
hyperNodesB := []*topologyv1alpha1.HyperNode{
{ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "hb"}},
}
constructorA := api.DiscovererConstructor(func(cfg api.DiscoveryConfig, kubeClient clientset.Interface) api.Discoverer {
return fakedisc.NewFakeDiscoverer(hyperNodesA, cfg)
})
constructorB := api.DiscovererConstructor(func(cfg api.DiscoveryConfig, kubeClient clientset.Interface) api.Discoverer {
return fakedisc.NewFakeDiscoverer(hyperNodesB, cfg)
})
api.RegisterDiscoverer("sourceA", constructorA)
api.RegisterDiscoverer("sourceB", constructorB)
discoveryConfig := &api.NetworkTopologyConfig{
NetworkTopologyDiscovery: []api.DiscoveryConfig{
{
Source: "sourceA",
Enabled: true,
},
{
Source: "sourceB",
Enabled: true,
},
},
}
loader := config.NewFakeLoader(discoveryConfig)
// Create manager
queue := workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[string]())
queue.Add("test-namespace/test-config")
fakeClient := fake.NewSimpleClientset()
m := NewManager(loader, queue, fakeClient)
err := m.Start()
assert.NoError(t, err)
timeout := time.After(time.Second)
for i := 0; i < 2; i++ {
select {
case result := <-m.ResultChannel():
if result.Source == "sourceA" {
assert.Equal(t, 2, len(result.HyperNodes))
assert.Equal(t, "ha1", result.HyperNodes[0].Name)
assert.Equal(t, "ha2", result.HyperNodes[1].Name)
} else if result.Source == "sourceB" {
assert.Equal(t, 1, len(result.HyperNodes))
assert.Equal(t, "hb", result.HyperNodes[0].Name)
}
case <-timeout:
t.Fatal("Test timed out waiting for results")
}
}
mgr := m.(*manager)
mgr.mutex.Lock()
assert.Equal(t, discoveryConfig, mgr.config)
mgr.mutex.Unlock()
// Stop manager
m.Stop()
}
func TestManager_syncHandler(t *testing.T) {
// Prepare test data
hyperNodes := []*topologyv1alpha1.HyperNode{
{ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "ha1"}},
}
constructor := api.DiscovererConstructor(func(cfg api.DiscoveryConfig, kubeClient clientset.Interface) api.Discoverer {
return fakedisc.NewFakeDiscoverer(hyperNodes, cfg)
})
api.RegisterDiscoverer("testSource", constructor)
discoveryConfigV1 := &api.NetworkTopologyConfig{
NetworkTopologyDiscovery: []api.DiscoveryConfig{
{
Source: "testSource",
Enabled: true,
Config: map[string]interface{}{
"key": "value",
},
},
},
}
discoveryConfigV2 := &api.NetworkTopologyConfig{
NetworkTopologyDiscovery: []api.DiscoveryConfig{
{
Source: "testSource",
Enabled: false,
Config: map[string]interface{}{
"key": "value",
},
},
},
}
loader := config.NewFakeLoader(discoveryConfigV1)
queue := workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[string]())
fakeClient := fake.NewSimpleClientset()
m := NewManager(loader, queue, fakeClient)
// Start the manager
err := m.Start()
assert.NoError(t, err)
// Enqueue a dummy key to trigger the sync handler
queue.Add("test-namespace/test-config")
// Give the manager some time to process the initial config
time.Sleep(100 * time.Millisecond)
//// Update the config with V2 version that disables the discoverer
loader.SetConfig(discoveryConfigV2)
// Enqueue the key again to trigger the sync handler with the updated config
queue.Add("test-namespace/test-config")
// Give the manager some time to process the updated config
time.Sleep(100 * time.Millisecond)
// Assert that the discoverer has been stopped
mgr := m.(*manager)
mgr.mutex.Lock()
_, exists := mgr.discoverers["testSource"]
mgr.mutex.Unlock()
assert.False(t, exists, "Discoverer should be stopped")
// Stop the manager
m.Stop()
}

View File

@ -0,0 +1,418 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ufm
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"net/http"
"sort"
"strings"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
"volcano.sh/volcano/pkg/controllers/hypernode/utils"
)
func init() {
api.RegisterDiscoverer("ufm", NewUFMDiscoverer)
}
const (
// maxBodySize defines the maximum size of response body (100MB)
maxBodySize = 100 << 20
// ufmPortsPath defines the API path for UFM ports resources
ufmPortsPath = "/ufmRest/resources/ports"
)
// UFMInterface represents a single interface between nodes in the UFM topology
type UFMInterface struct {
Description string `json:"description"`
Tier int `json:"tier"`
SystemName string `json:"system_name"`
NodeDescription string `json:"node_description"`
PeerNodeName string `json:"peer_node_name"`
}
// LeafSwitch represents a single leaf switch in the network topology
type LeafSwitch struct {
Name string
Tier int
NodeNames sets.Set[string]
}
// LeafSwitchesGroup represents a group of connected leaf switches
type LeafSwitchesGroup struct {
Leafs map[string]LeafSwitch
NodeNames sets.Set[string]
}
// ufmDiscoverer implements the Discoverer interface for UFM
type ufmDiscoverer struct {
endpoint string
username string
password string
kubeClient clientset.Interface
discoveryInterval time.Duration
client *http.Client
stopCh chan struct{}
}
// NewUFMDiscoverer creates a new UFM topology discoverer
func NewUFMDiscoverer(cfg api.DiscoveryConfig, kubeClient clientset.Interface) api.Discoverer {
endpoint := cfg.Config["endpoint"].(string)
insecureSkipVerify, _ := cfg.Config["insecureSkipVerify"].(bool)
if insecureSkipVerify {
klog.Warningln("WARNING: TLS certificate verification is disabled which is insecure. This should not be used in production environments")
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify},
}
client := &http.Client{Transport: tr, Timeout: 30 * time.Second}
u := &ufmDiscoverer{
endpoint: endpoint,
discoveryInterval: cfg.Interval,
client: client,
kubeClient: kubeClient,
stopCh: make(chan struct{}),
}
var username, password string
if cfg.Credentials != nil && cfg.Credentials.SecretRef != nil {
var err error
username, password, err = u.getCredentialsFromSecret(cfg.Credentials.SecretRef.Name, cfg.Credentials.SecretRef.Namespace)
if err != nil {
klog.ErrorS(err, "Failed to get credentials from secret", "secretName", cfg.Credentials.SecretRef.Name)
}
} else {
klog.InfoS("UFM authentication credentials are not configured")
}
u.username = username
u.password = password
klog.InfoS("UFM discoverer initialized")
return u
}
// Start begins the topology discovery process and returns the channel for receiving discovered topology
func (u *ufmDiscoverer) Start() (chan []*topologyv1alpha1.HyperNode, error) {
if u.endpoint == "" {
return nil, errors.New("UFM endpoint is not configured")
}
if u.username == "" || u.password == "" {
return nil, errors.New("UFM authentication credentials are not configured")
}
klog.InfoS("Starting UFM network topology discovery",
"endpoint", u.endpoint,
"interval", u.discoveryInterval)
// Create the output channel that this discoverer will manage
outputCh := make(chan []*topologyv1alpha1.HyperNode, 10)
// Start periodic discovery in a separate goroutine
go u.periodicDiscovery(outputCh)
return outputCh, nil
}
// Stop halts the discovery process
func (u *ufmDiscoverer) Stop() error {
close(u.stopCh)
return nil
}
// Name returns the discoverer name
func (u *ufmDiscoverer) Name() string {
return "ufm"
}
// getCredentialsFromSecret retrieves username and password from a Kubernetes Secret
func (u *ufmDiscoverer) getCredentialsFromSecret(name, namespace string) (string, string, error) {
secret, err := u.kubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return "", "", fmt.Errorf("failed to get secret %s/%s: %v", namespace, name, err)
}
usernameData, ok := secret.Data["username"]
if !ok {
return "", "", fmt.Errorf("username not found in secret %s/%s", namespace, name)
}
username := string(usernameData)
passwordData, ok := secret.Data["password"]
if !ok {
return "", "", fmt.Errorf("password not found in secret %s/%s", namespace, name)
}
password := string(passwordData)
return username, password, nil
}
// periodicDiscovery periodically discovers network topology
func (u *ufmDiscoverer) periodicDiscovery(outputCh chan []*topologyv1alpha1.HyperNode) {
// Perform immediate discovery first
u.discoverAndSend(outputCh)
// Set up ticker for periodic discovery
ticker := time.NewTicker(u.discoveryInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
u.discoverAndSend(outputCh)
case <-u.stopCh:
klog.InfoS("UFM network topology discovery stopped, closing output channel")
close(outputCh)
return
}
}
}
// discoverAndSend discovers the topology and sends it through the channel
func (u *ufmDiscoverer) discoverAndSend(outputCh chan []*topologyv1alpha1.HyperNode) {
// Fetch UFM data
ufmData, err := u.fetchUFMData()
if err != nil {
klog.ErrorS(err, "Failed to fetch UFM data")
return
}
// Process UFM data into HyperNodes
hyperNodes := u.buildHyperNodes(ufmData)
// Send discovered nodes through the channel
select {
case outputCh <- hyperNodes:
klog.InfoS("Sent network topology data", "hyperNodeCount", len(hyperNodes))
case <-u.stopCh:
// Discovery stopped, don't attempt to send
return
default:
klog.InfoS("Failed to send network topology data, channel might be full")
}
}
// fetchUFMData retrieves network topology data from the UFM API
func (u *ufmDiscoverer) fetchUFMData() ([]UFMInterface, error) {
var requestURL string
if strings.HasPrefix(u.endpoint, "http://") || strings.HasPrefix(u.endpoint, "https://") {
requestURL = strings.TrimRight(u.endpoint, "/") + ufmPortsPath
} else {
requestURL = "https://" + strings.TrimRight(u.endpoint, "/") + ufmPortsPath
}
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %v", err)
}
req.SetBasicAuth(u.username, u.password)
klog.InfoS("Sending request to UFM server", "url", requestURL)
resp, err := u.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %s", resp.Status)
}
resp.Body = http.MaxBytesReader(nil, resp.Body, maxBodySize)
// Parse response
var interfaces []UFMInterface
if err = json.NewDecoder(resp.Body).Decode(&interfaces); err != nil {
return nil, fmt.Errorf("failed to decode response: %v", err)
}
klog.InfoS("Successfully retrieved UFM data", "interfaceCount", len(interfaces))
return interfaces, nil
}
// buildHyperNodes converts UFM data to HyperNode resources
func (u *ufmDiscoverer) buildHyperNodes(ufmData []UFMInterface) []*topologyv1alpha1.HyperNode {
// Process UFM data into leaf switch groups
leafSwitchGroups := u.processLeafSwitchGroups(ufmData)
hyperNodes := make([]*topologyv1alpha1.HyperNode, 0, len(leafSwitchGroups)+1)
leafHyperNodeNames := make([]string, 0, len(leafSwitchGroups))
// Create leaf hypernodes
for groupID, group := range leafSwitchGroups {
// Create hypernode name for this leaf group
hnName := fmt.Sprintf("leaf-hn-%d", groupID)
nodeList := group.NodeNames.UnsortedList()
members := utils.BuildMembers(nodeList, topologyv1alpha1.MemberTypeNode)
// Create the HyperNode object
leafHyperNode := utils.BuildHyperNode(hnName, 1, members, map[string]string{
api.NetworkTopologySourceLabelKey: u.Name(),
})
hyperNodes = append(hyperNodes, leafHyperNode)
// Add to the list for the spine hypernode
leafHyperNodeNames = append(leafHyperNodeNames, hnName)
klog.InfoS("Created leaf HyperNode", "name", hnName, "nodeCount", len(nodeList))
}
// Create spine hypernode that includes all leaf hypernodes
if len(leafHyperNodeNames) > 0 {
spineHnName := "spine-hn"
members := utils.BuildMembers(leafHyperNodeNames, topologyv1alpha1.MemberTypeHyperNode)
spineHyperNode := utils.BuildHyperNode(spineHnName, 2, members, map[string]string{
api.NetworkTopologySourceLabelKey: u.Name(),
})
hyperNodes = append(hyperNodes, spineHyperNode)
klog.InfoS("Created spine HyperNode", "name", spineHnName, "leafCount", len(leafHyperNodeNames))
}
return hyperNodes
}
// processLeafSwitchGroups processes UFM data into leaf switch groups
func (u *ufmDiscoverer) processLeafSwitchGroups(ufmData []UFMInterface) []LeafSwitchesGroup {
leafSwitches := u.getLeafSwitches(ufmData)
return u.classifyLeafs(leafSwitches)
}
// getLeafSwitches extracts leaf switches from UFM data
func (u *ufmDiscoverer) getLeafSwitches(ufmData []UFMInterface) []LeafSwitch {
leafMap := make(map[string]*LeafSwitch)
for _, data := range ufmData {
// Only need to parse computer ufm data because it containers both leaf and computer connection information.
if !strings.Contains(data.Description, "Computer") {
continue
}
leafName := data.PeerNodeName
nodeName := data.SystemName
if _, exists := leafMap[leafName]; !exists {
leafMap[leafName] = &LeafSwitch{
Name: leafName,
Tier: data.Tier,
NodeNames: sets.New[string](nodeName),
}
} else {
leafMap[leafName].NodeNames.Insert(nodeName)
}
}
result := make([]LeafSwitch, 0, len(leafMap))
for _, leafSwitch := range leafMap {
result = append(result, *leafSwitch)
}
sort.Slice(result, func(i, j int) bool {
return result[i].Name < result[j].Name
})
return result
}
// classifyLeafs classifies leaf switches into connected groups
func (u *ufmDiscoverer) classifyLeafs(leafSwitches []LeafSwitch) []LeafSwitchesGroup {
leafMap := make(map[string]LeafSwitch)
for _, leaf := range leafSwitches {
leafMap[leaf.Name] = leaf
}
leafToNodes := u.buildLeafToNodesMap(leafSwitches)
nodesToLeafs := u.buildNodeToLeafsMap(leafToNodes)
leafGroups := make([]LeafSwitchesGroup, 0)
processedLeafs := sets.New[string]()
for _, leafSwitch := range leafSwitches {
if processedLeafs.Has(leafSwitch.Name) {
continue
}
// Use BFS to Find All Connected Leaf Nodes
queue := []string{leafSwitch.Name}
leafGroup := LeafSwitchesGroup{
Leafs: make(map[string]LeafSwitch),
NodeNames: sets.New[string](),
}
for len(queue) > 0 {
currentLeaf := queue[0]
queue = queue[1:]
if processedLeafs.Has(currentLeaf) {
continue
}
leafGroup.Leafs[currentLeaf] = leafMap[currentLeaf]
leafGroup.NodeNames.Insert(leafMap[currentLeaf].NodeNames.UnsortedList()...)
processedLeafs.Insert(currentLeaf)
// Find all other leafSwitch nodes that have common nodes with the current leafSwitch node
for node := range leafToNodes[currentLeaf] {
for relatedLeaf := range nodesToLeafs[node] {
if !processedLeafs.Has(relatedLeaf) {
queue = append(queue, relatedLeaf)
}
}
}
}
leafGroups = append(leafGroups, leafGroup)
}
return leafGroups
}
// buildLeafToNodesMap creates a mapping from leaf switch names to their connected nodes
func (u *ufmDiscoverer) buildLeafToNodesMap(leafSwitches []LeafSwitch) map[string]sets.Set[string] {
leafToNodes := make(map[string]sets.Set[string])
for _, leaf := range leafSwitches {
leafToNodes[leaf.Name] = leaf.NodeNames
}
return leafToNodes
}
// buildNodeToLeafsMap creates a mapping from node names to their connected leaf switches
func (u *ufmDiscoverer) buildNodeToLeafsMap(leafToNodes map[string]sets.Set[string]) map[string]sets.Set[string] {
nodesToLeafs := make(map[string]sets.Set[string])
for leaf, nodes := range leafToNodes {
for node := range nodes {
if _, exist := nodesToLeafs[node]; !exist {
nodesToLeafs[node] = sets.New[string]()
}
nodesToLeafs[node].Insert(leaf)
}
}
return nodesToLeafs
}

View File

@ -0,0 +1,355 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ufm
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
)
func TestUfmDiscoverer_Start(t *testing.T) {
tests := []struct {
name string
config api.DiscoveryConfig
mockResponseCode int
secretExists bool
secretData map[string][]byte
expectedError bool
expectHyperNodes bool
expectedHyperNodes map[string]*topologyv1alpha1.HyperNode
}{
{
name: "MissingEndpoint",
config: api.DiscoveryConfig{
Source: "ufm",
Config: map[string]interface{}{
"endpoint": "",
},
Credentials: &api.Credentials{
SecretRef: &api.SecretRef{
Name: "ufm-creds",
Namespace: "default",
},
},
},
secretExists: true,
secretData: map[string][]byte{"username": []byte("user"), "password": []byte("pass")},
expectedError: true,
},
{
name: "MissingSecretRef",
config: api.DiscoveryConfig{
Source: "ufm",
Config: map[string]interface{}{
"endpoint": "https://ufm.example.com",
},
},
expectedError: true,
},
{
name: "SecretNotFound",
config: api.DiscoveryConfig{
Source: "ufm",
Config: map[string]interface{}{
"endpoint": "https://ufm.example.com",
},
Credentials: &api.Credentials{
SecretRef: &api.SecretRef{
Name: "nonexistent",
Namespace: "default",
},
},
},
secretExists: false,
expectedError: true,
},
{
name: "MissingUsername",
config: api.DiscoveryConfig{
Source: "ufm",
Config: map[string]interface{}{
"endpoint": "https://ufm.example.com",
},
Credentials: &api.Credentials{
SecretRef: &api.SecretRef{
Name: "ufm-creds",
Namespace: "default",
},
},
},
secretExists: true,
secretData: map[string][]byte{"password": []byte("pass")},
expectedError: true,
},
{
name: "MissingPassword",
config: api.DiscoveryConfig{
Source: "ufm",
Config: map[string]interface{}{
"endpoint": "https://ufm.example.com",
},
Credentials: &api.Credentials{
SecretRef: &api.SecretRef{
Name: "ufm-creds",
Namespace: "default",
},
},
},
secretExists: true,
secretData: map[string][]byte{"username": []byte("user")},
expectedError: true,
},
{
name: "Success",
config: api.DiscoveryConfig{
Source: "ufm",
Config: map[string]interface{}{
"endpoint": "https://ufm.example.com",
},
Credentials: &api.Credentials{
SecretRef: &api.SecretRef{
Name: "ufm-creds",
Namespace: "default",
},
},
Interval: time.Minute,
},
secretExists: true,
secretData: map[string][]byte{"username": []byte("user"), "password": []byte("pass")},
mockResponseCode: http.StatusOK,
expectHyperNodes: true,
expectedHyperNodes: expectedHyperNodes(),
},
{
name: "ServerError",
config: api.DiscoveryConfig{
Source: "ufm",
Config: map[string]interface{}{
"endpoint": "https://ufm.example.com",
},
Credentials: &api.Credentials{
SecretRef: &api.SecretRef{
Name: "ufm-creds",
Namespace: "default",
},
},
Interval: time.Minute,
},
secretExists: true,
secretData: map[string][]byte{"username": []byte("user"), "password": []byte("pass")},
mockResponseCode: http.StatusInternalServerError,
expectHyperNodes: false,
expectedHyperNodes: map[string]*topologyv1alpha1.HyperNode{},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
fakeClient := fake.NewSimpleClientset()
if tc.secretExists {
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: tc.config.Credentials.SecretRef.Name,
Namespace: tc.config.Credentials.SecretRef.Namespace,
},
Data: tc.secretData,
}
_, err := fakeClient.CoreV1().Secrets(tc.config.Credentials.SecretRef.Namespace).Create(
context.TODO(), secret, metav1.CreateOptions{},
)
if err != nil {
t.Fatalf("Failed to create test secret: %v", err)
}
}
var serverBaseURL string
// Setup test server (only for non-error configuration test cases)
if tc.config.Config["endpoint"] != "" && tc.secretExists && !tc.expectedError {
server := setupTestServer(t, tc.mockResponseCode)
defer server.Close()
serverBaseURL = server.URL
tc.config.Config["endpoint"] = serverBaseURL
}
u := NewUFMDiscoverer(tc.config, fakeClient)
outputCh, err := u.Start()
if tc.expectedError {
assert.Error(t, err)
return
}
assert.NoError(t, err)
// If HyperNodes are expected, verify the generated nodes
if tc.expectHyperNodes {
var hyperNodes []*topologyv1alpha1.HyperNode
select {
case hyperNodes = <-outputCh:
case <-time.After(time.Second):
t.Fatal("Timeout waiting for output")
}
assert.Equal(t, len(tc.expectedHyperNodes), len(hyperNodes), "Hypernode count should match")
for _, hn := range hyperNodes {
expected, exists := tc.expectedHyperNodes[hn.Name]
assert.True(t, exists, "Generated hypernode %s should exist in expected hypernodes", hn.Name)
assert.Equal(t, expected, hn)
}
}
u.Stop()
})
}
}
// setupTestServer creates and returns an HTTP test server that mocks the UFM API
func setupTestServer(t *testing.T, statusCode int) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(statusCode)
if statusCode == http.StatusOK {
data, err := json.Marshal(mockUfmData())
if err != nil {
t.Fatalf("Failed to serialize mock data: %v", err)
}
w.Write(data)
} else {
w.Write([]byte("Server Error"))
}
}))
}
// mockUfmData returns test UFM data for testing
func mockUfmData() []UFMInterface {
return []UFMInterface{
// group0
{SystemName: "node0", PeerNodeName: "leaf0", NodeDescription: "node00-x", Description: "Computer IB Port"},
{SystemName: "node0", PeerNodeName: "leaf1", NodeDescription: "node00-x", Description: "Computer IB Port"},
{SystemName: "node0", PeerNodeName: "leaf2", NodeDescription: "node00-x", Description: "Computer IB Port"},
{SystemName: "node0", PeerNodeName: "leaf3", NodeDescription: "node00-x", Description: "Computer IB Port"},
{SystemName: "node1", PeerNodeName: "leaf0", NodeDescription: "node01-x", Description: "Computer IB Port"},
{SystemName: "node1", PeerNodeName: "leaf1", NodeDescription: "node01-x", Description: "Computer IB Port"},
{SystemName: "node1", PeerNodeName: "leaf2", NodeDescription: "node01-x", Description: "Computer IB Port"},
{SystemName: "node1", PeerNodeName: "leaf3", NodeDescription: "node01-x", Description: "Computer IB Port"},
{SystemName: "node2", PeerNodeName: "leaf0", NodeDescription: "node02-x", Description: "Computer IB Port"},
{SystemName: "node2", PeerNodeName: "leaf1", NodeDescription: "node02-x", Description: "Computer IB Port"},
{SystemName: "node2", PeerNodeName: "leaf2", NodeDescription: "node02-x", Description: "Computer IB Port"},
{SystemName: "node2", PeerNodeName: "leaf3", NodeDescription: "node02-x", Description: "Computer IB Port"},
{SystemName: "node3", PeerNodeName: "leaf0", NodeDescription: "node03-x", Description: "Computer IB Port"},
{SystemName: "node3", PeerNodeName: "leaf1", NodeDescription: "node03-x", Description: "Computer IB Port"},
{SystemName: "node3", PeerNodeName: "leaf2", NodeDescription: "node03-x", Description: "Computer IB Port"},
{SystemName: "node3", PeerNodeName: "leaf3", NodeDescription: "node03-x", Description: "Computer IB Port"},
// group1
{SystemName: "node4", PeerNodeName: "leaf4", NodeDescription: "node14-x", Description: "Computer IB Port"},
{SystemName: "node4", PeerNodeName: "leaf5", NodeDescription: "node14-x", Description: "Computer IB Port"},
{SystemName: "node4", PeerNodeName: "leaf6", NodeDescription: "node14-x", Description: "Computer IB Port"},
{SystemName: "node5", PeerNodeName: "leaf4", NodeDescription: "node15-x", Description: "Computer IB Port"},
{SystemName: "node5", PeerNodeName: "leaf5", NodeDescription: "node15-x", Description: "Computer IB Port"},
{SystemName: "node5", PeerNodeName: "leaf6", NodeDescription: "node15-x", Description: "Computer IB Port"},
{SystemName: "node5", PeerNodeName: "leaf7", NodeDescription: "node15-x", Description: "Computer IB Port"},
{SystemName: "node6", PeerNodeName: "leaf4", NodeDescription: "node16-x", Description: "Computer IB Port"},
{SystemName: "node6", PeerNodeName: "leaf5", NodeDescription: "node16-x", Description: "Computer IB Port"},
{SystemName: "node6", PeerNodeName: "leaf6", NodeDescription: "node16-x", Description: "Computer IB Port"},
{SystemName: "node6", PeerNodeName: "leaf7", NodeDescription: "node16-x", Description: "Computer IB Port"},
{SystemName: "node7", PeerNodeName: "leaf4", NodeDescription: "node17-x", Description: "Computer IB Port"},
{SystemName: "node7", PeerNodeName: "leaf5", NodeDescription: "node17-x", Description: "Computer IB Port"},
{SystemName: "node7", PeerNodeName: "leaf6", NodeDescription: "node17-x", Description: "Computer IB Port"},
}
}
func buildHyperNode(name string, tier int, members []topologyv1alpha1.MemberSpec, labels map[string]string) *topologyv1alpha1.HyperNode {
return &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: topologyv1alpha1.HyperNodeSpec{
Tier: tier,
Members: members,
},
}
}
func expectedHyperNodes() map[string]*topologyv1alpha1.HyperNode {
return map[string]*topologyv1alpha1.HyperNode{
"leaf-hn-0": buildHyperNode("leaf-hn-0", 1,
[]topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "node0"}},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "node1"}},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "node2"}},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "node3"}},
},
}, map[string]string{api.NetworkTopologySourceLabelKey: "ufm"}),
"leaf-hn-1": buildHyperNode("leaf-hn-1", 1,
[]topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "node4"}},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "node5"}},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "node6"}},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "node7"}},
},
}, map[string]string{api.NetworkTopologySourceLabelKey: "ufm"}),
"spine-hn": buildHyperNode("spine-hn", 2,
[]topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeHyperNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "leaf-hn-0"}},
},
{
Type: topologyv1alpha1.MemberTypeHyperNode,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: "leaf-hn-1"}},
}}, map[string]string{api.NetworkTopologySourceLabelKey: "ufm"}),
}
}

View File

@ -0,0 +1,204 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hypernode
import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/informers"
coreinformers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
listersv1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog/v2"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
vcclientset "volcano.sh/apis/pkg/client/clientset/versioned"
vcinformer "volcano.sh/apis/pkg/client/informers/externalversions"
topologyinformerv1alpha1 "volcano.sh/apis/pkg/client/informers/externalversions/topology/v1alpha1"
topologylisterv1alpha1 "volcano.sh/apis/pkg/client/listers/topology/v1alpha1"
"volcano.sh/volcano/pkg/controllers/framework"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
"volcano.sh/volcano/pkg/controllers/hypernode/config"
"volcano.sh/volcano/pkg/controllers/hypernode/discovery"
"volcano.sh/volcano/pkg/controllers/hypernode/utils"
)
func init() {
framework.RegisterController(&hyperNodeController{})
}
const (
name = "hyperNode-controller"
)
type hyperNodeController struct {
vcClient vcclientset.Interface
kubeClient kubernetes.Interface
vcInformerFactory vcinformer.SharedInformerFactory
informerFactory informers.SharedInformerFactory
hyperNodeInformer topologyinformerv1alpha1.HyperNodeInformer
hyperNodeLister topologylisterv1alpha1.HyperNodeLister
hyperNodeQueue workqueue.TypedRateLimitingInterface[string]
configMapInformer coreinformers.ConfigMapInformer
configMapLister listersv1.ConfigMapLister
configMapQueue workqueue.TypedRateLimitingInterface[string]
discoveryManager discovery.Manager
configMapNamespace string
configMapName string
}
// Run starts the hyperNode controller
func (hn *hyperNodeController) Run(stopCh <-chan struct{}) {
hn.vcInformerFactory.Start(stopCh)
hn.informerFactory.Start(stopCh)
for informerType, ok := range hn.informerFactory.WaitForCacheSync(stopCh) {
if !ok {
klog.ErrorS(nil, "Failed to sync informer cache: %v", informerType)
return
}
}
for informerType, ok := range hn.vcInformerFactory.WaitForCacheSync(stopCh) {
if !ok {
klog.ErrorS(nil, "Failed to sync informer cache", "informerType", informerType)
return
}
}
if err := hn.discoveryManager.Start(); err != nil {
klog.ErrorS(err, "Failed to start network topology discovery manager")
return
}
go hn.watchDiscoveryResults()
// Start HyperNode queue processor
go hn.processHyperNodeQueue()
klog.InfoS("HyperNode controller started")
<-stopCh
hn.discoveryManager.Stop()
hn.hyperNodeQueue.ShutDown()
klog.InfoS("HyperNode controller stopped")
}
// Name returns the name of the controller
func (hn *hyperNodeController) Name() string {
return name
}
// Initialize initializes the hyperNode controller
func (hn *hyperNodeController) Initialize(opt *framework.ControllerOption) error {
hn.vcClient = opt.VolcanoClient
hn.kubeClient = opt.KubeClient
hn.vcInformerFactory = opt.VCSharedInformerFactory
hn.informerFactory = opt.SharedInformerFactory
hn.hyperNodeInformer = hn.vcInformerFactory.Topology().V1alpha1().HyperNodes()
hn.hyperNodeLister = hn.hyperNodeInformer.Lister()
hn.hyperNodeQueue = workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[string]())
hn.setConfigMapNamespaceAndName()
hn.setupConfigMapInformer()
hn.configMapLister = hn.configMapInformer.Lister()
hn.configMapQueue = workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[string]())
configLoader := config.NewConfigLoader(
hn.configMapLister,
hn.configMapNamespace,
hn.configMapName,
)
hn.discoveryManager = discovery.NewManager(configLoader, hn.configMapQueue, hn.kubeClient)
// Add event handlers for HyperNode
hn.hyperNodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: hn.addHyperNode,
UpdateFunc: hn.updateHyperNode,
})
return nil
}
func (hn *hyperNodeController) watchDiscoveryResults() {
resultCh := hn.discoveryManager.ResultChannel()
klog.InfoS("Starting to watch discovery results")
for result := range resultCh {
if result.HyperNodes != nil {
hn.reconcileTopology(result.Source, result.HyperNodes)
}
}
klog.InfoS("Discovery result channel closed")
}
// reconcileTopology reconciles the discovered topology with existing HyperNode resources
func (hn *hyperNodeController) reconcileTopology(source string, discoveredNodes []*topologyv1alpha1.HyperNode) {
klog.InfoS("Starting topology reconciliation", "source", source, "discoveredNodeCount", len(discoveredNodes))
existingNodes, err := hn.hyperNodeLister.List(labels.SelectorFromSet(labels.Set{
api.NetworkTopologySourceLabelKey: source,
}))
if err != nil {
klog.ErrorS(err, "Failed to list existing HyperNode resources")
return
}
existingNodeMap := make(map[string]*topologyv1alpha1.HyperNode)
for _, node := range existingNodes {
existingNodeMap[node.Name] = node
}
discoveredNodeMap := make(map[string]*topologyv1alpha1.HyperNode)
for _, node := range discoveredNodes {
if node.Labels == nil {
node.Labels = make(map[string]string)
}
node.Labels[api.NetworkTopologySourceLabelKey] = source
discoveredNodeMap[node.Name] = node
}
for name, node := range discoveredNodeMap {
if _, exists := existingNodeMap[name]; !exists {
klog.InfoS("Creating new HyperNode", "name", name, "source", source)
if err := utils.CreateHyperNode(hn.vcClient, node); err != nil {
klog.ErrorS(err, "Failed to create HyperNode", "name", name)
}
} else {
klog.InfoS("Updating HyperNode", "name", name, "source", source)
if err := utils.UpdateHyperNode(hn.vcClient, hn.hyperNodeLister, node); err != nil {
klog.ErrorS(err, "Failed to update HyperNode", "name", name)
}
}
delete(existingNodeMap, name)
}
for name := range existingNodeMap {
klog.InfoS("Deleting HyperNode", "name", name, "source", source)
if err := utils.DeleteHyperNode(hn.vcClient, name); err != nil {
klog.ErrorS(err, "Failed to delete HyperNode", "name", name)
}
}
klog.InfoS("Topology reconciliation completed",
"source", source,
"discovered", len(discoveredNodes),
"created/updated", len(discoveredNodeMap),
"deleted", len(existingNodeMap))
}

View File

@ -0,0 +1,254 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hypernode
import (
"context"
"os"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/informers"
k8sfake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/util/workqueue"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
vcclientset "volcano.sh/apis/pkg/client/clientset/versioned/fake"
vcinformer "volcano.sh/apis/pkg/client/informers/externalversions"
"volcano.sh/volcano/pkg/controllers/framework"
"volcano.sh/volcano/pkg/controllers/hypernode/api"
"volcano.sh/volcano/pkg/controllers/hypernode/config"
"volcano.sh/volcano/pkg/controllers/hypernode/discovery"
)
type mockDiscoveryManager struct {
startCalled bool
stopCalled bool
resultCh chan discovery.Result
mu sync.Mutex
}
func (m *mockDiscoveryManager) Start() error {
m.mu.Lock()
defer m.mu.Unlock()
m.startCalled = true
return nil
}
func (m *mockDiscoveryManager) Stop() {
m.mu.Lock()
defer m.mu.Unlock()
m.stopCalled = true
close(m.resultCh)
}
func (m *mockDiscoveryManager) ResultChannel() <-chan discovery.Result {
return m.resultCh
}
func TestHyperNodeController_Run(t *testing.T) {
stopCh := make(chan struct{})
fakeVcClient := vcclientset.NewSimpleClientset()
fakeKubeClient := k8sfake.NewSimpleClientset()
existingHyperNodes := []*topologyv1alpha1.HyperNode{
{
ObjectMeta: metav1.ObjectMeta{
Name: "existing-node-1",
Labels: map[string]string{
api.NetworkTopologySourceLabelKey: "ufm",
},
},
Spec: topologyv1alpha1.HyperNodeSpec{
Members: []topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{Name: "existing-node-1"},
},
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "existing-node-2",
Labels: map[string]string{
api.NetworkTopologySourceLabelKey: "ufm",
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "existing-node-3",
Labels: map[string]string{
api.NetworkTopologySourceLabelKey: "roce",
},
},
},
}
for _, node := range existingHyperNodes {
_, err := fakeVcClient.TopologyV1alpha1().HyperNodes().Create(context.TODO(), node, metav1.CreateOptions{})
assert.NoError(t, err, "Should be able to create the existing HyperNode")
}
vcInformerFactory := vcinformer.NewSharedInformerFactory(fakeVcClient, 0)
kubeInformerFactory := informers.NewSharedInformerFactory(fakeKubeClient, 0)
mockManager := &mockDiscoveryManager{
resultCh: make(chan discovery.Result),
}
controller := &hyperNodeController{
vcClient: fakeVcClient,
kubeClient: fakeKubeClient,
vcInformerFactory: vcInformerFactory,
informerFactory: kubeInformerFactory,
hyperNodeInformer: vcInformerFactory.Topology().V1alpha1().HyperNodes(),
hyperNodeLister: vcInformerFactory.Topology().V1alpha1().HyperNodes().Lister(),
configMapInformer: kubeInformerFactory.Core().V1().ConfigMaps(),
configMapLister: kubeInformerFactory.Core().V1().ConfigMaps().Lister(),
discoveryManager: mockManager,
configMapNamespace: "test-namespace",
configMapName: "test-release-controller-configmap",
hyperNodeQueue: workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[string]()),
}
go controller.Run(stopCh)
time.Sleep(time.Second)
assert.True(t, func() bool { mockManager.mu.Lock(); defer mockManager.mu.Unlock(); return mockManager.startCalled }(), "Discovery manager should be started")
// phase1: update and create hypernode
go func() {
updatedHyperNode := &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: "existing-node-1",
},
Spec: topologyv1alpha1.HyperNodeSpec{
Members: []topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{Name: "updated-node-1"},
},
},
},
},
}
newHyperNode := &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: "new-hypernode",
},
Spec: topologyv1alpha1.HyperNodeSpec{},
}
mockManager.resultCh <- discovery.Result{
Source: "ufm",
HyperNodes: []*topologyv1alpha1.HyperNode{updatedHyperNode, newHyperNode},
}
}()
time.Sleep(300 * time.Millisecond)
// verify if the existing HyperNode is updated
updatedNode, err := fakeVcClient.TopologyV1alpha1().HyperNodes().Get(context.TODO(), "existing-node-1", metav1.GetOptions{})
assert.NoError(t, err, "Should be able to get the updated HyperNode")
assert.Equal(t, "updated-node-1", updatedNode.Spec.Members[0].Selector.ExactMatch.Name)
// verify if the new HyperNode is created
_, err = fakeVcClient.TopologyV1alpha1().HyperNodes().Get(context.TODO(), "new-hypernode", metav1.GetOptions{})
assert.NoError(t, err, "Should be able to get the created HyperNode")
// phase2: delete hypernode
go func() {
mockManager.resultCh <- discovery.Result{
Source: "ufm",
HyperNodes: []*topologyv1alpha1.HyperNode{},
}
}()
time.Sleep(300 * time.Millisecond)
// verify if the existing HyperNode with source match is deleted
nodeList, err := fakeVcClient.TopologyV1alpha1().HyperNodes().List(context.TODO(), metav1.ListOptions{
LabelSelector: labels.SelectorFromSet(labels.Set{
api.NetworkTopologySourceLabelKey: "ufm",
}).String(),
})
assert.NoError(t, err)
assert.Equal(t, 0, len(nodeList.Items), "All HyperNodes should have been deleted")
// verify if the existing HyperNode with source match is deleted
nodeList, err = fakeVcClient.TopologyV1alpha1().HyperNodes().List(context.TODO(), metav1.ListOptions{
LabelSelector: labels.SelectorFromSet(labels.Set{
api.NetworkTopologySourceLabelKey: "roce",
}).String(),
})
assert.NoError(t, err)
assert.Equal(t, 1, len(nodeList.Items), "HyperNodes from different discovery sources should not be deleted")
close(stopCh)
time.Sleep(100 * time.Millisecond)
assert.True(t, func() bool { mockManager.mu.Lock(); defer mockManager.mu.Unlock(); return mockManager.stopCalled }(), "Discovery manager should be stopped")
}
func TestHyperNodeController_Initialize(t *testing.T) {
os.Setenv(config.NamespaceEnvKey, "test-namespace")
os.Setenv(config.ReleaseNameEnvKey, "test-release")
defer func() {
os.Unsetenv(config.NamespaceEnvKey)
os.Unsetenv(config.ReleaseNameEnvKey)
}()
fakeVcClient := vcclientset.NewSimpleClientset()
fakeKubeClient := k8sfake.NewSimpleClientset()
vcInformerFactory := vcinformer.NewSharedInformerFactory(fakeVcClient, 0)
kubeInformerFactory := informers.NewSharedInformerFactory(fakeKubeClient, 0)
controller := &hyperNodeController{
informerFactory: kubeInformerFactory,
}
err := controller.Initialize(&framework.ControllerOption{
VolcanoClient: fakeVcClient,
KubeClient: fakeKubeClient,
VCSharedInformerFactory: vcInformerFactory,
SharedInformerFactory: kubeInformerFactory,
})
assert.NoError(t, err)
assert.Equal(t, fakeVcClient, controller.vcClient)
assert.Equal(t, fakeKubeClient, controller.kubeClient)
assert.Equal(t, vcInformerFactory, controller.vcInformerFactory)
assert.NotNil(t, controller.hyperNodeInformer)
assert.NotNil(t, controller.hyperNodeLister)
assert.NotNil(t, controller.discoveryManager)
assert.NotNil(t, controller.configMapQueue)
assert.Equal(t, "test-namespace", controller.configMapNamespace)
assert.Equal(t, "test-release-controller-configmap", controller.configMapName)
}

View File

@ -0,0 +1,116 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hypernode
import (
"context"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
)
func (hn *hyperNodeController) addHyperNode(obj interface{}) {
hyperNode, ok := obj.(*topologyv1alpha1.HyperNode)
if !ok {
klog.ErrorS(nil, "Cannot convert to *topologyv1alpha1.HyperNode", "obj", obj)
return
}
klog.V(3).InfoS("Add HyperNode", "name", hyperNode.Name)
hn.enqueueHyperNode(hyperNode)
}
func (hn *hyperNodeController) updateHyperNode(oldObj, newObj interface{}) {
hyperNode, ok := newObj.(*topologyv1alpha1.HyperNode)
if !ok {
klog.ErrorS(nil, "Cannot convert to *topologyv1alpha1.HyperNode", "obj", newObj)
return
}
klog.V(3).InfoS("Update HyperNode", "name", hyperNode.Name)
hn.enqueueHyperNode(hyperNode)
}
func (hn *hyperNodeController) enqueueHyperNode(hyperNode *topologyv1alpha1.HyperNode) {
key, err := cache.MetaNamespaceKeyFunc(hyperNode)
if err != nil {
klog.ErrorS(err, "Failed to get key for HyperNode", "hyperNode", hyperNode.Name)
return
}
hn.hyperNodeQueue.Add(key)
}
// processHyperNodeQueue processes items from the hyperNode work queue
func (hn *hyperNodeController) processHyperNodeQueue() {
for {
key, shutdown := hn.hyperNodeQueue.Get()
if shutdown {
klog.InfoS("HyperNode queue has been shut down")
return
}
func() {
defer hn.hyperNodeQueue.Done(key)
err := hn.syncHyperNodeStatus(key)
if err != nil {
klog.ErrorS(err, "Error syncing HyperNode", "key", key)
hn.hyperNodeQueue.AddRateLimited(key)
return
}
hn.hyperNodeQueue.Forget(key)
}()
}
}
// syncHyperNodeStatus updates the NodeCount field in HyperNode status
func (hn *hyperNodeController) syncHyperNodeStatus(key string) error {
_, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
klog.ErrorS(err, "Failed to split meta namespace key", "key", key)
return err
}
hyperNode, err := hn.hyperNodeLister.Get(name)
if err != nil {
// Check if the error happens because the HyperNode is deleted
if errors.IsNotFound(err) {
klog.InfoS("HyperNode has been deleted, no status update needed", "name", name)
return nil
}
klog.ErrorS(err, "Failed to get HyperNode", "name", name)
return err
}
nodeCount := len(hyperNode.Spec.Members)
if hyperNode.Status.NodeCount != int64(nodeCount) {
// Create a deep copy to avoid modifying cache objects
hyperNodeCopy := hyperNode.DeepCopy()
hyperNodeCopy.Status.NodeCount = int64(nodeCount)
_, err = hn.vcClient.TopologyV1alpha1().HyperNodes().UpdateStatus(context.Background(), hyperNodeCopy, metav1.UpdateOptions{})
if err != nil {
klog.ErrorS(err, "Failed to update HyperNode status", "name", name)
return err
}
klog.V(3).InfoS("Updated HyperNode status", "name", name, "nodeCount", nodeCount)
}
return nil
}

View File

@ -0,0 +1,393 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hypernode
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
testing2 "k8s.io/client-go/testing"
"k8s.io/client-go/util/workqueue"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
vcclientsetfake "volcano.sh/apis/pkg/client/clientset/versioned/fake"
vcinformer "volcano.sh/apis/pkg/client/informers/externalversions"
)
func newFakeHyperNodeController() (*hyperNodeController, *vcclientsetfake.Clientset, *fake.Clientset) {
vcClient := vcclientsetfake.NewSimpleClientset()
kubeClient := fake.NewSimpleClientset()
vcInformerFactory := vcinformer.NewSharedInformerFactory(vcClient, 0)
informerFactory := informers.NewSharedInformerFactory(kubeClient, 0)
controller := &hyperNodeController{
vcClient: vcClient,
kubeClient: kubeClient,
vcInformerFactory: vcInformerFactory,
informerFactory: informerFactory,
hyperNodeInformer: vcInformerFactory.Topology().V1alpha1().HyperNodes(),
hyperNodeLister: vcInformerFactory.Topology().V1alpha1().HyperNodes().Lister(),
hyperNodeQueue: workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[string]()),
}
return controller, vcClient, kubeClient
}
func TestAddHyperNode(t *testing.T) {
controller, _, _ := newFakeHyperNodeController()
// Test normal case
hyperNode := &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: "test-hypernode",
},
}
controller.addHyperNode(hyperNode)
// Verify that there is one element in the queue
assert.Equal(t, 1, controller.hyperNodeQueue.Len())
// Test error case (non-HyperNode object)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
}
controller.addHyperNode(pod)
// Verify that the queue length is still 1 (error object not added)
assert.Equal(t, 1, controller.hyperNodeQueue.Len())
}
func TestUpdateHyperNode(t *testing.T) {
controller, _, _ := newFakeHyperNodeController()
oldHyperNode := &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: "test-hypernode",
},
Spec: topologyv1alpha1.HyperNodeSpec{
Members: []topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node1",
},
},
},
},
},
}
newHyperNode := oldHyperNode.DeepCopy()
newHyperNode.Spec.Members = append(newHyperNode.Spec.Members, topologyv1alpha1.MemberSpec{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node2",
},
},
})
controller.updateHyperNode(oldHyperNode, newHyperNode)
// Verify that there is one element in the queue
assert.Equal(t, 1, controller.hyperNodeQueue.Len())
// Test error case (non-HyperNode object)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
}
controller.updateHyperNode(oldHyperNode, pod)
// Verify that the queue length is still 1 (error object not added)
assert.Equal(t, 1, controller.hyperNodeQueue.Len())
}
func TestEnqueueHyperNode(t *testing.T) {
controller, _, _ := newFakeHyperNodeController()
hyperNode := &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: "test-hypernode",
},
}
controller.enqueueHyperNode(hyperNode)
// Verify that there is one element in the queue
assert.Equal(t, 1, controller.hyperNodeQueue.Len())
// Check if the element in the queue is the correct key
key, shutdown := controller.hyperNodeQueue.Get()
assert.False(t, shutdown)
assert.Equal(t, "test-hypernode", key)
}
func TestSyncHyperNodeStatus(t *testing.T) {
testCases := []struct {
name string
hyperNodeKey string
setupFunc func(controller *hyperNodeController, vcClient *vcclientsetfake.Clientset) *topologyv1alpha1.HyperNode
expectedError bool
expectedUpdate bool
expectedCount int64
}{
{
name: "Normal case: NodeCount needs to be updated",
hyperNodeKey: "test-hypernode-1",
setupFunc: func(controller *hyperNodeController, vcClient *vcclientsetfake.Clientset) *topologyv1alpha1.HyperNode {
hyperNode := &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: "test-hypernode-1",
},
Spec: topologyv1alpha1.HyperNodeSpec{
Members: []topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node1",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node2",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node3",
},
},
},
},
},
Status: topologyv1alpha1.HyperNodeStatus{
NodeCount: 0, // Initial count does not match actual count
},
}
_, err := vcClient.TopologyV1alpha1().HyperNodes().Create(context.Background(), hyperNode, metav1.CreateOptions{})
assert.NoError(t, err)
return hyperNode
},
expectedError: false,
expectedUpdate: true,
expectedCount: 3,
},
{
name: "Node does not exist",
hyperNodeKey: "non-existent",
setupFunc: func(controller *hyperNodeController, vcClient *vcclientsetfake.Clientset) *topologyv1alpha1.HyperNode {
return nil
},
expectedError: false,
expectedUpdate: false,
expectedCount: 0,
},
{
name: "NodeCount already matches, no update needed",
hyperNodeKey: "test-hypernode-2",
setupFunc: func(controller *hyperNodeController, vcClient *vcclientsetfake.Clientset) *topologyv1alpha1.HyperNode {
hyperNode := &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: "test-hypernode-2",
},
Spec: topologyv1alpha1.HyperNodeSpec{
Members: []topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node1",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node2",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node3",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node4",
},
},
},
},
},
Status: topologyv1alpha1.HyperNodeStatus{
NodeCount: 4, // Already matches, no update needed
},
}
_, err := vcClient.TopologyV1alpha1().HyperNodes().Create(context.Background(), hyperNode, metav1.CreateOptions{})
assert.NoError(t, err)
return hyperNode
},
expectedError: false,
expectedUpdate: false,
expectedCount: 4,
},
{
name: "API error case",
hyperNodeKey: "test-hypernode-3",
setupFunc: func(controller *hyperNodeController, vcClient *vcclientsetfake.Clientset) *topologyv1alpha1.HyperNode {
hyperNode := &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: "test-hypernode-3",
},
Spec: topologyv1alpha1.HyperNodeSpec{
Members: []topologyv1alpha1.MemberSpec{
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node1",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node2",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node3",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node4",
},
},
},
{
Type: topologyv1alpha1.MemberTypeNode,
Selector: topologyv1alpha1.MemberSelector{
ExactMatch: &topologyv1alpha1.ExactMatch{
Name: "node5",
},
},
},
},
},
Status: topologyv1alpha1.HyperNodeStatus{
NodeCount: 0, // Needs update
},
}
_, err := vcClient.TopologyV1alpha1().HyperNodes().Create(context.Background(), hyperNode, metav1.CreateOptions{})
assert.NoError(t, err)
// Add error when updating status
vcClient.PrependReactor("update", "hypernodes", func(action testing2.Action) (handled bool, ret runtime.Object, err error) {
updateAction := action.(testing2.UpdateAction)
obj := updateAction.GetObject()
hn, ok := obj.(*topologyv1alpha1.HyperNode)
if ok && hn.Name == "test-hypernode-3" {
return true, nil, errors.NewInternalError(assert.AnError)
}
return false, nil, nil
})
return hyperNode
},
expectedError: true,
expectedUpdate: true,
expectedCount: 5,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
controller, vcClient, _ := newFakeHyperNodeController()
// Start informer
controller.vcInformerFactory.Start(nil)
controller.vcInformerFactory.WaitForCacheSync(nil)
// Set up test environment
hyperNode := tc.setupFunc(controller, vcClient)
if hyperNode != nil {
// Wait for informer to update
controller.vcInformerFactory.Topology().V1alpha1().HyperNodes().Informer().GetIndexer().Add(hyperNode)
}
err := controller.syncHyperNodeStatus(tc.hyperNodeKey)
// Verify results
if tc.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
// If update is expected and no error, verify status
if tc.expectedUpdate && !tc.expectedError && hyperNode != nil {
updatedHyperNode, err := vcClient.TopologyV1alpha1().HyperNodes().Get(context.Background(), hyperNode.Name, metav1.GetOptions{})
assert.NoError(t, err)
assert.Equal(t, tc.expectedCount, updatedHyperNode.Status.NodeCount)
}
})
}
}

View File

@ -0,0 +1,104 @@
/*
Copyright 2025 The Volcano Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package utils
import (
"context"
"sort"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/retry"
topologyv1alpha1 "volcano.sh/apis/pkg/apis/topology/v1alpha1"
vcclientset "volcano.sh/apis/pkg/client/clientset/versioned"
"volcano.sh/apis/pkg/client/listers/topology/v1alpha1"
)
func CreateHyperNode(vcClient vcclientset.Interface, node *topologyv1alpha1.HyperNode) error {
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
_, err := vcClient.TopologyV1alpha1().HyperNodes().Create(
context.Background(),
node,
metav1.CreateOptions{},
)
return err
})
}
func UpdateHyperNode(vcClient vcclientset.Interface, lister v1alpha1.HyperNodeLister, updated *topologyv1alpha1.HyperNode) error {
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
current, err := lister.Get(updated.Name)
if err != nil {
return err
}
current.Spec = updated.Spec
current.Status = updated.Status
if current.Labels == nil {
current.Labels = make(map[string]string)
}
for k, v := range updated.Labels {
current.Labels[k] = v
}
if current.Annotations == nil {
current.Annotations = make(map[string]string)
}
for k, v := range updated.Annotations {
current.Annotations[k] = v
}
_, err = vcClient.TopologyV1alpha1().HyperNodes().UpdateStatus(context.Background(), current, metav1.UpdateOptions{})
return err
})
}
func DeleteHyperNode(vcClient vcclientset.Interface, name string) error {
return vcClient.TopologyV1alpha1().HyperNodes().Delete(
context.Background(),
name,
metav1.DeleteOptions{},
)
}
// BuildHyperNode creates a HyperNode object
func BuildHyperNode(name string, tier int, members []topologyv1alpha1.MemberSpec, labels map[string]string) *topologyv1alpha1.HyperNode {
return &topologyv1alpha1.HyperNode{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: topologyv1alpha1.HyperNodeSpec{
Tier: tier,
Members: members,
},
}
}
// BuildMembers creates a list of topology member references
func BuildMembers(names []string, memberType topologyv1alpha1.MemberType) []topologyv1alpha1.MemberSpec {
members := make([]topologyv1alpha1.MemberSpec, 0, len(names))
sort.Strings(names)
for _, name := range names {
members = append(members, topologyv1alpha1.MemberSpec{
Type: memberType,
Selector: topologyv1alpha1.MemberSelector{ExactMatch: &topologyv1alpha1.ExactMatch{Name: name}},
})
}
return members
}

Some files were not shown because too many files have changed in this diff Show More