func (p *HostAdmitter) displacedRoutes(newRoute *routeapi.Route) ([]*routeapi.Route, error) { displaced := []*routeapi.Route{} // See if any existing routes block our host, or if we displace their host for i, route := range p.claimedHosts[newRoute.Spec.Host] { if route.Namespace == newRoute.Namespace { // Never displace a route in our namespace continue } if routeapi.RouteLessThan(route, newRoute) { return nil, fmt.Errorf("route %s/%s has host %s", route.Namespace, route.Name, route.Spec.Host) } displaced = append(displaced, p.claimedHosts[newRoute.Spec.Host][i]) } wildcardKey := routeapi.GetDomainForHost(newRoute.Spec.Host) // See if any existing wildcard routes block our domain, or if we displace them for i, route := range p.claimedWildcards[wildcardKey] { if route.Namespace == newRoute.Namespace { // Never displace a route in our namespace continue } if routeapi.RouteLessThan(route, newRoute) { return nil, fmt.Errorf("wildcard route %s/%s has host *.%s, blocking %s", route.Namespace, route.Name, wildcardKey, newRoute.Spec.Host) } displaced = append(displaced, p.claimedWildcards[wildcardKey][i]) } // If this is a wildcard route, see if any specific hosts block our wildcardSpec, or if we displace them if newRoute.Spec.WildcardPolicy == routeapi.WildcardPolicySubdomain { for i, route := range p.blockedWildcards[wildcardKey] { if route.Namespace == newRoute.Namespace { // Never displace a route in our namespace continue } if routeapi.RouteLessThan(route, newRoute) { return nil, fmt.Errorf("route %s/%s has host %s, blocking *.%s", route.Namespace, route.Name, route.Spec.Host, wildcardKey) } displaced = append(displaced, p.blockedWildcards[wildcardKey][i]) } } return displaced, nil }
func (srm RouteMap) InsertRoute(key string, route *routeapi.Route) { // To replace any existing route[s], first we remove all old entries. srm.RemoveRoute(key, route) m := srm[key] for idx := range m { if routeapi.RouteLessThan(route, m[idx]) { m = append(m, &routeapi.Route{}) // From: https://github.com/golang/go/wiki/SliceTricks copy(m[idx+1:], m[idx:]) m[idx] = route srm[key] = m // Ensure we return from here as we change the iterator. return } } // Newest route or empty slice, add to the end. srm[key] = append(m, route) }
func (p *HostAdmitter) displacedRoutes(newRoute *routeapi.Route) ([]*routeapi.Route, error, string) { displaced := []*routeapi.Route{} // See if any existing routes block our host, or if we displace their host for i, route := range p.claimedHosts[newRoute.Spec.Host] { if route.Namespace == newRoute.Namespace { if route.Name == newRoute.Name { continue } // Check for wildcard routes. Never displace a // non-wildcard route in our namespace if we are a // wildcard route. // E.g. *.acme.test can co-exist with a // route for www2.acme.test if newRoute.Spec.WildcardPolicy == routeapi.WildcardPolicySubdomain { continue } // New route is _NOT_ a wildcard - we need to check // if the paths are same and if it is older before // we can displace another route in our namespace. // The path check below allows non-wildcard routes // with different paths to coexist with the other // non-wildcard routes in this namespace. // E.g. www.acme.org/p1 can coexist with other // non-wildcard routes www.acme.org/p1/p2 or // www.acme.org/p2 or www.acme.org/p2/p3 // but ... // not with www.acme.org/p1 if route.Spec.Path != newRoute.Spec.Path { continue } } if routeapi.RouteLessThan(route, newRoute) { return nil, fmt.Errorf("route %s/%s has host %s", route.Namespace, route.Name, route.Spec.Host), route.Namespace } displaced = append(displaced, p.claimedHosts[newRoute.Spec.Host][i]) } wildcardKey := routeapi.GetDomainForHost(newRoute.Spec.Host) // See if any existing wildcard routes block our domain, or if we displace them for i, route := range p.claimedWildcards[wildcardKey] { if route.Namespace == newRoute.Namespace { if route.Name == newRoute.Name { continue } // Check for non-wildcard route. Never displace a // wildcard route in our namespace if we are not a // wildcard route. // E.g. www1.foo.test can co-exist with a // wildcard route for *.foo.test if newRoute.Spec.WildcardPolicy != routeapi.WildcardPolicySubdomain { continue } // New route is a wildcard - we need to check if the // paths are same and if it is older before we can // displace another route in our namespace. // The path check below allows wildcard routes with // different paths to coexist with the other wildcard // routes in this namespace. // E.g. *.bar.org/p1 can coexist with other wildcard // wildcard routes *.bar.org/p1/p2 or // *.bar.org/p2 or *.bar.org/p2/p3 // but ... // not with *.bar.org/p1 if route.Spec.Path != newRoute.Spec.Path { continue } } if routeapi.RouteLessThan(route, newRoute) { return nil, fmt.Errorf("wildcard route %s/%s has host *.%s, blocking %s", route.Namespace, route.Name, wildcardKey, newRoute.Spec.Host), route.Namespace } displaced = append(displaced, p.claimedWildcards[wildcardKey][i]) } // If this is a wildcard route, see if any specific hosts block our wildcardSpec, or if we displace them if newRoute.Spec.WildcardPolicy == routeapi.WildcardPolicySubdomain { for i, route := range p.blockedWildcards[wildcardKey] { if route.Namespace == newRoute.Namespace { // Never displace a route in our namespace continue } if routeapi.RouteLessThan(route, newRoute) { return nil, fmt.Errorf("route %s/%s has host %s, blocking *.%s", route.Namespace, route.Name, route.Spec.Host, wildcardKey), route.Namespace } displaced = append(displaced, p.blockedWildcards[wildcardKey][i]) } } return displaced, nil, newRoute.Namespace }
// HandleRoute processes watch events on the Route resource. // TODO: this function can probably be collapsed with the router itself, as a function that // determines which component needs to be recalculated (which template) and then does so // on demand. func (p *UniqueHost) HandleRoute(eventType watch.EventType, route *routeapi.Route) error { if p.allowedNamespaces != nil && !p.allowedNamespaces.Has(route.Namespace) { return nil } routeName := routeNameKey(route) host := p.hostForRoute(route) if len(host) == 0 { glog.V(4).Infof("Route %s has no host value", routeName) p.recorder.RecordRouteRejection(route, "NoHostValue", "no host value was defined for the route") return nil } route.Spec.Host = host // ensure hosts can only be claimed by one namespace at a time // TODO: this could be abstracted above this layer? if old, ok := p.hostToRoute[host]; ok { oldest := old[0] // multiple paths can be added from the namespace of the oldest route if oldest.Namespace == route.Namespace { added := false for i := range old { if old[i].Spec.Path == route.Spec.Path { if routeapi.RouteLessThan(old[i], route) { glog.V(4).Infof("Route %s cannot take %s from %s", routeName, host, routeNameKey(oldest)) err := fmt.Errorf("route %s already exposes %s and is older", oldest.Name, host) p.recorder.RecordRouteRejection(route, "HostAlreadyClaimed", err.Error()) return err } added = true if old[i].Namespace == route.Namespace && old[i].Name == route.Name { old[i] = route break } glog.V(4).Infof("route %s will replace path %s from %s because it is older", routeName, route.Spec.Path, old[i].Name) p.recorder.RecordRouteRejection(old[i], "HostAlreadyClaimed", fmt.Sprintf("replaced by older route %s", route.Name)) p.plugin.HandleRoute(watch.Deleted, old[i]) old[i] = route } } if !added { // Clean out any old form of this route next := []*routeapi.Route{} for i := range old { if routeNameKey(old[i]) != routeNameKey(route) { next = append(next, old[i]) } } old = next // We need to reset the oldest in case we removed it, but if it was the only // item, we'll just use ourselves since we'll become the oldest, and for // the append below, it doesn't matter if len(next) > 0 { oldest = old[0] } else { oldest = route } if routeapi.RouteLessThan(route, oldest) { p.hostToRoute[host] = append([]*routeapi.Route{route}, old...) } else { p.hostToRoute[host] = append(old, route) } } } else { if routeapi.RouteLessThan(oldest, route) { glog.V(4).Infof("Route %s cannot take %s from %s", routeName, host, routeNameKey(oldest)) err := fmt.Errorf("a route in another namespace holds %s and is older than %s", host, route.Name) p.recorder.RecordRouteRejection(route, "HostAlreadyClaimed", err.Error()) return err } glog.V(4).Infof("Route %s is reclaiming %s from namespace %s", routeName, host, oldest.Namespace) for i := range old { p.recorder.RecordRouteRejection(old[i], "HostAlreadyClaimed", fmt.Sprintf("namespace %s owns hostname %s", oldest.Namespace, host)) p.plugin.HandleRoute(watch.Deleted, old[i]) } p.hostToRoute[host] = []*routeapi.Route{route} } } else { glog.V(4).Infof("Route %s claims %s", routeName, host) p.hostToRoute[host] = []*routeapi.Route{route} } switch eventType { case watch.Added, watch.Modified: if old, ok := p.routeToHost[routeName]; ok { if old != host { glog.V(4).Infof("Route %s changed from serving host %s to host %s", routeName, old, host) delete(p.hostToRoute, old) } } p.routeToHost[routeName] = host return p.plugin.HandleRoute(eventType, route) case watch.Deleted: glog.V(4).Infof("Deleting routes for %s", routeName) if old, ok := p.hostToRoute[host]; ok { switch len(old) { case 1, 0: delete(p.hostToRoute, host) default: next := []*routeapi.Route{} for i := range old { if old[i].Name != route.Name { next = append(next, old[i]) } } if len(next) > 0 { p.hostToRoute[host] = next } else { delete(p.hostToRoute, host) } } } delete(p.routeToHost, routeName) return p.plugin.HandleRoute(eventType, route) } return nil }
func (r routeAge) Less(i, j int) bool { return routeapi.RouteLessThan(&r[i], &r[j]) }