in pkg/ingress/kube/ingress/controller.go [504:667]
func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wrapper *common.WrapperConfig) error {
if convertOptions == nil {
return fmt.Errorf("convertOptions is nil")
}
if wrapper == nil {
return fmt.Errorf("wrapperConfig is nil")
}
// Canary ingress will be processed in the end.
if wrapper.AnnotationsConfig.IsCanary() {
convertOptions.CanaryIngresses = append(convertOptions.CanaryIngresses, wrapper)
return nil
}
cfg := wrapper.Config
ingressV1, ok := cfg.Spec.(ingress.IngressSpec)
if !ok {
common.IncrementInvalidIngress(c.options.ClusterId, common.Unknown)
return fmt.Errorf("convert type is invalid in cluster %s", c.options.ClusterId)
}
if len(ingressV1.Rules) == 0 && ingressV1.Backend == nil {
common.IncrementInvalidIngress(c.options.ClusterId, common.EmptyRule)
return fmt.Errorf("invalid ingress rule %s:%s in cluster %s, either `defaultBackend` or `rules` must be specified", cfg.Namespace, cfg.Name, c.options.ClusterId)
}
if ingressV1.Backend != nil && (ingressV1.Backend.ServiceName != "" || ingressV1.Backend.Resource != nil) {
convertOptions.HasDefaultBackend = true
}
// In one ingress, we will limit the rule conflict.
// When the host, pathType, path of two rule are same, we think there is a conflict event.
definedRules := sets.New[string]()
var (
// But in across ingresses case, we will restrict this limit.
// When the {host, path, headers, method, params} of two rule in different ingress are same, we think there is a conflict event.
tempRuleKey []string
)
for _, rule := range ingressV1.Rules {
if rule.HTTP == nil || len(rule.HTTP.Paths) == 0 {
IngressLog.Warnf("invalid ingress rule %s:%s for host %q in cluster %s, no paths defined", cfg.Namespace, cfg.Name, rule.Host, c.options.ClusterId)
continue
}
wrapperVS, exist := convertOptions.VirtualServices[rule.Host]
if !exist {
wrapperVS = &common.WrapperVirtualService{
VirtualService: &networking.VirtualService{
Hosts: []string{rule.Host},
},
WrapperConfig: wrapper,
}
convertOptions.VirtualServices[rule.Host] = wrapperVS
}
// Record the latest app root for per host.
redirect := wrapper.AnnotationsConfig.Redirect
if redirect != nil && redirect.AppRoot != "" {
wrapperVS.AppRoot = redirect.AppRoot
}
wrapperHttpRoutes := make([]*common.WrapperHTTPRoute, 0, len(rule.HTTP.Paths))
for _, httpPath := range rule.HTTP.Paths {
wrapperHttpRoute := &common.WrapperHTTPRoute{
HTTPRoute: &networking.HTTPRoute{},
WrapperConfig: wrapper,
Host: rule.Host,
ClusterId: c.options.ClusterId,
}
var pathType common.PathType
originPath := httpPath.Path
if annotationsConfig := wrapper.AnnotationsConfig; annotationsConfig.NeedRegexMatch(originPath) {
if annotationsConfig.IsFullPathRegexMatch() {
pathType = common.FullPathRegex
} else {
pathType = common.PrefixRegex
}
} else {
switch *httpPath.PathType {
case ingress.PathTypeExact:
pathType = common.Exact
case ingress.PathTypePrefix:
pathType = common.Prefix
if httpPath.Path != "/" {
originPath = strings.TrimSuffix(httpPath.Path, "/")
}
}
}
wrapperHttpRoute.OriginPath = originPath
wrapperHttpRoute.OriginPathType = pathType
wrapperHttpRoute.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, wrapperVS)
wrapperHttpRoute.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, wrapperHttpRoute)
ingressRouteBuilder := convertOptions.IngressRouteCache.New(wrapperHttpRoute)
hostAndPath := wrapperHttpRoute.PathFormat()
key := createRuleKey(cfg.Annotations, hostAndPath)
wrapperHttpRoute.RuleKey = key
if WrapPreIngress, exist := convertOptions.Route2Ingress[key]; exist {
ingressRouteBuilder.PreIngress = WrapPreIngress.Config
ingressRouteBuilder.Event = common.DuplicatedRoute
}
tempRuleKey = append(tempRuleKey, key)
// Two duplicated rules in the same ingress.
if ingressRouteBuilder.Event == common.Normal {
pathFormat := wrapperHttpRoute.PathFormat()
if definedRules.Contains(pathFormat) {
ingressRouteBuilder.PreIngress = cfg
ingressRouteBuilder.Event = common.DuplicatedRoute
}
definedRules.Insert(pathFormat)
}
// backend service check
var event common.Event
destinationConfig := wrapper.AnnotationsConfig.Destination
wrapperHttpRoute.HTTPRoute.Route, event = c.backendToRouteDestination(&httpPath.Backend, cfg.Namespace, ingressRouteBuilder, destinationConfig)
if destinationConfig != nil {
wrapperHttpRoute.WeightTotal = int32(destinationConfig.WeightSum)
}
if ingressRouteBuilder.Event != common.Normal {
event = ingressRouteBuilder.Event
}
if event != common.Normal {
common.IncrementInvalidIngress(c.options.ClusterId, event)
ingressRouteBuilder.Event = event
} else {
wrapperHttpRoutes = append(wrapperHttpRoutes, wrapperHttpRoute)
}
convertOptions.IngressRouteCache.Add(ingressRouteBuilder)
}
for idx, item := range tempRuleKey {
if val, exist := convertOptions.Route2Ingress[item]; !exist || strings.Compare(val.RuleKey, tempRuleKey[idx]) != 0 {
convertOptions.Route2Ingress[item] = &common.WrapperConfigWithRuleKey{
Config: cfg,
RuleKey: tempRuleKey[idx],
}
}
}
old, f := convertOptions.HTTPRoutes[rule.Host]
if f {
old = append(old, wrapperHttpRoutes...)
convertOptions.HTTPRoutes[rule.Host] = old
} else {
convertOptions.HTTPRoutes[rule.Host] = wrapperHttpRoutes
}
// Sort, exact -> prefix -> regex
routes := convertOptions.HTTPRoutes[rule.Host]
IngressLog.Debugf("routes of host %s is %v", rule.Host, routes)
common.SortHTTPRoutes(routes)
}
return nil
}