Skip to content

Commit aec6e82

Browse files
committed
router: validate ingress compatibility with namespace filtering
1 parent 8834d91 commit aec6e82

File tree

2 files changed

+103
-28
lines changed

2 files changed

+103
-28
lines changed

test/integration/router/router_http_server.go

+36-15
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ func GetDefaultLocalAddress() string {
3030
return addr
3131
}
3232

33+
func NewTestHttpService() *TestHttpService {
34+
return NewTestHttpServiceExtended("")
35+
}
36+
3337
// NewTestHttpServer creates a new TestHttpService using default locations for listening address
3438
// as well as default certificates. New channels will be initialized which can be used by test clients
3539
// to feed events through the server to anything listening.
36-
func NewTestHttpService() *TestHttpService {
40+
func NewTestHttpServiceExtended(namespaceListResponse string) *TestHttpService {
3741
endpointChannel := make(chan string)
3842
routeChannel := make(chan string)
3943
ingressChannel := make(chan string)
@@ -48,21 +52,27 @@ func NewTestHttpService() *TestHttpService {
4852
alternatePodHttpAddr := fmt.Sprintf("%s:8889", addr)
4953
podHttpsAddr := fmt.Sprintf("%s:8443", addr)
5054

55+
// Ensure an empty namespace response is valid json
56+
if namespaceListResponse == "" {
57+
namespaceListResponse = "{}"
58+
}
59+
5160
return &TestHttpService{
52-
MasterHttpAddr: masterHttpAddr,
53-
PodHttpAddr: podHttpAddr,
54-
AlternatePodHttpAddr: alternatePodHttpAddr,
55-
PodHttpsAddr: podHttpsAddr,
56-
PodTestPath: "test",
57-
PodHttpsCert: []byte(Example2Cert),
58-
PodHttpsKey: []byte(Example2Key),
59-
PodHttpsCaCert: []byte(ExampleCACert),
60-
EndpointChannel: endpointChannel,
61-
RouteChannel: routeChannel,
62-
IngressChannel: ingressChannel,
63-
SecretChannel: secretChannel,
64-
NodeChannel: nodeChannel,
65-
SvcChannel: svcChannel,
61+
MasterHttpAddr: masterHttpAddr,
62+
PodHttpAddr: podHttpAddr,
63+
AlternatePodHttpAddr: alternatePodHttpAddr,
64+
PodHttpsAddr: podHttpsAddr,
65+
PodTestPath: "test",
66+
PodHttpsCert: []byte(Example2Cert),
67+
PodHttpsKey: []byte(Example2Key),
68+
PodHttpsCaCert: []byte(ExampleCACert),
69+
EndpointChannel: endpointChannel,
70+
RouteChannel: routeChannel,
71+
IngressChannel: ingressChannel,
72+
SecretChannel: secretChannel,
73+
NodeChannel: nodeChannel,
74+
SvcChannel: svcChannel,
75+
NamespaceListResponse: namespaceListResponse,
6676
}
6777
}
6878

@@ -90,6 +100,8 @@ type TestHttpService struct {
90100
NodeChannel chan string
91101
SvcChannel chan string
92102

103+
NamespaceListResponse string
104+
93105
listeners []net.Listener
94106
}
95107

@@ -143,6 +155,14 @@ func (s *TestHttpService) handleHelloPodTestSecure(w http.ResponseWriter, r *htt
143155
fmt.Fprint(w, HelloPodPathSecure)
144156
}
145157

158+
// handleNamespaceList handles calls to /api/v1/namespaces/* and returns a canned response
159+
func (s *TestHttpService) handleNamespaceList(w http.ResponseWriter, r *http.Request) {
160+
w.Header().Set("Content-Type", "application/json")
161+
glog.Errorf("Returning response: %s", s.NamespaceListResponse)
162+
163+
fmt.Fprint(w, s.NamespaceListResponse)
164+
}
165+
146166
// handleSvcList handles calls to /api/v1beta1/services and always returns empty data
147167
func (s *TestHttpService) handleSvcList(w http.ResponseWriter, r *http.Request) {
148168
w.Header().Set("Content-Type", "application/json")
@@ -263,6 +283,7 @@ func (s *TestHttpService) startMaster() error {
263283
apis := []string{"v1"}
264284

265285
for _, version := range apis {
286+
masterServer.HandleFunc(fmt.Sprintf("/api/%s/namespaces/", version), s.handleNamespaceList)
266287
masterServer.HandleFunc(fmt.Sprintf("/api/%s/endpoints", version), s.handleEndpointList)
267288
masterServer.HandleFunc(fmt.Sprintf("/api/%s/watch/endpoints", version), s.handleEndpointWatch)
268289
masterServer.HandleFunc(fmt.Sprintf("/oapi/%s/routes", version), s.handleRouteList)

test/integration/router_test.go

+67-13
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020

2121
kapi "k8s.io/kubernetes/pkg/api"
2222
"k8s.io/kubernetes/pkg/api/unversioned"
23+
kv1 "k8s.io/kubernetes/pkg/api/v1"
2324
"k8s.io/kubernetes/pkg/apis/extensions"
2425
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
26+
"k8s.io/kubernetes/pkg/runtime"
2527
"k8s.io/kubernetes/pkg/util/intstr"
2628
knet "k8s.io/kubernetes/pkg/util/net"
2729
"k8s.io/kubernetes/pkg/util/wait"
@@ -37,6 +39,8 @@ import (
3739
const (
3840
defaultRouterImage = "openshift/origin-haproxy-router"
3941

42+
defaultNamespace = "router-namespace"
43+
4044
tcWaitSeconds = 1
4145

4246
statsPort = 1936
@@ -1275,13 +1279,20 @@ pgfj+yGLmkUw8JwgGH6xCUbHO+WBUFSlPf+Y50fJeO+OrjqPXAVKeSV3ZCwWjKT4
12751279
u3YLAbyW/lHhOCiZu2iAI8AbmXem9lW6Tr7p/97s0w==
12761280
-----END RSA PRIVATE KEY-----`
12771281

1282+
// Constants used to default createAndStartRouterContainerExtended
1283+
const (
1284+
defaultBindPortsAfterSync = false
1285+
defaultEnableIngress = false
1286+
defaultNamespaceLabels = ""
1287+
)
1288+
12781289
// createAndStartRouterContainer is responsible for deploying the router image in docker. It assumes that all router images
12791290
// will use a command line flag that can take --master which points to the master url
12801291
func createAndStartRouterContainer(dockerCli *dockerClient.Client, masterIp string, routerStatsPort int, reloadInterval int) (containerId string, err error) {
1281-
return createAndStartRouterContainerExtended(dockerCli, masterIp, routerStatsPort, reloadInterval, false, false)
1292+
return createAndStartRouterContainerExtended(dockerCli, masterIp, routerStatsPort, reloadInterval, defaultBindPortsAfterSync, defaultEnableIngress, defaultNamespaceLabels)
12821293
}
12831294

1284-
func createAndStartRouterContainerExtended(dockerCli *dockerClient.Client, masterIp string, routerStatsPort int, reloadInterval int, bindPortsAfterSync, enableIngress bool) (containerId string, err error) {
1295+
func createAndStartRouterContainerExtended(dockerCli *dockerClient.Client, masterIp string, routerStatsPort int, reloadInterval int, bindPortsAfterSync, enableIngress bool, namespaceLabels string) (containerId string, err error) {
12851296
ports := []string{"80", "443"}
12861297
if routerStatsPort > 0 {
12871298
ports = append(ports, fmt.Sprintf("%d", routerStatsPort))
@@ -1319,6 +1330,7 @@ func createAndStartRouterContainerExtended(dockerCli *dockerClient.Client, maste
13191330
fmt.Sprintf("DEFAULT_CERTIFICATE=%s\n%s", defaultCert, defaultKey),
13201331
fmt.Sprintf("ROUTER_BIND_PORTS_AFTER_SYNC=%s", strconv.FormatBool(bindPortsAfterSync)),
13211332
fmt.Sprintf("ROUTER_ENABLE_INGRESS=%s", strconv.FormatBool(enableIngress)),
1333+
fmt.Sprintf("NAMESPACE_LABELS=%s", namespaceLabels),
13221334
}
13231335

13241336
reloadIntVar := fmt.Sprintf("RELOAD_INTERVAL=%ds", reloadInterval)
@@ -1635,7 +1647,7 @@ func TestRouterBindsPortsAfterSync(t *testing.T) {
16351647

16361648
bindPortsAfterSync := true
16371649
reloadInterval := 1
1638-
routerId, err := createAndStartRouterContainerExtended(dockerCli, fakeMasterAndPod.MasterHttpAddr, statsPort, reloadInterval, bindPortsAfterSync, false)
1650+
routerId, err := createAndStartRouterContainerExtended(dockerCli, fakeMasterAndPod.MasterHttpAddr, statsPort, reloadInterval, bindPortsAfterSync, defaultEnableIngress, defaultNamespaceLabels)
16391651
if err != nil {
16401652
t.Fatalf("Error starting container %s : %v", getRouterImage(), err)
16411653
}
@@ -1696,10 +1708,12 @@ func TestRouterBindsPortsAfterSync(t *testing.T) {
16961708

16971709
type routerIntegrationTest func(*testing.T, *tr.TestHttpService)
16981710

1699-
func runRouterTest(t *testing.T, rit routerIntegrationTest) {
1711+
func runRouterTest(t *testing.T, rit routerIntegrationTest, enableIngress bool, namespaceNames *[]string) {
1712+
namespaceLabels, namespaceListResponse := getNamespaceConfig(t, namespaceNames)
1713+
17001714
//create a server which will act as a user deployed application that
17011715
//serves http and https as well as act as a master to simulate watches
1702-
fakeMasterAndPod := tr.NewTestHttpService()
1716+
fakeMasterAndPod := tr.NewTestHttpServiceExtended(namespaceListResponse)
17031717
defer fakeMasterAndPod.Stop()
17041718

17051719
err := fakeMasterAndPod.Start()
@@ -1717,10 +1731,8 @@ func runRouterTest(t *testing.T, rit routerIntegrationTest) {
17171731
}
17181732

17191733
reloadInterval := 1
1720-
bindPortsAfterSync := false
1721-
enableIngress := true
17221734
routerId, err := createAndStartRouterContainerExtended(
1723-
dockerCli, fakeMasterAndPod.MasterHttpAddr, statsPort, reloadInterval, bindPortsAfterSync, enableIngress)
1735+
dockerCli, fakeMasterAndPod.MasterHttpAddr, statsPort, reloadInterval, defaultBindPortsAfterSync, enableIngress, namespaceLabels)
17241736

17251737
if err != nil {
17261738
t.Fatalf("Error starting container %s : %v", getRouterImage(), err)
@@ -1731,6 +1743,43 @@ func runRouterTest(t *testing.T, rit routerIntegrationTest) {
17311743
rit(t, fakeMasterAndPod)
17321744
}
17331745

1746+
func getNamespaceConfig(t *testing.T, namespaceNames *[]string) (namespaceLabels, namespaceListResponse string) {
1747+
if namespaceNames == nil {
1748+
return
1749+
}
1750+
1751+
key := "env"
1752+
value := "testing"
1753+
1754+
// If namespace names are provided (event an empty set), ensure
1755+
// namespace filtering is exercised by adding namespaces for the
1756+
// provided names with labels that the router will filter on.
1757+
namespaceLabels = fmt.Sprintf("%s=%s", key, value)
1758+
1759+
namespaceList := &kapi.NamespaceList{
1760+
ListMeta: unversioned.ListMeta{
1761+
ResourceVersion: fmt.Sprintf("%d", len(*namespaceNames)),
1762+
},
1763+
Items: []kapi.Namespace{},
1764+
}
1765+
for _, name := range *namespaceNames {
1766+
namespaceList.Items = append(namespaceList.Items, kapi.Namespace{
1767+
ObjectMeta: kapi.ObjectMeta{
1768+
Name: name,
1769+
Labels: map[string]string{key: value},
1770+
},
1771+
})
1772+
}
1773+
1774+
obj, err := runtime.Encode(kapi.Codecs.LegacyCodec(kv1.SchemeGroupVersion), namespaceList)
1775+
if err != nil {
1776+
t.Fatalf("Unexpected error: %v", err)
1777+
}
1778+
namespaceListResponse = string(obj)
1779+
1780+
return
1781+
}
1782+
17341783
// eventString marshals ingress events into a string. A separate
17351784
// method is required because ingress uses a different schema version
17361785
// (v1beta1) than routes (v1).
@@ -1748,7 +1797,6 @@ func ingressConfiguredRouter(t *testing.T, fakeMasterAndPod *tr.TestHttpService)
17481797

17491798
routeAddress := getRouteAddress()
17501799

1751-
namespace := "my-namespace"
17521800
serviceName := "my-service"
17531801
host := "my.host"
17541802
path := fmt.Sprintf("/%s", fakeMasterAndPod.PodTestPath)
@@ -1758,7 +1806,7 @@ func ingressConfiguredRouter(t *testing.T, fakeMasterAndPod *tr.TestHttpService)
17581806
Object: &kapi.Endpoints{
17591807
ObjectMeta: kapi.ObjectMeta{
17601808
Name: serviceName,
1761-
Namespace: namespace,
1809+
Namespace: defaultNamespace,
17621810
},
17631811
Subsets: []kapi.EndpointSubset{httpEndpoint},
17641812
},
@@ -1776,7 +1824,7 @@ func ingressConfiguredRouter(t *testing.T, fakeMasterAndPod *tr.TestHttpService)
17761824
Object: &kapi.Secret{
17771825
ObjectMeta: kapi.ObjectMeta{
17781826
Name: secretName,
1779-
Namespace: namespace,
1827+
Namespace: defaultNamespace,
17801828
},
17811829
Data: map[string][]byte{
17821830
"tls.crt": []byte(defaultCert),
@@ -1791,7 +1839,7 @@ func ingressConfiguredRouter(t *testing.T, fakeMasterAndPod *tr.TestHttpService)
17911839
Object: &extensions.Ingress{
17921840
ObjectMeta: kapi.ObjectMeta{
17931841
Name: "foo",
1794-
Namespace: namespace,
1842+
Namespace: defaultNamespace,
17951843
},
17961844
Spec: extensions.IngressSpec{
17971845
TLS: []extensions.IngressTLS{
@@ -1838,9 +1886,15 @@ func ingressConfiguredRouter(t *testing.T, fakeMasterAndPod *tr.TestHttpService)
18381886
if err := waitForRoute(url, host, "https", nil, tr.HelloPodPath); err != nil {
18391887
t.Fatalf("Error accessing secured ingress configured route: %v", err)
18401888
}
1889+
1890+
// TODO check that an ingress in a namespace not targeted by the router does not
1891+
// result in exposed routes.
18411892
}
18421893

18431894
// TestRouterIngress validates that an ingress resource can configure a router to expose a tls route.
18441895
func TestIngressConfiguredRouter(t *testing.T) {
1845-
runRouterTest(t, ingressConfiguredRouter)
1896+
enableIngress := true
1897+
// Enable namespace filtering to allow validation of compatibility with ingress.
1898+
namespaceNames := []string{defaultNamespace}
1899+
runRouterTest(t, ingressConfiguredRouter, enableIngress, &namespaceNames)
18461900
}

0 commit comments

Comments
 (0)