// addRoute admits routes based on subdomain ownership - returns errors if the route is not admitted. func (p *HostAdmitter) addRoute(route *routeapi.Route) error { // Find displaced routes (or error if an existing route displaces us) displacedRoutes, err := p.displacedRoutes(route) if err != nil { msg := fmt.Sprintf("a route in another namespace holds host %s", route.Spec.Host) p.recorder.RecordRouteRejection(route, "HostAlreadyClaimed", msg) return fmt.Errorf("%s (%s)", msg, err) } // Remove displaced routes for _, displacedRoute := range displacedRoutes { wildcardKey := routeapi.GetDomainForHost(displacedRoute.Spec.Host) p.claimedHosts.RemoveRoute(displacedRoute.Spec.Host, displacedRoute) p.blockedWildcards.RemoveRoute(wildcardKey, displacedRoute) p.claimedWildcards.RemoveRoute(wildcardKey, displacedRoute) msg := fmt.Sprintf("a route in another namespace holds host %s", displacedRoute.Spec.Host) p.recorder.RecordRouteRejection(displacedRoute, "HostAlreadyClaimed", msg) p.plugin.HandleRoute(watch.Deleted, displacedRoute) } if len(route.Spec.WildcardPolicy) == 0 { route.Spec.WildcardPolicy = routeapi.WildcardPolicyNone } // Add the new route wildcardKey := routeapi.GetDomainForHost(route.Spec.Host) switch route.Spec.WildcardPolicy { case routeapi.WildcardPolicyNone: // claim the host, block wildcards that would conflict with this host p.claimedHosts.InsertRoute(route.Spec.Host, route) p.blockedWildcards.InsertRoute(wildcardKey, route) // ensure the route doesn't exist as a claimed wildcard (in case it previously was) p.claimedWildcards.RemoveRoute(wildcardKey, route) case routeapi.WildcardPolicySubdomain: // claim the wildcard p.claimedWildcards.InsertRoute(wildcardKey, route) // ensure the route doesn't exist as a claimed host or blocked wildcard p.claimedHosts.RemoveRoute(route.Spec.Host, route) p.blockedWildcards.RemoveRoute(wildcardKey, route) default: p.claimedHosts.RemoveRoute(route.Spec.Host, route) p.claimedWildcards.RemoveRoute(wildcardKey, route) p.blockedWildcards.RemoveRoute(wildcardKey, route) err := fmt.Errorf("unsupported wildcard policy %s", route.Spec.WildcardPolicy) p.recorder.RecordRouteRejection(route, "RouteNotAdmitted", err.Error()) return err } return nil }
// HandleRoute processes watch events on the Route resource. func (p *HostAdmitter) HandleRoute(eventType watch.EventType, route *routeapi.Route) error { if err := p.admitter(route); err != nil { glog.Errorf("Route %s not admitted: %s", routeNameKey(route), err.Error()) p.recorder.RecordRouteRejection(route, "RouteNotAdmitted", err.Error()) return err } if p.restrictOwnership && len(route.Spec.Host) > 0 { switch eventType { case watch.Added, watch.Modified: if err := p.addRoute(route); err != nil { glog.Errorf("Route %s not admitted: %s", routeNameKey(route), err.Error()) return err } case watch.Deleted: p.claimedHosts.RemoveRoute(route.Spec.Host, route) wildcardKey := routeapi.GetDomainForHost(route.Spec.Host) p.claimedWildcards.RemoveRoute(wildcardKey, route) p.blockedWildcards.RemoveRoute(wildcardKey, route) } } return p.plugin.HandleRoute(eventType, route) }
// Generate a regular expression to match wildcard hosts (and paths if any) // for a [sub]domain. func genSubdomainWildcardRegexp(hostname, path string, exactPath bool) string { subdomain := routeapi.GetDomainForHost(hostname) if len(subdomain) == 0 { glog.Warningf("Generating subdomain wildcard regexp - invalid host name %s", hostname) return fmt.Sprintf("%s%s", hostname, path) } expr := regexp.QuoteMeta(fmt.Sprintf(".%s%s", subdomain, path)) if exactPath { return fmt.Sprintf("[^\\.]*%s", expr) } return fmt.Sprintf("[^\\.]*%s(|/.*)", expr) }
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 (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 }