func (ic *GenericController) createServers(data []interface{}, upstreams map[string]*ingress.Backend) map[string]*ingress.Server { servers := make(map[string]*ingress.Server) ngxProxy := *proxy.ParseAnnotations(ic.cfg.Backend.BackendDefaults(), nil) upsDefaults := ic.cfg.Backend.BackendDefaults() // default server servers[defServerName] = &ingress.Server{ Hostname: defServerName, Locations: []*ingress.Location{ { Path: rootLocation, IsDefBackend: true, Backend: ic.getDefaultUpstream().Name, Proxy: ngxProxy, }, }} // initialize all the servers for _, ingIf := range data { ing := ingIf.(*extensions.Ingress) // check if ssl passthrough is configured sslpt, err := sslpassthrough.ParseAnnotations(upsDefaults, ing) if err != nil { glog.V(5).Infof("error reading ssl passthrough annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err) } for _, rule := range ing.Spec.Rules { host := rule.Host if host == "" { host = defServerName } if _, ok := servers[host]; ok { // server already configured continue } servers[host] = &ingress.Server{ Hostname: host, Locations: []*ingress.Location{ { Path: rootLocation, IsDefBackend: true, Backend: ic.getDefaultUpstream().Name, Proxy: ngxProxy, }, }, SSLPassthrough: sslpt} } } // configure default location and SSL for _, ingIf := range data { ing := ingIf.(*extensions.Ingress) for _, rule := range ing.Spec.Rules { host := rule.Host if host == "" { host = defServerName } // only add a certificate if the server does not have one previously configured // TODO: TLS without secret? if len(ing.Spec.TLS) > 0 && servers[host].SSLCertificate == "" && ing.Spec.TLS[0].SecretName != "" { key := fmt.Sprintf("%v/%v", ing.Namespace, ing.Spec.TLS[0].SecretName) bc, exists := ic.sslCertTracker.Get(key) if exists { cert := bc.(*ingress.SSLCert) if isHostValid(host, cert) { servers[host].SSLCertificate = cert.PemFileName servers[host].SSLPemChecksum = cert.PemSHA } } else { glog.Warningf("secret %v does not exists", key) } } if ing.Spec.Backend != nil { defUpstream := fmt.Sprintf("%v-%v-%v", ing.GetNamespace(), ing.Spec.Backend.ServiceName, ing.Spec.Backend.ServicePort.String()) if backendUpstream, ok := upstreams[defUpstream]; ok { if host == "" || host == defServerName { ic.recorder.Eventf(ing, api.EventTypeWarning, "MAPPING", "error: rules with Spec.Backend are allowed only with hostnames") continue } servers[host].Locations[0].Backend = backendUpstream.Name } } } } return servers }
// 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 }