Skip to content

Commit

Permalink
add e2e test for http3 & fix ci error
Browse files Browse the repository at this point in the history
Signed-off-by: A3bz <[email protected]>
  • Loading branch information
zesiar0 committed Jan 28, 2024
1 parent 6572fd3 commit 546b25e
Show file tree
Hide file tree
Showing 3 changed files with 371 additions and 0 deletions.
136 changes: 136 additions & 0 deletions test/e2e/base/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -521,3 +521,139 @@ spec:
allowedRoutes:
kinds:
- kind: UDPRoute
---
apiVersion: v1
kind: Namespace
metadata:
name: gateway-conformance-https
labels:
gateway-conformance: http3
---
apiVersion: v1
kind: Secret
metadata:
name: http3-secret
namespace: gateway-conformance-https
type: kubernetes.io/tls
data:
tls.crt: |
MIIC2jCCAcICAQAwDQYJKoZIhvcNAQELBQAwLTEVMBMGA1UECgwMZXhhbXBsZSBJ
bmMuMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0yNDAxMTQwOTA1MzZaFw0yNTAx
MTMwOTA1MzZaMDkxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTEdMBsGA1UECgwU
ZXhhbXBsZSBvcmdhbml6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC92bKJitdHJdc/RO2q5hWNsHYR+Uz6Pv1dY3DPBPvYt8DL56sngqyJ/gOO
cbM9meUsaBOi84tvjcwut703qDa3ZFqJ1wKpuBTzfZEmJifflT6ZxQeEkp/Hv0M1
Mm8hREtXIYxLiWWV0QItvTSEtuJRJuWDyA6eJ6X1hrYzLUvKrEsCRDXe6og0hRLs
5C91S5SgEI3jZusipGusdrbPblUG4xy+mKu5qnhhF+Y9PMPaIReOKVs4tVclPKwI
oaJdbr8unU640BvwWnq0Rfir/ewjZEEHvcf9T1x1i/Nge6H8yPpmuyKnpFUd6Cfu
kC3y3/5RntUhv8y1EKt9zPbgRd+3AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHJx
ajB2YAaQAVAOgbotMlO9aHLeGkBaIraL3zG4lMZFe0PKmibMk7vCMb07MJsBrvsg
/1Aw3FU5FCSASQKSpbmWfur0iZn1nD0Pj6Vq6pPXInrjm0qSIn0emO1vHDP1YIzs
0e2lqWx7PeAfl+rW4mPYCPueEnEhafxKVdXXjjfQFk+8i7HkSaGXHJ+Fnw+obD46
c5OlbHQncE1B7ldvVz6xkKx53wbpY7pqtrDpkXnQ6iikgE8pV1Iv4fvkyzRl1htR
8lNo7GCKCnhcmC5NXCt+b4kXHb/g6yozQ5nVTxGDxeNKpVjF4mPSgukLwfUM/mJb
0lz/HNZP7/u55vQridc=
tls.key: |
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC92bKJitdHJdc/
RO2q5hWNsHYR+Uz6Pv1dY3DPBPvYt8DL56sngqyJ/gOOcbM9meUsaBOi84tvjcwu
t703qDa3ZFqJ1wKpuBTzfZEmJifflT6ZxQeEkp/Hv0M1Mm8hREtXIYxLiWWV0QIt
vTSEtuJRJuWDyA6eJ6X1hrYzLUvKrEsCRDXe6og0hRLs5C91S5SgEI3jZusipGus
drbPblUG4xy+mKu5qnhhF+Y9PMPaIReOKVs4tVclPKwIoaJdbr8unU640BvwWnq0
Rfir/ewjZEEHvcf9T1x1i/Nge6H8yPpmuyKnpFUd6CfukC3y3/5RntUhv8y1EKt9
zPbgRd+3AgMBAAECggEAPZHKavzSEErfYa9y/Ied2dOulumo40dmV9EWFq1hF8mQ
BxDc2vVwC1iLwrhxDrtILXybUa92OvlbjJMeFFzDUfqDukw2muAOO2Evn5WkPMhk
Au55H6+FjfwjSa9PhxRk8p74ps6qWUntUPQZgijlgLgCmxhYVUY6dkP39uTrvZBP
RV4GDpGRciGDxtRsL1p2q6AA8Cw4GPudw31zF+GOdohlvWnbF86aOGyJGT4yrxND
CgfOMp9pFnmxOGs5H8XnirxscU5MBPzmez6a0SOyVdym7g4M30fDdMzMils7iHP6
gQG0P1ShS94JAOXlpcJ6HNuMaeeS/lIjaV6teez64QKBgQDwQeQwEK4bBIrhfQsk
HGuPRJRfljlLZWNnx30HSR4bCIFbkHXUxxNPdmV3uIQamw4cDozonpx5p7ZwqEnj
a0g4FG+Vizd8UbK4v4Nh1HbsrsM3hWcAHRA44Q4GCs2zfl1GU2ZmfBKLrqlnYinA
HjbeW46qzOrUo0/9e4ULeHaN2QKBgQDKSkW5SjrJypjGpMUuqkpBdbXkiXwOcMXi
ZYbeHznqWjmeOn+5FKH5yQK6YRPE0ElsajWbQo9wB1TaGexuqttmjK1DdKChHzXY
KjnU+NZIc8xRC/NywF5TfMjgsqTTu+1j5L0+oH1gG+EOqOMiXLeUg7zx0uSReRuA
LB+bhGYQDwKBgQCsqu5QDOzTxgP54mArZ7F1mZf4yoIKyacTYq9cmRPl4cr1/3Bv
p1MdfL2XuShX5q/RQjPKypMya3IWrRvNMcEJbMZ090S9OAn19M17dbKyjzcFo/5O
epS6DCSD+yeypFJsxGuHpAvAD5r4DfhrdkuAqtRxtCvg/i3ywt7vS3gIoQKBgAMv
eDmauaJD1nQKVDCP7etp9xtKLvHrRLJz9tO4mijektmzAh/f+0z7pVdZ//2xafaT
FZUET03sT6G/lsGHXz0WKgRN6dmfrDFjONXptTYe3LDKB2NfNB0Mcf1X0tCivPI9
v90kmdTRm5GnhywbWai1ClykfYTs60nxZ+4C/7hxAoGADC6Fqzl9DE2+Sc1Vpqlq
RahiMOz7bgWRhXVXaAOtVvLieVdVwNHcGhiUS7pObwQb8u/bXMUScrmrblZ0fpm4
HQe9nVbQT7GtAD4UPFouCQZRevXznzZJJ1fmhOjFrc5Uj486q/xkg2fcejpUhDgG
N5Czgf9NPX47ZlnnnYSz2KU=
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: http3-gateway
namespace: gateway-conformance-https
spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- name: https

Check failure on line 592 in test/e2e/base/manifests.yaml

View workflow job for this annotation

GitHub Actions / lint

592:5 [indentation] wrong indentation: expected 2 but found 4
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret

Check failure on line 598 in test/e2e/base/manifests.yaml

View workflow job for this annotation

GitHub Actions / lint

598:11 [indentation] wrong indentation: expected 8 but found 10
name: http3-secret
namespace: gateway-conformance-https
- name: http
protocol: HTTP
port: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: http3-backend
namespace: gateway-conformance-https
---
apiVersion: v1
kind: Service
metadata:
name: http3-backend
namespace: gateway-conformance-https
labels:
service: http3-backend
app: http3-backend
spec:
ports:
- name: http3

Check failure on line 621 in test/e2e/base/manifests.yaml

View workflow job for this annotation

GitHub Actions / lint

621:5 [indentation] wrong indentation: expected 2 but found 4
port: 3000
targetPort: 3000
selectors:
app: http3-backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: http3-backend
namespace: gateway-conformance-https
spec:
replicas: 1
selector:
matchLabels:
app: http3-backend
version: v1
template:
metadata:
labels:
app: http3-backend
version: v1
spec:
serviceAccountName: http3-backend
containers:
- image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e

Check failure on line 646 in test/e2e/base/manifests.yaml

View workflow job for this annotation

GitHub Actions / lint

646:9 [indentation] wrong indentation: expected 6 but found 8
imagePullPolicy: IfNotPresent
name: backend
ports:
- containerPort: 3000

Check failure on line 650 in test/e2e/base/manifests.yaml

View workflow job for this annotation

GitHub Actions / lint

650:13 [indentation] wrong indentation: expected 10 but found 12
env:
- name: POD_NAME

Check failure on line 652 in test/e2e/base/manifests.yaml

View workflow job for this annotation

GitHub Actions / lint

652:17 [indentation] wrong indentation: expected 14 but found 16
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
32 changes: 32 additions & 0 deletions test/e2e/testdata/http3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: enable-http3
namespace: gateway-conformance-https
spec:
http3: {}
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: http3-gateway
namespace: gateway-conformance-https
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http3-backend
namespace: gateway-conformance-https
spec:
parentRefs:
- name: http3-gateway
rules:
- backendRefs:
- kind: Service
name: http3-backend
port: 3000
group: ""
weight: 1
matches:
- path:
type: PathPrefix
value: /
203 changes: 203 additions & 0 deletions test/e2e/tests/http3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.

//go:build e2e
// +build e2e

package tests

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
httpv1 "net/http"
"testing"
"time"

"github.com/quic-go/quic-go/http3"
"github.com/quic-go/quic-go/qlog"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/gateway-api/conformance/utils/http"
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
"sigs.k8s.io/gateway-api/conformance/utils/suite"
)

func init() {
ConformanceTests = append(ConformanceTests, HTTP3Test)
}

var HTTP3Test = suite.ConformanceTest{
ShortName: "Http3",
Description: "Testing http3 request",
Manifests: []string{"testdata/http3.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
t.Run("Send http3 request", func(t *testing.T) {
namespace := "gateway-conformance-https"
routeNN := types.NamespacedName{Name: "http3-route", Namespace: namespace}
gwNN := types.NamespacedName{Name: "http3-gateway", Namespace: namespace}
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
expectedResponse := http.ExpectedResponse{
Request: http.Request{
Path: "/get",
Headers: map[string]string{
"Host": "www.example.com",
},
},
Response: http.Response{
StatusCode: 200,
Headers: map[string]string{
"User-Agent": "curl/7.84.0-DEV",
"X-Forwarded-For": "https",
},
},
Namespace: namespace,
}

req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTPS", "https")
req.CertPem = []byte("-----BEGIN CERTIFICATE-----\nMIIDOzCCAiOgAwIBAgIULZP//n+WsdygxEtcJUFrsxz27wQwDQYJKoZIhvcNAQEL\nBQAwLTEVMBMGA1UECgwMZXhhbXBsZSBJbmMuMRQwEgYDVQQDDAtleGFtcGxlLmNv\nbTAeFw0yNDAxMjExNzEwMDRaFw0zNDAxMTgxNzEwMDRaMC0xFTATBgNVBAoMDGV4\nYW1wbGUgSW5jLjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQCjtTC0OjUyYeb5N8iTWXrUYJ56aDxjmA7uHz4NK5dQ\ngGkmvTLxFbQ6mJSkwBRwtVslJfl9xR7/bJhXwcA+oha0DBOuGfeXrJzy8+ax6dAX\nwzZYdfnMzW0U2MP6mfh3TwnFTywyvCanI3dZaQ1d46chHFHcoxYAoarc0SCwj+LW\nrxvuXrvCwUKWz/UY2jEIw0WkLBZ4j7ZlxTNCQAUZMYUHZzFP0R0CxSpFCi6cRrDW\nDy7RgsLrYuAr9YHKIEXmpyTukF5/gZOUGsl8S7ndQgL4n6r8qKxRZOdIlkmi/As/\nFf2nRg5kwECxwx3HkrSJqtuLgCuCpCur88+edujU1CztAgMBAAGjUzBRMB0GA1Ud\nDgQWBBSpvaIWmBrdqoWTyAZwx58wcJgbazAfBgNVHSMEGDAWgBSpvaIWmBrdqoWT\nyAZwx58wcJgbazAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAv\nsEbjaQiLj9KJXWh7jUB8iQZMEh4oyB1oHbsUrqf03QEMlh/cuTChn4+kmzW4K6EN\nh2mnsVMnX5ZegEFkvXcEVgAc1QFbyAB4j2L4EM4kaQ8+quKsS9N8O9qHvUmiMg3R\nVlEYwpIN/DqiHQBY22IV5KxmRZDaDqlJMOi8WjQAh5AnSo0WZC3PnDgHuMDxcOaZ\n7cw2p1/RdaFZB9Va00g5JsSl2GtaibW3oWDFJAGKH+04lz/bT3lcVv0R4ADsF3qv\noBj2wtFDiqjj5dqe6/QtJCMkNDCbbzw2buogzuPBplE0Z4WtsWaQIq4MBnfGi8AF\n+sSBiMU4B9gf4oU93U2m\n-----END CERTIFICATE-----")
req.KeyPem = []byte("-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCjtTC0OjUyYeb5\nN8iTWXrUYJ56aDxjmA7uHz4NK5dQgGkmvTLxFbQ6mJSkwBRwtVslJfl9xR7/bJhX\nwcA+oha0DBOuGfeXrJzy8+ax6dAXwzZYdfnMzW0U2MP6mfh3TwnFTywyvCanI3dZ\naQ1d46chHFHcoxYAoarc0SCwj+LWrxvuXrvCwUKWz/UY2jEIw0WkLBZ4j7ZlxTNC\nQAUZMYUHZzFP0R0CxSpFCi6cRrDWDy7RgsLrYuAr9YHKIEXmpyTukF5/gZOUGsl8\nS7ndQgL4n6r8qKxRZOdIlkmi/As/Ff2nRg5kwECxwx3HkrSJqtuLgCuCpCur88+e\ndujU1CztAgMBAAECggEACrVe4MyARNIBy2e3pih/qqCW/+pNbaAEIAA+rUVenJcz\nFE6NecGB8dBKbtwaqi8OwydmAnEFwKRa8xNF2ZhS3wb9iuarcF+L6loudburOewH\neXIJe6OstjpMrYSWARXpjXTK7xb+z0bjDHVHo3kxVO7hjaAZtRkI0HDEkDrFQOYw\nzpYhQwJVCp5/ZHY4M7bzjWHySdIt/K+lyTLPSF3b6d/aNWQK1iQbQ4gPFJeW2wZP\nkj8zeSxTSxD6psahrjho/SXfGNTNq1m5jOM9fjR3BB/DPS3IBTXDl5IUOFCsFmd/\nu5CnXOX5RJJl/+gMFWEM25RNYEcFrNUpcSS67r1i+QKBgQDTlpBL9BCvTnWGl5Ru\nPL4u+AIgK4foKA1aczXAGZ5/oeCdcXpqdBvCQPBHjXZnQngbZ5tkR5fcXM376wrl\n4E+AOetbeYfJjvlRm9L6QmlFY/Nlv3McBDM4KGh6ZeiWjyR2yqgGtRO/kk3oFQ+p\npYeN+t0Fu9lO++fqUNt8yxsgtQKBgQDGEdUQfIdwAgokW/uWj5PZmOuaUkcKwcX4\nt2+4Gz/b5QE0pBCvpmrb33SriUrKBSQnzvU0r70Ig2wndY6ulfcb5sUieM/LHZiR\npxtDqVIKpQiXLHo4iycn6tY7lKqwWsCqXUxjB5JMfW1IeEzj4XexeRly4sLDapS4\nSJNe5GVWWQKBgHsY7XpC1DIpg1Z6eXBpBnxs7U+qA7edFae5v1uzi/LVSshObNni\nEwRAo4n9UxVgJmBLNqxwunkJxQz7AawbhCUljTf6zHUHKSXBck0GthgYvlJDv8Rc\n7S+O0rni8B4nyR8TaA3+6y5Y/9o15pbcJrEDcfMUBqldBN/ditRflbjBAoGAdD+D\nDWoJE3Qe/7f8sSETZWKa5LfleirARnli2Gslz6lYS8z+/hhuHx3HG+Y4PtlFnxeY\nUpPSHm0DzSTx2QWrQnTuvoypaEy2fsXU+qElxZmWsSMpmIYTNRpfIhjfFSIucc7Q\nRk7rTnlO6nmwpw5tcXvhs8vjA05Ket4doFPsJgECgYAEVrbe2182Z2bG0xacHPJw\nZ3SJAh8JEj9MG1Cj0M3COP+iCWR2JDk0F7bsDDhpEZrPtyF1Ea0i1YpFDJEAXmIw\nZaHfywv9wqkv+1/T15O9qHZ3GDkyRTqD//HgdGbVBWF3AyPzXkW5EM35A4qUVq7I\nKe1vZDEBEfK/Mq/DJ877sg==\n-----END PRIVATE KEY-----")
cReq, cResp, err := CaptureRoundTrip(req)
if err != nil {
t.Errorf("failed to get expected response: %v", err)
}

if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil {
t.Errorf("failed to compare request and response: %v", err)
}
})
},
}

func CaptureRoundTrip(request roundtripper.Request) (*roundtripper.CapturedRequest, *roundtripper.CapturedResponse, error) {
transport, err := httpTransport(request)
if err != nil {
return nil, nil, err
}
return defaultRoundTrip(request, transport)
}

func httpTransport(request roundtripper.Request) (*http3.RoundTripper, error) {
transport := &http3.RoundTripper{
QuicConfig: &quic.Config{

Check failure on line 87 in test/e2e/tests/http3.go

View workflow job for this annotation

GitHub Actions / lint

undefined: quic (typecheck)

Check failure on line 87 in test/e2e/tests/http3.go

View workflow job for this annotation

GitHub Actions / lint

undefined: quic) (typecheck)
Tracer: qlog.DefaultTracer,
},
}
if request.Server != "" && len(request.CertPem) != 0 && len(request.KeyPem) != 0 {
tlsConfig, err := tlsClientConfig(request.Server, request.CertPem, request.KeyPem)
if err != nil {
return nil, err
}
transport.TLSClientConfig = tlsConfig
}
return transport, nil
}

func tlsClientConfig(server string, certPem []byte, keyPem []byte) (*tls.Config, error) {
// Create a certificate from the provided cert and key
cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
return nil, fmt.Errorf("unexpected error creating cert: %w", err)
}

// Add the provided cert as a trusted CA
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certPem) {
return nil, fmt.Errorf("unexpected error adding trusted CA: %w", err)
}

if server == "" {
return nil, fmt.Errorf("unexpected error, server name required for TLS")
}

// Create the tls Config for this provided host, cert, and trusted CA
// Disable G402: TLS MinVersion too low. (gosec)
// #nosec G402
return &tls.Config{
Certificates: []tls.Certificate{cert},
ServerName: server,
RootCAs: certPool,
}, nil
}

func defaultRoundTrip(request roundtripper.Request, transport *http3.RoundTripper) (*roundtripper.CapturedRequest, *roundtripper.CapturedResponse, error) {
client := &httpv1.Client{}

if request.UnfollowRedirect {
client.CheckRedirect = func(req *httpv1.Request, via []*httpv1.Request) error {
return httpv1.ErrUseLastResponse
}
}

client.Transport = transport

method := "GET"
if request.Method != "" {
method = request.Method
}
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
req, err := httpv1.NewRequestWithContext(ctx, method, request.URL.String(), nil)
if err != nil {
return nil, nil, err
}

if request.Host != "" {
req.Host = request.Host
}

if request.Headers != nil {
for name, value := range request.Headers {
req.Header.Set(name, value[0])
}
}

resp, err := client.Do(req)
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()

cReq := &roundtripper.CapturedRequest{}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, nil, err
}

if resp.Header.Get("Content-Type") == "application/json" {
err = json.Unmarshal(body, cReq)
if err != nil {
return nil, nil, fmt.Errorf("unexpected error reading response: %w", err)
}
} else {
cReq.Method = method
}

cRes := &roundtripper.CapturedResponse{
StatusCode: resp.StatusCode,
ContentLength: resp.ContentLength,
Protocol: resp.Proto,
Headers: resp.Header,
}

if roundtripper.IsRedirect(resp.StatusCode) {
redirectURL, err := resp.Location()
if err != nil {
return nil, nil, err
}
cRes.RedirectRequest = &roundtripper.RedirectRequest{
Scheme: redirectURL.Scheme,
Host: redirectURL.Hostname(),
Port: redirectURL.Port(),
Path: redirectURL.Path,
}
}

return cReq, cRes, nil
}

0 comments on commit 546b25e

Please sign in to comment.