diff --git a/pkg/router/controller/host_admitter.go b/pkg/router/controller/host_admitter.go index 4ffb065a6c8c..adea3d19a194 100644 --- a/pkg/router/controller/host_admitter.go +++ b/pkg/router/controller/host_admitter.go @@ -87,6 +87,10 @@ type HostAdmitter struct { // ownership (of subdomains) to a single owner/namespace. disableNamespaceCheck bool + // allowedNamespaces is the set of allowed namespaces. + // Note that nil (aka allow all) has a different meaning than empty set. + allowedNamespaces sets.String + claimedHosts RouteMap claimedWildcards RouteMap blockedWildcards RouteMap @@ -122,6 +126,12 @@ func (p *HostAdmitter) HandleEndpoints(eventType watch.EventType, endpoints *kap // HandleRoute processes watch events on the Route resource. func (p *HostAdmitter) HandleRoute(eventType watch.EventType, route *routeapi.Route) error { + if p.allowedNamespaces != nil && !p.allowedNamespaces.Has(route.Namespace) { + // Ignore routes we don't need to "service" due to namespace + // restrictions (ala for sharding). + return nil + } + if err := p.admitter(route); err != nil { glog.V(4).Infof("Route %s not admitted: %s", routeNameKey(route), err.Error()) p.recorder.RecordRouteRejection(route, "RouteNotAdmitted", err.Error()) @@ -151,6 +161,7 @@ func (p *HostAdmitter) HandleRoute(eventType watch.EventType, route *routeapi.Ro // HandleNamespaces limits the scope of valid routes to only those that match // the provided namespace list. func (p *HostAdmitter) HandleNamespaces(namespaces sets.String) error { + p.allowedNamespaces = namespaces return p.plugin.HandleNamespaces(namespaces) } diff --git a/pkg/router/controller/host_admitter_test.go b/pkg/router/controller/host_admitter_test.go index 7a5a49a6249f..06f53f3a52b4 100644 --- a/pkg/router/controller/host_admitter_test.go +++ b/pkg/router/controller/host_admitter_test.go @@ -3,6 +3,7 @@ package controller import ( "fmt" "math/rand" + "reflect" "strings" "testing" "time" @@ -1004,3 +1005,124 @@ func TestDisableOwnershipChecksFuzzing(t *testing.T) { t.Errorf("Unexpected errors:\n%s", strings.Join(errors.List(), "\n")) } } + +func TestHandleNamespaceProcessing(t *testing.T) { + p := &fakePlugin{} + recorder := rejectionRecorder{rejections: make(map[string]string)} + admitter := NewHostAdmitter(p, wildcardAdmitter, true, false, recorder) + + // Set namespaces handled in the host admitter plugin, the fakePlugin in + // the test chain doesn't support this, so ignore not expected error. + err := admitter.HandleNamespaces(sets.NewString("ns1", "ns2", "nsx")) + if err != nil && err.Error() != "not expected" { + t.Fatalf("unexpected error: %v", err) + } + + tests := []struct { + name string + namespace string + host string + policy routeapi.WildcardPolicyType + expected bool + }{ + { + name: "expected", + namespace: "ns1", + host: "update.expected.test", + policy: routeapi.WildcardPolicyNone, + expected: true, + }, + { + name: "not-expected", + namespace: "updatemenot", + host: "no-update.expected.test", + policy: routeapi.WildcardPolicyNone, + expected: false, + }, + { + name: "expected-wild", + namespace: "ns1", + host: "update.wild.expected.test", + policy: routeapi.WildcardPolicySubdomain, + expected: true, + }, + { + name: "not-expected-wild-not-owner", + namespace: "nsx", + host: "second.wild.expected.test", + policy: routeapi.WildcardPolicySubdomain, + expected: false, + }, + { + name: "not-expected-wild", + namespace: "otherns", + host: "noupdate.wild.expected.test", + policy: routeapi.WildcardPolicySubdomain, + expected: false, + }, + { + name: "expected-wild-other-subdomain", + namespace: "nsx", + host: "host.third.wild.expected.test", + policy: routeapi.WildcardPolicySubdomain, + expected: true, + }, + { + name: "not-expected-plain-2", + namespace: "notallowed", + host: "not.allowed.expected.test", + policy: routeapi.WildcardPolicyNone, + expected: false, + }, + { + name: "not-expected-blocked", + namespace: "nsx", + host: "blitz.domain.blocked.test", + policy: routeapi.WildcardPolicyNone, + expected: false, + }, + { + name: "not-expected-blocked-wildcard", + namespace: "ns2", + host: "wild.blocked.domain.blocked.test", + policy: routeapi.WildcardPolicySubdomain, + expected: false, + }, + } + + for _, tc := range tests { + route := &routeapi.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: tc.name, + Namespace: tc.namespace, + UID: types.UID(tc.name), + }, + Spec: routeapi.RouteSpec{ + Host: tc.host, + WildcardPolicy: tc.policy, + }, + Status: routeapi.RouteStatus{ + Ingress: []routeapi.RouteIngress{ + { + Host: tc.host, + RouterName: "nsproc", + Conditions: []routeapi.RouteIngressCondition{}, + WildcardPolicy: tc.policy, + }, + }, + }, + } + + err := admitter.HandleRoute(watch.Added, route) + if tc.expected { + if err != nil { + t.Fatalf("test case %s unexpected error: %v", tc.name, err) + } + if !reflect.DeepEqual(p.route, route) { + t.Fatalf("test case %s expected route to be processed: %+v", tc.name, route) + } + } else if err == nil && reflect.DeepEqual(p.route, route) { + t.Fatalf("test case %s did not expected route to be processed: %+v", tc.name, route) + } + } +}