func (lbc *loadBalancerController) getUpstreamServers(ngxCfg config.Configuration, data []interface{}) ([]*nginx.Upstream, []*nginx.Server) { upstreams := lbc.createUpstreams(ngxCfg, data) upstreams[defUpstreamName] = lbc.getDefaultUpstream() servers := lbc.createServers(data) if _, ok := servers[defServerName]; !ok { // default server - no servername. // there is no rule with default backend servers[defServerName] = &nginx.Server{ Name: defServerName, Locations: []*nginx.Location{{ Path: rootLocation, IsDefBackend: true, Upstream: *lbc.getDefaultUpstream(), }, }, } } for _, ingIf := range data { ing := ingIf.(*extensions.Ingress) for _, rule := range ing.Spec.Rules { if rule.IngressRuleValue.HTTP == nil { continue } nginxAuth, err := auth.ParseAnnotations(lbc.client, ing, auth.DefAuthDirectory) glog.V(3).Infof("nginx auth %v", nginxAuth) if err != nil { glog.V(3).Infof("error reading authentication in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } rl, err := ratelimit.ParseAnnotations(ing) glog.V(3).Infof("nginx rate limit %v", rl) if err != nil { glog.V(3).Infof("error reading rate limit annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } secUpstream, err := secureupstream.ParseAnnotations(ing) if err != nil { glog.V(3).Infof("error reading secure upstream in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } locRew, err := rewrite.ParseAnnotations(ngxCfg, ing) if err != nil { glog.V(3).Infof("error parsing rewrite annotations for Ingress rule %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } wl, err := ipwhitelist.ParseAnnotations(ngxCfg.WhitelistSourceRange, ing) glog.V(3).Infof("nginx white list %v", wl) if err != nil { glog.V(3).Infof("error reading white list annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } host := rule.Host if host == "" { host = defServerName } server := servers[host] if server == nil { server = servers["_"] } for _, path := range rule.HTTP.Paths { upsName := fmt.Sprintf("%v-%v-%v", ing.GetNamespace(), path.Backend.ServiceName, path.Backend.ServicePort.String()) ups := upstreams[upsName] nginxPath := path.Path // if there's no path defined we assume / if nginxPath == "" { lbc.recorder.Eventf(ing, api.EventTypeWarning, "MAPPING", "Ingress rule '%v/%v' contains no path definition. Assuming /", ing.GetNamespace(), ing.GetName()) nginxPath = rootLocation } // Validate that there is no another previuous // rule for the same host and path. addLoc := true for _, loc := range server.Locations { if loc.Path == rootLocation && nginxPath == rootLocation && loc.IsDefBackend { loc.Upstream = *ups loc.Auth = *nginxAuth loc.RateLimit = *rl loc.Redirect = *locRew loc.SecureUpstream = secUpstream loc.Whitelist = *wl addLoc = false continue } if loc.Path == nginxPath { lbc.recorder.Eventf(ing, api.EventTypeWarning, "MAPPING", "Path '%v' already defined in another Ingress rule", nginxPath) addLoc = false break } } if addLoc { server.Locations = append(server.Locations, &nginx.Location{ Path: nginxPath, Upstream: *ups, Auth: *nginxAuth, RateLimit: *rl, Redirect: *locRew, SecureUpstream: secUpstream, Whitelist: *wl, }) } } } } // TODO: find a way to make this more readable // The structs must be ordered to always generate the same file // if the content does not change. aUpstreams := make([]*nginx.Upstream, 0, len(upstreams)) for _, value := range upstreams { if len(value.Backends) == 0 { glog.Warningf("upstream %v does not have any active endpoints. Using default backend", value.Name) value.Backends = append(value.Backends, nginx.NewDefaultServer()) } sort.Sort(nginx.UpstreamServerByAddrPort(value.Backends)) aUpstreams = append(aUpstreams, value) } sort.Sort(nginx.UpstreamByNameServers(aUpstreams)) aServers := make([]*nginx.Server, 0, len(servers)) for _, value := range servers { sort.Sort(nginx.LocationByPath(value.Locations)) aServers = append(aServers, value) } sort.Sort(nginx.ServerByName(aServers)) return aUpstreams, aServers }
// getUpstreamServers returns a list of Upstream and Server to be used in NGINX. // An upstream can be used in multiple servers if the namespace, service name and port are the same func (lbc *loadBalancerController) getUpstreamServers(ngxCfg config.Configuration, data []interface{}) ([]*ingress.Upstream, []*ingress.Server) { upstreams := lbc.createUpstreams(ngxCfg, data) servers := lbc.createServers(data, upstreams) for _, ingIf := range data { ing := ingIf.(*extensions.Ingress) nginxAuth, err := auth.ParseAnnotations(lbc.client, ing, auth.DefAuthDirectory) glog.V(3).Infof("nginx auth %v", nginxAuth) if err != nil { glog.V(3).Infof("error reading authentication in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } rl, err := ratelimit.ParseAnnotations(ing) glog.V(3).Infof("nginx rate limit %v", rl) if err != nil { glog.V(3).Infof("error reading rate limit annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } secUpstream, err := secureupstream.ParseAnnotations(ing) if err != nil { glog.V(3).Infof("error reading secure upstream in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } locRew, err := rewrite.ParseAnnotations(ngxCfg, ing) if err != nil { glog.V(3).Infof("error parsing rewrite annotations for Ingress rule %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } wl, err := ipwhitelist.ParseAnnotations(ngxCfg.WhitelistSourceRange, ing) glog.V(3).Infof("nginx white list %v", wl) if err != nil { glog.V(3).Infof("error reading white list annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } eCORS, err := cors.ParseAnnotations(ing) if err != nil { glog.V(3).Infof("error reading CORS annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } ra, err := authreq.ParseAnnotations(ing) glog.V(3).Infof("nginx auth request %v", ra) if err != nil { glog.V(3).Infof("error reading auth request annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } for _, rule := range ing.Spec.Rules { host := rule.Host if host == "" { host = defServerName } server := servers[host] if server == nil { server = servers[defServerName] } if rule.HTTP == nil && host != defServerName { // no rules, host is not default server. // check if Ingress rules contains Backend and replace default backend defBackend := fmt.Sprintf("default-backend-%v-%v-%v", ing.GetNamespace(), ing.Spec.Backend.ServiceName, ing.Spec.Backend.ServicePort.String()) ups := upstreams[defBackend] for _, loc := range server.Locations { loc.Upstream = *ups } continue } for _, path := range rule.HTTP.Paths { upsName := fmt.Sprintf("%v-%v-%v", ing.GetNamespace(), path.Backend.ServiceName, path.Backend.ServicePort.String()) ups := upstreams[upsName] // we need to check if the upstream contains the default backend if isDefaultUpstream(ups) && ing.Spec.Backend != nil { defBackend := fmt.Sprintf("default-backend-%v-%v-%v", ing.GetNamespace(), ing.Spec.Backend.ServiceName, ing.Spec.Backend.ServicePort.String()) if defUps, ok := upstreams[defBackend]; ok { ups = defUps } } nginxPath := path.Path // if there's no path defined we assume / // in NGINX / == /* if nginxPath == "" { lbc.recorder.Eventf(ing, api.EventTypeWarning, "MAPPING", "Ingress rule '%v/%v' contains no path definition. Assuming /", ing.GetNamespace(), ing.GetName()) nginxPath = rootLocation } // Validate that there is no previous rule for the same host and path. addLoc := true for _, loc := range server.Locations { if loc.Path == rootLocation && nginxPath == rootLocation && loc.IsDefBackend { loc.Upstream = *ups loc.Auth = *nginxAuth loc.RateLimit = *rl loc.Redirect = *locRew loc.SecureUpstream = secUpstream loc.Whitelist = *wl loc.IsDefBackend = false loc.Upstream = *ups loc.EnableCORS = eCORS loc.ExternalAuthURL = ra addLoc = false continue } if loc.Path == nginxPath { lbc.recorder.Eventf(ing, api.EventTypeWarning, "MAPPING", "Path '%v' already defined in another Ingress rule", nginxPath) addLoc = false break } } if addLoc { server.Locations = append(server.Locations, &ingress.Location{ Path: nginxPath, Upstream: *ups, Auth: *nginxAuth, RateLimit: *rl, Redirect: *locRew, SecureUpstream: secUpstream, Whitelist: *wl, EnableCORS: eCORS, ExternalAuthURL: ra, }) } } } } // TODO: find a way to make this more readable // The structs must be ordered to always generate the same file // if the content does not change. aUpstreams := make([]*ingress.Upstream, 0, len(upstreams)) for _, value := range upstreams { if len(value.Backends) == 0 { glog.Warningf("upstream %v does not have any active endpoints. Using default backend", value.Name) value.Backends = append(value.Backends, nginx.NewDefaultServer()) } sort.Sort(ingress.UpstreamServerByAddrPort(value.Backends)) aUpstreams = append(aUpstreams, value) } sort.Sort(ingress.UpstreamByNameServers(aUpstreams)) aServers := make([]*ingress.Server, 0, len(servers)) for _, value := range servers { sort.Sort(ingress.LocationByPath(value.Locations)) aServers = append(aServers, value) } sort.Sort(ingress.ServerByName(aServers)) return aUpstreams, aServers }