Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
apiVersion: config.karmada.io/v1alpha1
kind: ResourceInterpreterCustomization
metadata:
name: declarative-configuration-job
spec:
target:
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
customizations:
healthInterpretation:
luaScript: >
function InterpretHealth(observedObj)
if observedObj.status == nil or observedObj.status.state == nil then
return false
end
local phase = observedObj.status.state.phase
if phase == nil or phase == '' then
return false
end
if phase == 'Running' or phase == 'Completed' or phase == "Pending" or
phase == "Aborting" or phase == "Aborted" or phase == "Restarting" or
phase == "Completing" or phase == "Terminating" or phase == "Terminated" then
return true
end
return false
end
componentResource:
luaScript: |
local kube = require("kube")
local function get(obj, path)
local cur = obj
for i = 1, #path do
if cur == nil then return nil end
cur = cur[path[i]]
end
return cur
end
local function to_num(v, default)
if v == nil or v == '' then
return default
end
local n = tonumber(v)
if n ~= nil then return n end
return default
end
function GetComponents(observedObj)
local components = {}
local tasks = get(observedObj, {"spec","tasks"})
if tasks == nil then
return components
end
for _, task in ipairs(tasks) do
local replicas = to_num(task.minAvailable, 1)
requires = kube.accuratePodRequirements(task.template)
table.insert(components, {
name = task.name,
replicas = replicas,
replicaRequirements = requires
})
end
return components
end
statusAggregation:
luaScript: >
local function durationVal(d)
if type(d) == "number" then
return d
end
if type(d) ~= "string" then
return 0
end
local totalSeconds = 0
for num, unit in string.gmatch(d, "([%d%.]+)([hms])") do
num = tonumber(num)
if unit == "h" then
totalSeconds = totalSeconds + num * 3600
elseif unit == "m" then
totalSeconds = totalSeconds + num * 60
elseif unit == "s" then
totalSeconds = totalSeconds + num
end
end
if totalSeconds > 0 then
return totalSeconds
end
return tonumber(d) or 0
end
local function omitEmpty(t)
if t == nil then return nil end
local out = {}
for k, v in pairs(t) do
if type(v) == "table" then
local inner = omitEmpty(v)
if inner ~= nil and next(inner) ~= nil then
out[k] = inner
end
elseif v ~= nil and not (v == 0 or v == "" or v == "0s") then
out[k] = v
end
end
if next(out) ~= nil then
return out
else
return nil
end
end
function AggregateStatus(desiredObj, statusItems)
if statusItems == nil then return desiredObj end
if desiredObj.status == nil then desiredObj.status = {} end
local status = {
state = {},
minAvailable = 0,
taskStatusCount = {},
pending = 0,
running = 0,
succeeded = 0,
failed = 0,
terminating = 0,
unknown = 0,
version = 0,
retryCount = 0,
controlledResources = {},
conditions = {},
runningDuration = "0s",
state = {}
}
for i = 1, #statusItems do
local s = statusItems[i].status
if s ~= nil then
status.minAvailable = status.minAvailable + (s.minAvailable or 0)
status.pending = status.pending + (s.pending or 0)
status.running = status.running + (s.running or 0)
status.succeeded = status.succeeded + (s.succeeded or 0)
status.failed = status.failed + (s.failed or 0)
status.terminating = status.terminating + (s.terminating or 0)
status.unknown = status.unknown + (s.unknown or 0)
status.version = math.max(status.version, s.version or 0)
status.retryCount = status.retryCount + (s.retryCount or 0)
status.state = s.state
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state might be overwritten unexpectly, for instance, we are aggreatingstates from two member clusters, the second state would overrite the first one.
But it good enough for current stage, as Karmada not supports split Volcano Jobs currently.

It's hard to aggreate the state, we have met this situation when aggreating Kubernetes Jobs at https://github.com/karmada-io/karmada/blob/adef1e59748e1e1d31cb75fffe406b5dd69a66d7/pkg/util/helper/job.go#L76-L9.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dekaihu Any idea about how to handle the state aggregation?

if durationVal(s.runningDuration) > durationVal(status.runningDuration) then
status.runningDuration = s.runningDuration
end
if s.taskStatusCount ~= nil then
for taskName, taskStatus in pairs(s.taskStatusCount) do
if status.taskStatusCount[taskName] == nil then
status.taskStatusCount[taskName] = { phase = {} }
end
if taskStatus.phase ~= nil then
for phaseName, count in pairs(taskStatus.phase) do
status.taskStatusCount[taskName].phase[phaseName] = (status.taskStatusCount[taskName].phase[phaseName] or 0) + count
end
end
end
end
if s.controlledResources then
for k, v in pairs(s.controlledResources) do
status.controlledResources[k] = v
end
end
if s.conditions ~= nil then
for _, c in ipairs(s.conditions) do
table.insert(status.conditions, c)
end
end
end
end
desiredObj.status = omitEmpty(status) or {}
return desiredObj
end
statusReflection:
luaScript: >
function ReflectStatus(observedObj)
local status = {}
if observedObj == nil or observedObj.status == nil then
return status
end
local s = observedObj.status
status.minAvailable = s.minAvailable
status.pending = s.pending
status.running = s.running
status.succeeded = s.succeeded
status.failed = s.failed
status.terminating = s.terminating
status.unknown = s.unknown
status.version = s.version
status.retryCount = s.retryCount
status.runningDuration = s.runningDuration
status.taskStatusCount = {}
if s.taskStatusCount ~= nil then
for k, v in pairs(s.taskStatusCount) do
status.taskStatusCount[k] = v
end
end
status.controlledResources = {}
if s.controlledResources ~= nil then
for k, v in pairs(s.controlledResources) do
status.controlledResources[k] = v
end
end
if s.state ~= nil then
status.state = s.state
end
status.conditions = {}
if type(s.conditions) == "table" then
for _, cond in ipairs(s.conditions) do
table.insert(status.conditions, cond)
end
end
return status
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tests:
- desiredInputPath: testdata/desired-job.yaml
statusInputPath: testdata/status-file.yaml
operation: AggregateStatus
- observedInputPath: testdata/observed-job.yaml
operation: InterpretHealth
- observedInputPath: testdata/observed-job.yaml
operation: InterpretStatus
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: dk-job
spec:
maxRetry: 3
minAvailable: 3
plugins:
env: []
ssh: []
svc:
- --disable-network-policy=true
queue: default
schedulerName: volcano
tasks:
- minAvailable: 1
name: job-nginx1
replicas: 1
template:
metadata:
name: nginx1
spec:
containers:
- args:
- sleep 10
command:
- bash
- -c
image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
resources:
requests:
cpu: 100m
nodeSelector:
kubernetes.io/os: linux
restartPolicy: OnFailure
- minAvailable: 2
name: job-nginx2
replicas: 3
template:
metadata:
name: nginx2
spec:
containers:
- args:
- sleep 30
command:
- bash
- -c
image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
resources:
requests:
cpu: 100m
nodeSelector:
kubernetes.io/os: linux
restartPolicy: OnFailure
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: dk-job
spec:
maxRetry: 3
minAvailable: 3
plugins:
env: []
ssh: []
svc:
- --disable-network-policy=true
queue: default
schedulerName: volcano
tasks:
- minAvailable: 1
name: job-nginx1
replicas: 1
template:
metadata:
name: nginx1
spec:
containers:
- args:
- sleep 10
command:
- bash
- -c
image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
resources:
requests:
cpu: 100m
nodeSelector:
kubernetes.io/os: linux
restartPolicy: OnFailure
- minAvailable: 2
name: job-nginx2
replicas: 3
template:
metadata:
name: nginx2
spec:
containers:
- args:
- sleep 30
command:
- bash
- -c
image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
resources:
requests:
cpu: 100m
nodeSelector:
kubernetes.io/os: linux
restartPolicy: OnFailure
status:
conditions:
- lastTransitionTime: "2025-09-29T10:57:03Z"
status: Pending
- lastTransitionTime: "2025-09-29T10:57:07Z"
status: Running
- lastTransitionTime: "2025-09-29T10:57:38Z"
status: Completed
failed: 0
minAvailable: 3
pending: 0
retryCount: 0
running: 0
runningDuration: 35.688396584s
state:
lastTransitionTime: "2025-09-29T10:57:38Z"
phase: Completed
succeeded: 4
taskStatusCount:
job-nginx1:
phase:
Succeeded: 1
job-nginx2:
phase:
Succeeded: 3
terminating: 0
unknown: 0
version: 1
Loading