| 
 | 1 | +apiVersion: config.karmada.io/v1alpha1  | 
 | 2 | +kind: ResourceInterpreterCustomization  | 
 | 3 | +metadata:  | 
 | 4 | +  name: declarative-configuration-job  | 
 | 5 | +spec:  | 
 | 6 | +  target:  | 
 | 7 | +    apiVersion: batch.volcano.sh/v1alpha1  | 
 | 8 | +    kind: Job  | 
 | 9 | +  customizations:  | 
 | 10 | +    healthInterpretation:  | 
 | 11 | +      luaScript: >  | 
 | 12 | +        function InterpretHealth(observedObj)  | 
 | 13 | +          if observedObj.status == nil or observedObj.status.state == nil then  | 
 | 14 | +            return false  | 
 | 15 | +          end  | 
 | 16 | +          local phase = observedObj.status.state.phase  | 
 | 17 | +          if phase == nil or phase == '' then  | 
 | 18 | +            return false  | 
 | 19 | +          end  | 
 | 20 | +          if phase == 'Running' or phase == 'Completed' or phase == "Pending" or  | 
 | 21 | +          phase == "Aborting" or phase == "Aborted" or phase == "Restarting" or  | 
 | 22 | +          phase == "Completing" or phase == "Terminating" or phase == "Terminated" then  | 
 | 23 | +            return true  | 
 | 24 | +          end  | 
 | 25 | +          return false  | 
 | 26 | +        end  | 
 | 27 | +    componentResource:  | 
 | 28 | +      luaScript: |  | 
 | 29 | +        local kube = require("kube")  | 
 | 30 | +        local function get(obj, path)  | 
 | 31 | +          local cur = obj  | 
 | 32 | +          for i = 1, #path do  | 
 | 33 | +            if cur == nil then return nil end  | 
 | 34 | +            cur = cur[path[i]]  | 
 | 35 | +          end  | 
 | 36 | +          return cur  | 
 | 37 | +        end  | 
 | 38 | +
  | 
 | 39 | +        local function to_num(v, default)  | 
 | 40 | +          if v == nil or v == '' then  | 
 | 41 | +            return default  | 
 | 42 | +          end  | 
 | 43 | +          local n = tonumber(v)  | 
 | 44 | +          if n ~= nil then return n end  | 
 | 45 | +          return default  | 
 | 46 | +        end  | 
 | 47 | +
  | 
 | 48 | +        function GetComponents(observedObj)  | 
 | 49 | +          local components = {}  | 
 | 50 | +
  | 
 | 51 | +          local tasks = get(observedObj, {"spec","tasks"})  | 
 | 52 | +          if tasks == nil then  | 
 | 53 | +            return components  | 
 | 54 | +          end  | 
 | 55 | +
  | 
 | 56 | +          for _, task in ipairs(tasks) do  | 
 | 57 | +            local replicas = to_num(task.minAvailable, 1)  | 
 | 58 | +            requires = kube.accuratePodRequirements(task.template)  | 
 | 59 | +
  | 
 | 60 | +            table.insert(components, {  | 
 | 61 | +              name = task.name,  | 
 | 62 | +              replicas = replicas,  | 
 | 63 | +              replicaRequirements = requires  | 
 | 64 | +            })  | 
 | 65 | +          end  | 
 | 66 | +
  | 
 | 67 | +          return components  | 
 | 68 | +        end  | 
 | 69 | +    statusAggregation:  | 
 | 70 | +      luaScript: >  | 
 | 71 | +        local function durationVal(d)  | 
 | 72 | +          if type(d) == "number" then  | 
 | 73 | +            return d  | 
 | 74 | +          end  | 
 | 75 | +          if type(d) ~= "string" then  | 
 | 76 | +            return 0  | 
 | 77 | +          end  | 
 | 78 | +          local totalSeconds = 0  | 
 | 79 | +          for num, unit in string.gmatch(d, "([%d%.]+)([hms])") do  | 
 | 80 | +            num = tonumber(num)  | 
 | 81 | +            if unit == "h" then  | 
 | 82 | +              totalSeconds = totalSeconds + num * 3600  | 
 | 83 | +            elseif unit == "m" then  | 
 | 84 | +              totalSeconds = totalSeconds + num * 60  | 
 | 85 | +            elseif unit == "s" then  | 
 | 86 | +              totalSeconds = totalSeconds + num  | 
 | 87 | +            end  | 
 | 88 | +          end  | 
 | 89 | +          if totalSeconds > 0 then  | 
 | 90 | +            return totalSeconds  | 
 | 91 | +          end  | 
 | 92 | +          return tonumber(d) or 0  | 
 | 93 | +        end  | 
 | 94 | +        local function omitEmpty(t)  | 
 | 95 | +          if t == nil then return nil end  | 
 | 96 | +          local out = {}  | 
 | 97 | +          for k, v in pairs(t) do  | 
 | 98 | +            if type(v) == "table" then  | 
 | 99 | +              local inner = omitEmpty(v)  | 
 | 100 | +              if inner ~= nil and next(inner) ~= nil then  | 
 | 101 | +                out[k] = inner  | 
 | 102 | +              end  | 
 | 103 | +            elseif v ~= nil and not (v == 0 or v == "" or v == "0s") then  | 
 | 104 | +              out[k] = v  | 
 | 105 | +            end  | 
 | 106 | +          end  | 
 | 107 | +          if next(out) ~= nil then  | 
 | 108 | +            return out  | 
 | 109 | +          else  | 
 | 110 | +            return nil  | 
 | 111 | +          end  | 
 | 112 | +        end  | 
 | 113 | +
  | 
 | 114 | +        function AggregateStatus(desiredObj, statusItems)  | 
 | 115 | +          if statusItems == nil then return desiredObj end  | 
 | 116 | +          if desiredObj.status == nil then desiredObj.status = {} end  | 
 | 117 | +            | 
 | 118 | +          if #statusItems == 1 then  | 
 | 119 | +            desiredObj.status = statusItems[1].status  | 
 | 120 | +            return desiredObj  | 
 | 121 | +          end  | 
 | 122 | +            | 
 | 123 | +          local failedClusters = {}  | 
 | 124 | +          local completedClusters = 0  | 
 | 125 | +          local latestTransition = {}  | 
 | 126 | +          local successfulClusters = 0  | 
 | 127 | +          local hasFailed = false  | 
 | 128 | +          local failedConditions = {}  | 
 | 129 | +          local failedPhases = {  | 
 | 130 | +            Failed = true,  | 
 | 131 | +            Aborted = true,  | 
 | 132 | +            Aborting = true,  | 
 | 133 | +            Terminated = true,  | 
 | 134 | +            Terminating = true,  | 
 | 135 | +          }  | 
 | 136 | +          local status = {  | 
 | 137 | +            state = {},  | 
 | 138 | +            minAvailable = 0,  | 
 | 139 | +            taskStatusCount = {},  | 
 | 140 | +            pending = 0,  | 
 | 141 | +            running = 0,  | 
 | 142 | +            succeeded = 0,  | 
 | 143 | +            failed = 0,  | 
 | 144 | +            terminating = 0,  | 
 | 145 | +            unknown = 0,  | 
 | 146 | +            version = 0,  | 
 | 147 | +            retryCount = 0,  | 
 | 148 | +            controlledResources = {},  | 
 | 149 | +            conditions = {},  | 
 | 150 | +            runningDuration = "0s",  | 
 | 151 | +          }  | 
 | 152 | +
  | 
 | 153 | +          for i = 1, #statusItems do  | 
 | 154 | +            local s = statusItems[i].status  | 
 | 155 | +            if s ~= nil then  | 
 | 156 | +              status.minAvailable = status.minAvailable + (s.minAvailable or 0)  | 
 | 157 | +              status.pending = status.pending + (s.pending or 0)  | 
 | 158 | +              status.running = status.running + (s.running or 0)  | 
 | 159 | +              status.succeeded = status.succeeded + (s.succeeded or 0)  | 
 | 160 | +              status.failed = status.failed + (s.failed or 0)  | 
 | 161 | +              status.terminating = status.terminating + (s.terminating or 0)  | 
 | 162 | +              status.unknown = status.unknown + (s.unknown or 0)  | 
 | 163 | +              status.version = math.max(status.version, s.version or 0)  | 
 | 164 | +              status.retryCount = status.retryCount + (s.retryCount or 0)  | 
 | 165 | +              if durationVal(s.runningDuration) > durationVal(status.runningDuration) then  | 
 | 166 | +                status.runningDuration = s.runningDuration  | 
 | 167 | +              end  | 
 | 168 | +
  | 
 | 169 | +              if s.taskStatusCount ~= nil then  | 
 | 170 | +                for taskName, taskStatus in pairs(s.taskStatusCount) do  | 
 | 171 | +                  if status.taskStatusCount[taskName] == nil then  | 
 | 172 | +                    status.taskStatusCount[taskName] = { phase = {} }  | 
 | 173 | +                  end  | 
 | 174 | +                  if taskStatus.phase ~= nil then  | 
 | 175 | +                    for phaseName, count in pairs(taskStatus.phase) do  | 
 | 176 | +                      status.taskStatusCount[taskName].phase[phaseName] = (status.taskStatusCount[taskName].phase[phaseName] or 0) + count  | 
 | 177 | +                    end  | 
 | 178 | +                  end  | 
 | 179 | +                end  | 
 | 180 | +              end  | 
 | 181 | +
  | 
 | 182 | +              if s.controlledResources then  | 
 | 183 | +                for k, v in pairs(s.controlledResources) do  | 
 | 184 | +                  status.controlledResources[k] = v  | 
 | 185 | +                end  | 
 | 186 | +              end  | 
 | 187 | +                | 
 | 188 | +              if s.conditions then  | 
 | 189 | +                local clusterHasFailed = false  | 
 | 190 | +                for _, c in ipairs(s.conditions) do  | 
 | 191 | +                  if failedPhases[c.status] then  | 
 | 192 | +                    clusterHasFailed = true  | 
 | 193 | +                    hasFailed = true  | 
 | 194 | +                  end  | 
 | 195 | +                  if not clusterHasFailed then  | 
 | 196 | +                    local exist = latestTransition[c.status]  | 
 | 197 | +                    if exist == nil or c.lastTransitionTime > exist.lastTransitionTime then  | 
 | 198 | +                      latestTransition[c.status] = c  | 
 | 199 | +                    end  | 
 | 200 | +                  end  | 
 | 201 | +                end  | 
 | 202 | +                if clusterHasFailed and #failedConditions == 0 then  | 
 | 203 | +                  failedConditions = s.conditions  | 
 | 204 | +                end  | 
 | 205 | +              end  | 
 | 206 | +
  | 
 | 207 | +              if s.state ~= nil then  | 
 | 208 | +                local st = s.state  | 
 | 209 | +                if st.phase == "Completed" then  | 
 | 210 | +                  successfulClusters = successfulClusters + 1  | 
 | 211 | +                elseif st.phase == "Failed" or st.phase == "Aborted" then  | 
 | 212 | +                  table.insert(failedClusters, statusItems[i].clusterName)  | 
 | 213 | +                end  | 
 | 214 | +              end  | 
 | 215 | +            end  | 
 | 216 | +          end  | 
 | 217 | +
  | 
 | 218 | +          if #failedClusters > 0 then  | 
 | 219 | +            status.state.phase = "Failed"  | 
 | 220 | +            status.state.reason = "VolcanoJobFailed"  | 
 | 221 | +            status.state.message = "Job failed in clusters: " .. table.concat(failedClusters, ",")  | 
 | 222 | +            status.state.lastTransitionTime =  os.date("!%Y-%m-%dT%H:%M:%SZ")  | 
 | 223 | +          end  | 
 | 224 | +
  | 
 | 225 | +          if successfulClusters == #statusItems and successfulClusters > 0 then  | 
 | 226 | +            status.state.phase = "Completed"  | 
 | 227 | +            status.state.reason = "Completed"  | 
 | 228 | +            status.state.message = "Job completed successfully"  | 
 | 229 | +            status.state.lastTransitionTime = os.date("!%Y-%m-%dT%H:%M:%SZ")     | 
 | 230 | +          end  | 
 | 231 | +
  | 
 | 232 | +          if hasFailed then  | 
 | 233 | +            status.conditions = failedConditions  | 
 | 234 | +          else  | 
 | 235 | +            for _, v in pairs(latestTransition) do  | 
 | 236 | +              table.insert(status.conditions, v)  | 
 | 237 | +            end  | 
 | 238 | +          end  | 
 | 239 | +
  | 
 | 240 | +          desiredObj.status = omitEmpty(status) or {}  | 
 | 241 | +          return desiredObj  | 
 | 242 | +        end  | 
 | 243 | +    statusReflection:  | 
 | 244 | +      luaScript: >  | 
 | 245 | +        function ReflectStatus(observedObj)  | 
 | 246 | +          local status = {}  | 
 | 247 | +
  | 
 | 248 | +          if observedObj == nil or observedObj.status == nil then  | 
 | 249 | +            return status  | 
 | 250 | +          end  | 
 | 251 | +
  | 
 | 252 | +          local s = observedObj.status  | 
 | 253 | +          status.minAvailable = s.minAvailable  | 
 | 254 | +          status.pending = s.pending  | 
 | 255 | +          status.running = s.running  | 
 | 256 | +          status.succeeded = s.succeeded  | 
 | 257 | +          status.failed = s.failed  | 
 | 258 | +          status.terminating = s.terminating  | 
 | 259 | +          status.unknown = s.unknown  | 
 | 260 | +          status.version = s.version  | 
 | 261 | +          status.retryCount = s.retryCount  | 
 | 262 | +          status.runningDuration = s.runningDuration  | 
 | 263 | +
  | 
 | 264 | +          status.taskStatusCount = {}  | 
 | 265 | +          if s.taskStatusCount ~= nil then  | 
 | 266 | +            for k, v in pairs(s.taskStatusCount) do  | 
 | 267 | +              status.taskStatusCount[k] = v  | 
 | 268 | +            end  | 
 | 269 | +          end  | 
 | 270 | +
  | 
 | 271 | +          status.controlledResources = {}  | 
 | 272 | +          if s.controlledResources ~= nil then  | 
 | 273 | +            for k, v in pairs(s.controlledResources) do  | 
 | 274 | +              status.controlledResources[k] = v  | 
 | 275 | +            end  | 
 | 276 | +          end  | 
 | 277 | +
  | 
 | 278 | +          if s.state ~= nil then  | 
 | 279 | +            status.state = s.state  | 
 | 280 | +          end  | 
 | 281 | +            | 
 | 282 | +          status.conditions = {}  | 
 | 283 | +          if type(s.conditions) == "table" then  | 
 | 284 | +            for _, cond in ipairs(s.conditions) do  | 
 | 285 | +              table.insert(status.conditions, cond)  | 
 | 286 | +            end  | 
 | 287 | +          end  | 
 | 288 | +
  | 
 | 289 | +          return status  | 
 | 290 | +        end  | 
0 commit comments