in pkg/providers/apisix/translation/apisix_route.go [62:334]
func (t *translator) translateHTTPRouteV2(ctx *translation.TranslateContext, ar *configv2.ApisixRoute) error {
ruleNameMap := make(map[string]struct{})
for _, part := range ar.Spec.HTTP {
if _, ok := ruleNameMap[part.Name]; ok {
return errors.New("duplicated route rule name")
}
ruleNameMap[part.Name] = struct{}{}
var timeout *apisixv1.UpstreamTimeout
if part.Timeout != nil {
timeout = &apisixv1.UpstreamTimeout{
Connect: apisixv1.DefaultUpstreamTimeout,
Read: apisixv1.DefaultUpstreamTimeout,
Send: apisixv1.DefaultUpstreamTimeout,
}
if part.Timeout.Connect.Duration > 0 {
timeout.Connect = int(part.Timeout.Connect.Seconds())
}
if part.Timeout.Read.Duration > 0 {
timeout.Read = int(part.Timeout.Read.Seconds())
}
if part.Timeout.Send.Duration > 0 {
timeout.Send = int(part.Timeout.Send.Seconds())
}
}
pluginMap := make(apisixv1.Plugins)
// add route plugins
for _, plugin := range part.Plugins {
if !plugin.Enable {
continue
}
if plugin.Config != nil {
if plugin.SecretRef != "" {
sec, err := t.SecretLister.Secrets(ar.Namespace).Get(plugin.SecretRef)
if err != nil {
log.Errorw("The config secretRef is invalid",
zap.Any("plugin", plugin.Name),
zap.String("secretRef", plugin.SecretRef))
break
}
log.Debugw("Add new items, then override items with the same plugin key",
zap.Any("plugin", plugin.Name),
zap.String("secretRef", plugin.SecretRef))
for key, value := range sec.Data {
utils.InsertKeyInMap(key, string(value), plugin.Config)
}
}
pluginMap[plugin.Name] = plugin.Config
} else {
pluginMap[plugin.Name] = make(map[string]interface{})
}
}
// add Authentication plugins
if part.Authentication.Enable {
switch part.Authentication.Type {
case "keyAuth":
pluginMap["key-auth"] = part.Authentication.KeyAuth
case "basicAuth":
pluginMap["basic-auth"] = make(map[string]interface{})
case "wolfRBAC":
pluginMap["wolf-rbac"] = make(map[string]interface{})
case "jwtAuth":
pluginMap["jwt-auth"] = part.Authentication.JwtAuth
case "hmacAuth":
pluginMap["hmac-auth"] = make(map[string]interface{})
case "ldapAuth":
pluginMap["ldap-auth"] = part.Authentication.LDAPAuth
default:
pluginMap["basic-auth"] = make(map[string]interface{})
}
}
var (
exprs [][]apisixv1.StringOrSlice
err error
)
if part.Match.NginxVars != nil {
exprs, err = t.TranslateRouteMatchExprs(part.Match.NginxVars)
if err != nil {
log.Errorw("ApisixRoute with bad nginxVars",
zap.Error(err),
zap.Any("ApisixRoute", ar),
)
return err
}
}
if err := translation.ValidateRemoteAddrs(part.Match.RemoteAddrs); err != nil {
log.Errorw("ApisixRoute with invalid remote addrs",
zap.Error(err),
zap.Strings("remote_addrs", part.Match.RemoteAddrs),
zap.Any("ApisixRoute", ar),
)
return err
}
route := apisixv1.NewDefaultRoute()
route.Name = apisixv1.ComposeRouteName(ar.Namespace, ar.Name, part.Name)
route.ID = id.GenID(route.Name)
route.Priority = part.Priority
route.RemoteAddrs = part.Match.RemoteAddrs
route.Vars = exprs
route.Hosts = part.Match.Hosts
route.Uris = part.Match.Paths
route.Methods = part.Match.Methods
route.EnableWebsocket = part.Websocket
route.Plugins = pluginMap
route.Timeout = timeout
route.FilterFunc = part.Match.FilterFunc
if part.PluginConfigName != "" {
ns := ar.Namespace
if part.PluginConfigNamespace != "" {
ns = part.PluginConfigNamespace
}
route.PluginConfigId = id.GenID(apisixv1.ComposePluginConfigName(ns, part.PluginConfigName))
}
for k, v := range ar.ObjectMeta.Labels {
route.Metadata.Labels[k] = v
}
ctx.AddRoute(route)
// --- translate "Backends" ---
backends := part.Backends
if len(backends) > 0 {
// Use the first backend as the default backend in Route,
// others will be configured in traffic-split plugin.
backend := backends[0]
backends = backends[1:]
svcClusterIP, svcPort, err := t.GetServiceClusterIPAndPort(&backend, ar.Namespace)
if err != nil {
log.Errorw("failed to get service port in backend",
zap.Any("backend", backend),
zap.Any("apisix_route", ar),
zap.Error(err),
)
return err
}
upstreamName := apisixv1.ComposeUpstreamName(ar.Namespace, backend.ServiceName, backend.Subset, svcPort, backend.ResolveGranularity)
route.UpstreamId = id.GenID(upstreamName)
if len(backends) > 0 {
weight := translation.DefaultWeight
if backend.Weight != nil {
weight = *backend.Weight
}
plugin, err := t.translateTrafficSplitPlugin(ctx, ar.Namespace, weight, backends)
if err != nil {
log.Errorw("failed to translate traffic-split plugin",
zap.Error(err),
zap.Any("ApisixRoute", ar),
)
return err
}
route.Plugins["traffic-split"] = plugin
}
if !ctx.CheckUpstreamExist(upstreamName) {
ups, err := t.translateService(ar.Namespace, backend.ServiceName, backend.Subset, backend.ResolveGranularity, svcClusterIP, svcPort)
if err != nil {
return err
}
ctx.AddUpstream(ups)
}
}
if len(part.Backends) == 0 && len(part.Upstreams) > 0 {
// Only have Upstreams
upName := apisixv1.ComposeExternalUpstreamName(ar.Namespace, part.Upstreams[0].Name)
route.UpstreamId = id.GenID(upName)
}
// --- translate Upstreams ---
var ups []*apisixv1.Upstream
for i, au := range part.Upstreams {
up, err := t.translateExternalApisixUpstream(ar.Namespace, au.Name)
if err != nil {
log.Errorw(fmt.Sprintf("failed to translate ApisixUpstream at Upstream[%v]", i),
zap.Error(err),
zap.String("apisix_upstream", ar.Namespace+"/"+au.Name),
)
continue
}
if au.Weight != nil {
up.Labels["meta_weight"] = strconv.Itoa(*au.Weight)
} else {
up.Labels["meta_weight"] = strconv.Itoa(translation.DefaultWeight)
}
ups = append(ups, up)
}
if len(ups) == 0 {
continue
}
var wups []apisixv1.TrafficSplitConfigRuleWeightedUpstream
if len(part.Backends) == 0 {
if len(ups) > 1 {
for i, up := range ups {
weight, err := strconv.Atoi(up.Labels["meta_weight"])
if err != nil {
// shouldn't happen
log.Errorw(fmt.Sprintf("failed to parse translated upstream weight at %v", i),
zap.Error(err),
zap.String("meta_weight", up.Labels["meta_weight"]),
)
continue
}
if i == 0 {
// set as default
wups = append(wups, apisixv1.TrafficSplitConfigRuleWeightedUpstream{
Weight: weight,
})
} else {
wups = append(wups, apisixv1.TrafficSplitConfigRuleWeightedUpstream{
UpstreamID: ups[i].ID,
Weight: weight,
})
}
}
}
} else {
// Mixed backends and upstreams
if cfg, ok := route.Plugins["traffic-split"]; ok {
if tsCfg, ok := cfg.(*apisixv1.TrafficSplitConfig); ok {
wups = tsCfg.Rules[0].WeightedUpstreams
}
}
if len(wups) == 0 {
// append the default upstream in the route.
weight := translation.DefaultWeight
if part.Backends[0].Weight != nil {
weight = *part.Backends[0].Weight
}
wups = append(wups, apisixv1.TrafficSplitConfigRuleWeightedUpstream{
Weight: weight,
})
}
for i, up := range ups {
weight, err := strconv.Atoi(up.Labels["meta_weight"])
if err != nil {
// shouldn't happen
log.Errorw(fmt.Sprintf("failed to parse translated upstream weight at %v", i),
zap.Error(err),
zap.String("meta_weight", up.Labels["meta_weight"]),
)
continue
}
wups = append(wups, apisixv1.TrafficSplitConfigRuleWeightedUpstream{
UpstreamID: ups[i].ID,
Weight: weight,
})
}
}
if len(wups) > 0 {
route.Plugins["traffic-split"] = &apisixv1.TrafficSplitConfig{
Rules: []apisixv1.TrafficSplitConfigRule{
{
WeightedUpstreams: wups,
},
},
}
}
for _, up := range ups {
ctx.AddUpstream(up)
}
}
return nil
}