// getBackendServers returns a list of Upstream and Server to be used by the backend // An upstream can be used in multiple servers if the namespace, service name and port are the same func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress.Server) { ings := ic.ingLister.Store.List() sort.Sort(ingressByRevision(ings)) upstreams := ic.createUpstreams(ings) servers := ic.createServers(ings, upstreams) upsDefaults := ic.cfg.Backend.BackendDefaults() for _, ingIf := range ings { ing := ingIf.(*extensions.Ingress) nginxAuth, err := auth.ParseAnnotations(ing, auth.DefAuthDirectory, ic.getSecret) glog.V(5).Infof("auth annotation: %v", nginxAuth) if err != nil { glog.V(5).Infof("error reading authentication in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } rl, err := ratelimit.ParseAnnotations(ing) glog.V(5).Infof("rate limit annotation: %v", rl) if err != nil { glog.V(5).Infof("error reading rate limit annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } locRew, err := rewrite.ParseAnnotations(upsDefaults, ing) if err != nil { glog.V(5).Infof("error parsing rewrite annotations for Ingress rule %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } wl, err := ipwhitelist.ParseAnnotations(upsDefaults, ing) glog.V(5).Infof("white list annotation: %v", wl) if err != nil { glog.V(5).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(5).Infof("error reading CORS annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } ra, err := authreq.ParseAnnotations(ing) glog.V(5).Infof("auth request annotation: %v", ra) if err != nil { glog.V(5).Infof("error reading auth request annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } prx := proxy.ParseAnnotations(upsDefaults, ing) glog.V(5).Infof("proxy timeouts annotation: %v", prx) certAuth, err := authtls.ParseAnnotations(ing, ic.getAuthCertificate) glog.V(5).Infof("auth request annotation: %v", certAuth) if err != nil { glog.V(5).Infof("error reading certificate auth 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] } // use default upstream defBackend := upstreams[defUpstreamName] // we need to check if the spec contains the default backend if ing.Spec.Backend != nil { glog.V(3).Infof("ingress rule %v/%v defines a default Backend %v/%v", ing.Namespace, ing.Name, ing.Spec.Backend.ServiceName, ing.Spec.Backend.ServicePort.String()) name := fmt.Sprintf("%v-%v-%v", ing.GetNamespace(), ing.Spec.Backend.ServiceName, ing.Spec.Backend.ServicePort.String()) if defUps, ok := upstreams[name]; ok { defBackend = defUps } } if rule.HTTP == nil && len(ing.Spec.TLS) == 0 && host != defServerName { glog.V(3).Infof("ingress rule %v/%v does not contains HTTP or TLS rules. using default backend", ing.Namespace, ing.Name) server.Locations[0].Backend = defBackend.Name 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] // if there's no path defined we assume / nginxPath := rootLocation if path.Path != "" { nginxPath = path.Path } addLoc := true for _, loc := range server.Locations { if loc.Path == nginxPath { addLoc = false if !loc.IsDefBackend { glog.V(3).Infof("avoiding replacement of ingress rule %v/%v location %v upstream %v (%v)", ing.Namespace, ing.Name, loc.Path, ups.Name, loc.Backend) break } glog.V(3).Infof("replacing ingress rule %v/%v location %v upstream %v (%v)", ing.Namespace, ing.Name, loc.Path, ups.Name, loc.Backend) loc.Backend = ups.Name loc.IsDefBackend = false loc.BasicDigestAuth = *nginxAuth loc.RateLimit = *rl loc.Redirect = *locRew loc.Whitelist = *wl loc.Backend = ups.Name loc.EnableCORS = eCORS loc.ExternalAuth = ra loc.Proxy = *prx loc.CertificateAuth = *certAuth break } } // is a new location if addLoc { glog.V(3).Infof("adding location %v in ingress rule %v/%v upstream %v", nginxPath, ing.Namespace, ing.Name, ups.Name) server.Locations = append(server.Locations, &ingress.Location{ Path: nginxPath, Backend: ups.Name, IsDefBackend: false, BasicDigestAuth: *nginxAuth, RateLimit: *rl, Redirect: *locRew, Whitelist: *wl, EnableCORS: eCORS, ExternalAuth: ra, Proxy: *prx, CertificateAuth: *certAuth, }) } } } } // 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.Backend, 0, len(upstreams)) for _, value := range upstreams { if len(value.Endpoints) == 0 { glog.V(3).Infof("upstream %v does not have any active endpoints. Using default backend", value.Name) value.Endpoints = append(value.Endpoints, newDefaultServer()) } sort.Sort(ingress.EndpointByAddrPort(value.Endpoints)) aUpstreams = append(aUpstreams, value) } sort.Sort(ingress.BackendByNameServers(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 }
func (ic *GenericController) getStreamServices(data map[string]string, proto api.Protocol) []*ingress.Location { var svcs []*ingress.Location // k -> port to expose // v -> <namespace>/<service name>:<port from service to be used> for k, v := range data { port, err := strconv.Atoi(k) if err != nil { glog.Warningf("%v is not valid as a TCP port", k) continue } // this ports used by the backend if local_strings.StringInSlice(k, reservedPorts) { glog.Warningf("port %v cannot be used for TCP or UDP services. It is reserved for the Ingress controller", k) continue } nsSvcPort := strings.Split(v, ":") if len(nsSvcPort) != 2 { glog.Warningf("invalid format (namespace/name:port) '%v'", k) continue } nsName := nsSvcPort[0] svcPort := nsSvcPort[1] svcNs, svcName, err := k8s.ParseNameNS(nsName) if err != nil { glog.Warningf("%v", err) continue } svcObj, svcExists, err := ic.svcLister.Indexer.GetByKey(nsName) if err != nil { glog.Warningf("error getting service %v: %v", nsName, err) continue } if !svcExists { glog.Warningf("service %v was not found", nsName) continue } svc := svcObj.(*api.Service) var endps []ingress.Endpoint targetPort, err := strconv.Atoi(svcPort) if err != nil { for _, sp := range svc.Spec.Ports { if sp.Name == svcPort { endps = ic.getEndpoints(svc, sp.TargetPort, proto, &healthcheck.Upstream{}) break } } } else { // we need to use the TargetPort (where the endpoints are running) for _, sp := range svc.Spec.Ports { if sp.Port == int32(targetPort) { endps = ic.getEndpoints(svc, sp.TargetPort, proto, &healthcheck.Upstream{}) break } } } sort.Sort(ingress.EndpointByAddrPort(endps)) // tcp upstreams cannot contain empty upstreams and there is no // default backend equivalent for TCP if len(endps) == 0 { glog.Warningf("service %v/%v does not have any active endpoints", svcNs, svcName) continue } svcs = append(svcs, &ingress.Location{ Path: k, Backend: fmt.Sprintf("%v-%v-%v", svcNs, svcName, port), }) } return svcs }