spec = obj.data.spec if obj.canaryWeight == -1 then obj.canaryWeight = 100 obj.stableWeight = 0 end function GetHost(destination) local host = destination.destination.host dot_position = string.find(host, ".", 1, true) if (dot_position) then host = string.sub(host, 1, dot_position - 1) end return host end -- find routes of VirtualService with stableService function GetRulesToPatch(spec, stableService, protocol) local matchedRoutes = {} if (spec[protocol] ~= nil) then for _, rule in ipairs(spec[protocol]) do -- skip routes contain matches if (rule.match == nil) then for _, route in ipairs(rule.route) do if GetHost(route) == stableService then table.insert(matchedRoutes, rule) end end end end end return matchedRoutes end function CalculateWeight(route, stableWeight, n) local weight if (route.weight) then weight = math.floor(route.weight * stableWeight / 100) else weight = math.floor(stableWeight / n) end return weight end -- generate routes with matches, insert a rule before other rules, only support http headers, cookies etc. function GenerateRoutesWithMatches(spec, matches, stableService, canaryService) for _, match in ipairs(matches) do local route = {} route["match"] = {} for key, value in pairs(match) do local vsMatch = {} vsMatch[key] = {} for _, rule in ipairs(value) do if rule["type"] == "RegularExpression" then matchType = "regex" elseif rule["type"] == "Exact" then matchType = "exact" elseif rule["type"] == "Prefix" then matchType = "prefix" end if key == "headers" then vsMatch[key][rule["name"]] = {} vsMatch[key][rule["name"]][matchType] = rule.value else vsMatch[key][matchType] = rule.value end end table.insert(route["match"], vsMatch) end route.route = { { destination = {} } } -- stableService == canaryService indicates DestinationRule exists and subset is set to be canary by default if stableService == canaryService then route.route[1].destination.host = stableService route.route[1].destination.subset = "canary" else route.route[1].destination.host = canaryService end table.insert(spec.http, 1, route) end end -- generate routes without matches, change every rule whose host is stableService function GenerateRoutes(spec, stableService, canaryService, stableWeight, canaryWeight, protocol) local matchedRules = GetRulesToPatch(spec, stableService, protocol) for _, rule in ipairs(matchedRules) do local canary if stableService ~= canaryService then canary = { destination = { host = canaryService, }, weight = canaryWeight, } else canary = { destination = { host = stableService, subset = "canary", }, weight = canaryWeight, } end -- incase there are multiple versions traffic already, do a for-loop for _, route in ipairs(rule.route) do -- update stable service weight route.weight = CalculateWeight(route, stableWeight, #rule.route) end table.insert(rule.route, canary) end end if (obj.matches) then GenerateRoutesWithMatches(spec, obj.matches, obj.stableService, obj.canaryService) else GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "http") GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "tcp") GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "tls") end return obj.data