Beispiel #1
0
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
}
Beispiel #2
0
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)
}
Beispiel #3
0
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
}
Beispiel #4
0
// 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
}
Beispiel #5
0
func (r routeAge) Less(i, j int) bool {
	return routeapi.RouteLessThan(&r[i], &r[j])
}