...

Source file src/github.com/datawire/ambassador/v2/pkg/gateway/gw_transforms_test.go

Documentation: github.com/datawire/ambassador/v2/pkg/gateway

     1  package gateway_test
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net/http"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	gw "sigs.k8s.io/gateway-api/apis/v1alpha1"
    12  
    13  	"github.com/datawire/ambassador/v2/pkg/envoytest"
    14  	"github.com/datawire/ambassador/v2/pkg/gateway"
    15  	"github.com/datawire/ambassador/v2/pkg/kates"
    16  	"github.com/datawire/dlib/dlog"
    17  )
    18  
    19  func TestGatewayMatches(t *testing.T) {
    20  	t.Parallel()
    21  	ctx := dlog.NewTestContext(t, false)
    22  	envoytest.SetupRequestLogger(t, ":9000", ":9002")
    23  	e := envoytest.SetupEnvoyController(t, ":8003")
    24  	addr, err := envoytest.GetLoopbackAddr(ctx, 8003)
    25  	require.NoError(t, err)
    26  	envoytest.SetupEnvoy(t, addr, "8080:8080")
    27  
    28  	d := makeDispatcher(t)
    29  
    30  	// One rule for each type of path match (exact, prefix, regex) and each type of header match
    31  	// (exact and regex).
    32  	err = d.UpsertYaml(`
    33  ---
    34  kind: Gateway
    35  apiVersion: networking.x-k8s.io/v1alpha1
    36  metadata:
    37    name: my-gateway
    38    namespace: default
    39  spec:
    40    listeners:
    41    - protocol: HTTP
    42      port: 8080
    43  ---
    44  kind: HTTPRoute
    45  apiVersion: networking.x-k8s.io/v1alpha1
    46  metadata:
    47    name: my-route
    48    namespace: default
    49  
    50  spec:
    51    rules:
    52    - matches:
    53      - path:
    54          type: Exact
    55          value: /exact
    56      forwardTo:
    57      - serviceName: foo-backend-1
    58        port: 9000
    59        weight: 100
    60    - matches:
    61      - path:
    62          type: Prefix
    63          value: /prefix
    64      forwardTo:
    65      - serviceName: foo-backend-1
    66        weight: 100
    67    - matches:
    68      - path:
    69          type: RegularExpression
    70          value: "/regular_expression(_[aA]+)?"
    71      forwardTo:
    72      - serviceName: foo-backend-1
    73        weight: 100
    74    - matches:
    75      - headers:
    76          type: Exact
    77          values:
    78            exact: foo
    79      forwardTo:
    80      - serviceName: foo-backend-1
    81        weight: 100
    82    - matches:
    83      - headers:
    84          type: RegularExpression
    85          values:
    86            regular_expression: "foo(_[aA]+)?"
    87      forwardTo:
    88      - serviceName: foo-backend-1
    89        weight: 100
    90  `)
    91  
    92  	require.NoError(t, err)
    93  
    94  	loopbackIp, err := envoytest.GetLoopbackIp(ctx)
    95  	require.NoError(t, err)
    96  
    97  	err = d.Upsert(makeEndpoint("default", "foo-backend-1", loopbackIp, 9000))
    98  	require.NoError(t, err)
    99  	err = d.Upsert(makeEndpoint("default", "foo-backend-2", loopbackIp, 9001))
   100  	require.NoError(t, err)
   101  
   102  	version, snapshot := d.GetSnapshot(ctx)
   103  	status, err := e.Configure("test-id", version, *snapshot)
   104  	require.NoError(t, err)
   105  	if status != nil {
   106  		t.Fatalf("envoy error: %s", status.Message)
   107  	}
   108  
   109  	// Sometimes envoy seems to acknowledge the configuration before listening on the port. (This is
   110  	// weird because sometimes envoy sends back an error indicating that it cannot bind to the
   111  	// port. Either way, we need to check that we can actually connect before running the rest of
   112  	// the tests.
   113  	checkReady(t, "http://127.0.0.1:8080/")
   114  
   115  	assertGet(t, "http://127.0.0.1:8080/exact", 200, "Hello World")
   116  	assertGet(t, "http://127.0.0.1:8080/exact/foo", 404, "")
   117  	assertGet(t, "http://127.0.0.1:8080/prefix", 200, "Hello World")
   118  	assertGet(t, "http://127.0.0.1:8080/prefix/foo", 200, "Hello World")
   119  
   120  	assertGet(t, "http://127.0.0.1:8080/regular_expression", 200, "Hello World")
   121  	assertGet(t, "http://127.0.0.1:8080/regular_expression_a", 200, "Hello World")
   122  	assertGet(t, "http://127.0.0.1:8080/regular_expression_aaaaaaaa", 200, "Hello World")
   123  	assertGet(t, "http://127.0.0.1:8080/regular_expression_aaAaaaAa", 200, "Hello World")
   124  	assertGet(t, "http://127.0.0.1:8080/regular_expression_aaAaaaAab", 404, "")
   125  
   126  	assertGetHeader(t, "http://127.0.0.1:8080", "exact", "foo", 200, "Hello World")
   127  	assertGetHeader(t, "http://127.0.0.1:8080", "exact", "bar", 404, "")
   128  	assertGetHeader(t, "http://127.0.0.1:8080", "regular_expression", "foo", 200, "Hello World")
   129  	assertGetHeader(t, "http://127.0.0.1:8080", "regular_expression", "foo_aaaaAaaaa", 200, "Hello World")
   130  	assertGetHeader(t, "http://127.0.0.1:8080", "regular_expression", "foo_aaaaAaaaab", 404, "")
   131  	assertGetHeader(t, "http://127.0.0.1:8080", "regular_expression", "bar", 404, "")
   132  }
   133  
   134  func TestBadMatchTypes(t *testing.T) {
   135  	t.Parallel()
   136  	d := makeDispatcher(t)
   137  
   138  	// One rule for each type of path match (exact, prefix, regex) and each type of header match
   139  	// (exact and regex).
   140  	err := d.UpsertYaml(`
   141  ---
   142  kind: HTTPRoute
   143  apiVersion: networking.x-k8s.io/v1alpha1
   144  metadata:
   145    name: my-route
   146    namespace: default
   147  spec:
   148    rules:
   149    - matches:
   150      - path:
   151          type: Blah
   152          value: /exact
   153      forwardTo:
   154      - serviceName: foo-backend-1
   155        port: 9000
   156        weight: 100
   157  `)
   158  	assertErrorContains(t, err, `processing HTTPRoute:default:my-route: unknown path match type: "Blah"`)
   159  
   160  	err = d.UpsertYaml(`
   161  ---
   162  kind: HTTPRoute
   163  apiVersion: networking.x-k8s.io/v1alpha1
   164  metadata:
   165    name: my-route
   166    namespace: default
   167  spec:
   168    rules:
   169    - matches:
   170      - headers:
   171          type: Bleh
   172          values:
   173            exact: foo
   174      forwardTo:
   175      - serviceName: foo-backend-1
   176        weight: 100
   177  `)
   178  	assertErrorContains(t, err, `processing HTTPRoute:default:my-route: unknown header match type: Bleh`)
   179  }
   180  
   181  func makeDispatcher(t *testing.T) *gateway.Dispatcher {
   182  	d := gateway.NewDispatcher()
   183  	err := d.Register("Gateway", func(untyped kates.Object) (*gateway.CompiledConfig, error) {
   184  		return gateway.Compile_Gateway(untyped.(*gw.Gateway))
   185  	})
   186  	require.NoError(t, err)
   187  	err = d.Register("HTTPRoute", func(untyped kates.Object) (*gateway.CompiledConfig, error) {
   188  		return gateway.Compile_HTTPRoute(untyped.(*gw.HTTPRoute))
   189  	})
   190  	require.NoError(t, err)
   191  	err = d.Register("Endpoints", func(untyped kates.Object) (*gateway.CompiledConfig, error) {
   192  		return gateway.Compile_Endpoints(untyped.(*kates.Endpoints))
   193  	})
   194  	require.NoError(t, err)
   195  	return d
   196  }
   197  
   198  func makeEndpoint(namespace, name, ip string, port int) *kates.Endpoints {
   199  	ports := []kates.EndpointPort{{Port: int32(port)}}
   200  	addrs := []kates.EndpointAddress{{IP: ip}}
   201  
   202  	return &kates.Endpoints{
   203  		TypeMeta:   kates.TypeMeta{Kind: "Endpoints"},
   204  		ObjectMeta: kates.ObjectMeta{Namespace: namespace, Name: name},
   205  		Subsets:    []kates.EndpointSubset{{Addresses: addrs, Ports: ports}},
   206  	}
   207  }
   208  
   209  func checkReady(t *testing.T, url string) {
   210  	delay := 10 * time.Millisecond
   211  	for {
   212  		if delay > 10*time.Second {
   213  			require.Fail(t, "url never became ready", url)
   214  		}
   215  		_, err := http.Get(url)
   216  		if err != nil {
   217  			t.Logf("error %v, retrying...", err)
   218  			time.Sleep(delay)
   219  			delay = delay * 2
   220  		}
   221  		return
   222  	}
   223  }
   224  
   225  func assertGet(t *testing.T, url string, code int, expected string) {
   226  	resp, err := http.Get(url)
   227  	require.NoError(t, err)
   228  	require.Equal(t, code, resp.StatusCode)
   229  	actual, err := ioutil.ReadAll(resp.Body)
   230  	require.NoError(t, err)
   231  	assert.Equal(t, expected, string(actual))
   232  }
   233  
   234  func assertGetHeader(t *testing.T, url, header, value string, code int, expected string) {
   235  	req, err := http.NewRequest(http.MethodGet, url, nil)
   236  	require.NoError(t, err)
   237  	req.Header.Set(header, value)
   238  	resp, err := http.DefaultClient.Do(req)
   239  	require.NoError(t, err)
   240  	require.Equal(t, code, resp.StatusCode)
   241  	actual, err := ioutil.ReadAll(resp.Body)
   242  	require.NoError(t, err)
   243  	assert.Equal(t, expected, string(actual))
   244  }
   245  

View as plain text