func()

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
}