Merge current branch v2.4 into master
This commit is contained in:
commit
702e301990
100 changed files with 561 additions and 557 deletions
|
@ -4,7 +4,6 @@ import (
|
|||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
|
@ -42,9 +41,7 @@ var (
|
|||
)
|
||||
|
||||
func TestLogRotation(t *testing.T) {
|
||||
tempDir := createTempDir(t, "traefik_")
|
||||
|
||||
fileName := filepath.Join(tempDir, "traefik.log")
|
||||
fileName := filepath.Join(t.TempDir(), "traefik.log")
|
||||
rotatedFileName := fileName + ".rotated"
|
||||
|
||||
config := &types.AccessLog{FilePath: fileName, Format: CommonFormat}
|
||||
|
@ -100,7 +97,7 @@ func TestLogRotation(t *testing.T) {
|
|||
|
||||
func lineCount(t *testing.T, fileName string) int {
|
||||
t.Helper()
|
||||
fileContents, err := ioutil.ReadFile(fileName)
|
||||
fileContents, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading from file %s: %s", fileName, err)
|
||||
}
|
||||
|
@ -117,8 +114,6 @@ func lineCount(t *testing.T, fileName string) int {
|
|||
}
|
||||
|
||||
func TestLoggerHeaderFields(t *testing.T) {
|
||||
tmpDir := createTempDir(t, CommonFormat)
|
||||
|
||||
expectedValue := "expectedValue"
|
||||
|
||||
testCases := []struct {
|
||||
|
@ -172,7 +167,7 @@ func TestLoggerHeaderFields(t *testing.T) {
|
|||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
logFile, err := ioutil.TempFile(tmpDir, "*.log")
|
||||
logFile, err := os.CreateTemp(t.TempDir(), "*.log")
|
||||
require.NoError(t, err)
|
||||
|
||||
config := &types.AccessLog{
|
||||
|
@ -202,7 +197,7 @@ func TestLoggerHeaderFields(t *testing.T) {
|
|||
writer.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
logData, err := ioutil.ReadFile(logFile.Name())
|
||||
logData, err := os.ReadFile(logFile.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
if test.expected == types.AccessLogDrop {
|
||||
|
@ -215,13 +210,11 @@ func TestLoggerHeaderFields(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoggerCLF(t *testing.T) {
|
||||
tmpDir := createTempDir(t, CommonFormat)
|
||||
|
||||
logFilePath := filepath.Join(tmpDir, logFileNameSuffix)
|
||||
logFilePath := filepath.Join(t.TempDir(), logFileNameSuffix)
|
||||
config := &types.AccessLog{FilePath: logFilePath, Format: CommonFormat}
|
||||
doLogging(t, config)
|
||||
|
||||
logData, err := ioutil.ReadFile(logFilePath)
|
||||
logData, err := os.ReadFile(logFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedLog := ` TestHost - TestUser [13/Apr/2016:07:14:19 -0700] "POST testpath HTTP/0.0" 123 12 "testReferer" "testUserAgent" 1 "testRouter" "http://127.0.0.1/testService" 1ms`
|
||||
|
@ -229,13 +222,11 @@ func TestLoggerCLF(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAsyncLoggerCLF(t *testing.T) {
|
||||
tmpDir := createTempDir(t, CommonFormat)
|
||||
|
||||
logFilePath := filepath.Join(tmpDir, logFileNameSuffix)
|
||||
logFilePath := filepath.Join(t.TempDir(), logFileNameSuffix)
|
||||
config := &types.AccessLog{FilePath: logFilePath, Format: CommonFormat, BufferingSize: 1024}
|
||||
doLogging(t, config)
|
||||
|
||||
logData, err := ioutil.ReadFile(logFilePath)
|
||||
logData, err := os.ReadFile(logFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedLog := ` TestHost - TestUser [13/Apr/2016:07:14:19 -0700] "POST testpath HTTP/0.0" 123 12 "testReferer" "testUserAgent" 1 "testRouter" "http://127.0.0.1/testService" 1ms`
|
||||
|
@ -452,9 +443,7 @@ func TestLoggerJSON(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := createTempDir(t, JSONFormat)
|
||||
|
||||
logFilePath := filepath.Join(tmpDir, logFileNameSuffix)
|
||||
logFilePath := filepath.Join(t.TempDir(), logFileNameSuffix)
|
||||
|
||||
test.config.FilePath = logFilePath
|
||||
if test.tls {
|
||||
|
@ -463,7 +452,7 @@ func TestLoggerJSON(t *testing.T) {
|
|||
doLogging(t, test.config)
|
||||
}
|
||||
|
||||
logData, err := ioutil.ReadFile(logFilePath)
|
||||
logData, err := os.ReadFile(logFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonData := make(map[string]interface{})
|
||||
|
@ -677,7 +666,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
|
|||
|
||||
doLogging(t, test.config)
|
||||
|
||||
written, err := ioutil.ReadFile(file.Name())
|
||||
written, err := os.ReadFile(file.Name())
|
||||
require.NoError(t, err, "unable to read captured stdout from file")
|
||||
assertValidLogData(t, test.expectedLog, written)
|
||||
})
|
||||
|
@ -713,16 +702,16 @@ func assertValidLogData(t *testing.T, expected string, logData []byte) {
|
|||
assert.Equal(t, resultExpected[OriginContentSize], result[OriginContentSize], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RequestRefererHeader], result[RequestRefererHeader], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RequestUserAgentHeader], result[RequestUserAgentHeader], formatErrMessage)
|
||||
assert.Regexp(t, regexp.MustCompile("[0-9]*"), result[RequestCount], formatErrMessage)
|
||||
assert.Regexp(t, regexp.MustCompile(`\d*`), result[RequestCount], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RouterName], result[RouterName], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[ServiceURL], result[ServiceURL], formatErrMessage)
|
||||
assert.Regexp(t, regexp.MustCompile("[0-9]*ms"), result[Duration], formatErrMessage)
|
||||
assert.Regexp(t, regexp.MustCompile(`\d*ms`), result[Duration], formatErrMessage)
|
||||
}
|
||||
|
||||
func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) {
|
||||
t.Helper()
|
||||
|
||||
file, err := ioutil.TempFile("", "testlogger")
|
||||
file, err := os.CreateTemp("", "testlogger")
|
||||
require.NoError(t, err, "failed to create temp file")
|
||||
|
||||
original := os.Stdout
|
||||
|
@ -736,17 +725,6 @@ func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) {
|
|||
return file, restoreStdout
|
||||
}
|
||||
|
||||
func createTempDir(t *testing.T, prefix string) string {
|
||||
t.Helper()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", prefix)
|
||||
require.NoError(t, err, "failed to create temp dir")
|
||||
|
||||
t.Cleanup(func() { _ = os.RemoveAll(tmpDir) })
|
||||
|
||||
return tmpDir
|
||||
}
|
||||
|
||||
func doLoggingTLSOpt(t *testing.T, config *types.AccessLog, enableTLS bool) {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -46,7 +46,7 @@ func loadUsers(fileName string, appendUsers []string) ([]string, error) {
|
|||
}
|
||||
|
||||
func getLinesFromFile(filename string) ([]string, error) {
|
||||
dat, err := ioutil.ReadFile(filename)
|
||||
dat, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package auth
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
|
@ -66,7 +66,7 @@ func TestBasicAuthSuccess(t *testing.T) {
|
|||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal")
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
|
||||
|
@ -97,7 +97,7 @@ func TestBasicAuthUserHeader(t *testing.T) {
|
|||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
|
||||
|
@ -128,7 +128,7 @@ func TestBasicAuthHeaderRemoved(t *testing.T) {
|
|||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
@ -159,7 +159,7 @@ func TestBasicAuthHeaderPresent(t *testing.T) {
|
|||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
@ -215,9 +215,8 @@ func TestBasicAuthUsersFromFile(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
// Creates the temporary configuration file with the users
|
||||
usersFile, err := ioutil.TempFile("", "auth-users")
|
||||
usersFile, err := os.CreateTemp(t.TempDir(), "auth-users")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(usersFile.Name())
|
||||
|
||||
_, err = usersFile.Write([]byte(test.userFileContent))
|
||||
require.NoError(t, err)
|
||||
|
@ -250,7 +249,7 @@ func TestBasicAuthUsersFromFile(t *testing.T) {
|
|||
require.Equal(t, http.StatusOK, res.StatusCode, "Cannot authenticate user "+userName)
|
||||
|
||||
var body []byte
|
||||
body, err = ioutil.ReadAll(res.Body)
|
||||
body, err = io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
@ -270,7 +269,7 @@ func TestBasicAuthUsersFromFile(t *testing.T) {
|
|||
require.Equal(t, `Basic realm="`+test.realm+`"`, res.Header.Get("WWW-Authenticate"))
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -3,7 +3,7 @@ package auth
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
|
@ -93,9 +93,8 @@ func TestDigestAuthUsersFromFile(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
// Creates the temporary configuration file with the users
|
||||
usersFile, err := ioutil.TempFile("", "auth-users")
|
||||
usersFile, err := os.CreateTemp(t.TempDir(), "auth-users")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(usersFile.Name())
|
||||
|
||||
_, err = usersFile.Write([]byte(test.userFileContent))
|
||||
require.NoError(t, err)
|
||||
|
@ -127,7 +126,7 @@ func TestDigestAuthUsersFromFile(t *testing.T) {
|
|||
require.Equal(t, http.StatusOK, res.StatusCode, "Cannot authenticate user "+userName)
|
||||
|
||||
var body []byte
|
||||
body, err = ioutil.ReadAll(res.Body)
|
||||
body, err = io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
@ -145,7 +144,7 @@ func TestDigestAuthUsersFromFile(t *testing.T) {
|
|||
require.Equal(t, http.StatusUnauthorized, res.StatusCode)
|
||||
|
||||
var body []byte
|
||||
body, err = ioutil.ReadAll(res.Body)
|
||||
body, err = io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
@ -126,7 +126,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
body, readError := ioutil.ReadAll(forwardResponse.Body)
|
||||
body, readError := io.ReadAll(forwardResponse.Body)
|
||||
if readError != nil {
|
||||
logMessage := fmt.Sprintf("Error reading body %s. Cause: %s", fa.address, readError)
|
||||
logger.Debug(logMessage)
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
@ -44,7 +43,7 @@ func TestForwardAuthFail(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
@ -92,7 +91,7 @@ func TestForwardAuthSuccess(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
@ -134,7 +133,7 @@ func TestForwardAuthRedirect(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, "http://example.com/redirect-test", location.String())
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
@ -187,7 +186,7 @@ func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, "http://example.com/redirect-test", location.String(), "they should be equal")
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, string(body), "there should be something in the body")
|
||||
}
|
||||
|
@ -238,7 +237,7 @@ func TestForwardAuthFailResponseHeaders(t *testing.T) {
|
|||
assert.Equal(t, value, res.Header[key])
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
@ -489,5 +488,5 @@ type mockBackend struct {
|
|||
}
|
||||
|
||||
func (b *mockBackend) Setup(componentName string) (opentracing.Tracer, io.Closer, error) {
|
||||
return b.Tracer, ioutil.NopCloser(nil), nil
|
||||
return b.Tracer, io.NopCloser(nil), nil
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package compress
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
@ -205,7 +205,7 @@ func TestIntegrationShouldNotCompress(t *testing.T) {
|
|||
assert.Equal(t, gzipValue, resp.Header.Get(contentEncodingHeader))
|
||||
assert.Equal(t, acceptEncodingHeader, resp.Header.Get(varyHeader))
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, fakeCompressedBody, body)
|
||||
})
|
||||
|
@ -287,7 +287,7 @@ func TestIntegrationShouldCompress(t *testing.T) {
|
|||
assert.Equal(t, gzipValue, resp.Header.Get(contentEncodingHeader))
|
||||
assert.Equal(t, acceptEncodingHeader, resp.Header.Get(varyHeader))
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
if assert.ObjectsAreEqualValues(body, fakeBody) {
|
||||
assert.Fail(t, "expected a compressed body", "got %v", body)
|
||||
|
|
|
@ -92,39 +92,42 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
// check the recorder code against the configured http status code ranges
|
||||
code := catcher.getCode()
|
||||
for _, block := range c.httpCodeRanges {
|
||||
if code >= block[0] && code <= block[1] {
|
||||
logger.Debugf("Caught HTTP Status Code %d, returning error page", code)
|
||||
if code < block[0] || code > block[1] {
|
||||
continue
|
||||
}
|
||||
|
||||
var query string
|
||||
if len(c.backendQuery) > 0 {
|
||||
query = "/" + strings.TrimPrefix(c.backendQuery, "/")
|
||||
query = strings.ReplaceAll(query, "{status}", strconv.Itoa(code))
|
||||
}
|
||||
logger.Debugf("Caught HTTP Status Code %d, returning error page", code)
|
||||
|
||||
pageReq, err := newRequest(backendURL + query)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
rw.WriteHeader(code)
|
||||
_, err = fmt.Fprint(rw, http.StatusText(code))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
var query string
|
||||
if len(c.backendQuery) > 0 {
|
||||
query = "/" + strings.TrimPrefix(c.backendQuery, "/")
|
||||
query = strings.ReplaceAll(query, "{status}", strconv.Itoa(code))
|
||||
}
|
||||
|
||||
recorderErrorPage := newResponseRecorder(ctx, rw)
|
||||
utils.CopyHeaders(pageReq.Header, req.Header)
|
||||
|
||||
c.backendHandler.ServeHTTP(recorderErrorPage, pageReq.WithContext(req.Context()))
|
||||
|
||||
utils.CopyHeaders(rw.Header(), recorderErrorPage.Header())
|
||||
pageReq, err := newRequest(backendURL + query)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
rw.WriteHeader(code)
|
||||
|
||||
if _, err = rw.Write(recorderErrorPage.GetBody().Bytes()); err != nil {
|
||||
logger.Error(err)
|
||||
_, err = fmt.Fprint(rw, http.StatusText(code))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
recorderErrorPage := newResponseRecorder(ctx, rw)
|
||||
utils.CopyHeaders(pageReq.Header, req.Header)
|
||||
|
||||
c.backendHandler.ServeHTTP(recorderErrorPage, pageReq.WithContext(req.Context()))
|
||||
|
||||
utils.CopyHeaders(rw.Header(), recorderErrorPage.Header())
|
||||
rw.WriteHeader(code)
|
||||
|
||||
if _, err = rw.Write(recorderErrorPage.GetBody().Bytes()); err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -279,10 +280,12 @@ func TestRateLimit(t *testing.T) {
|
|||
// actual default value
|
||||
burst = 1
|
||||
}
|
||||
|
||||
period := time.Duration(test.config.Period)
|
||||
if period == 0 {
|
||||
period = time.Second
|
||||
}
|
||||
|
||||
if test.config.Average == 0 {
|
||||
if reqCount < 75*test.incomingLoad/100 {
|
||||
t.Fatalf("we (arbitrarily) expect at least 75%% of the requests to go through with no rate limiting, and yet only %d/%d went through", reqCount, test.incomingLoad)
|
||||
|
@ -297,14 +300,18 @@ func TestRateLimit(t *testing.T) {
|
|||
// we take into account the configured burst,
|
||||
// because it also helps absorbing non-bursty traffic.
|
||||
rate := float64(test.config.Average) / float64(period)
|
||||
|
||||
wantCount := int(int64(rate*float64(test.loadDuration)) + burst)
|
||||
|
||||
// Allow for a 2% leeway
|
||||
maxCount := wantCount * 102 / 100
|
||||
|
||||
// With very high CPU loads,
|
||||
// we can expect some extra delay in addition to the rate limiting we already do,
|
||||
// so we allow for some extra leeway there.
|
||||
// Feel free to adjust wrt to the load on e.g. the CI.
|
||||
minCount := wantCount * 95 / 100
|
||||
minCount := computeMinCount(wantCount)
|
||||
|
||||
if reqCount < minCount {
|
||||
t.Fatalf("rate was slower than expected: %d requests (wanted > %d) in %v", reqCount, minCount, elapsed)
|
||||
}
|
||||
|
@ -314,3 +321,11 @@ func TestRateLimit(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func computeMinCount(wantCount int) int {
|
||||
if os.Getenv("CI") != "" {
|
||||
return wantCount * 60 / 100
|
||||
}
|
||||
|
||||
return wantCount * 95 / 100
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -73,12 +73,12 @@ func (r *retry) GetTracingInformation() (string, ext.SpanKindEnum) {
|
|||
}
|
||||
|
||||
func (r *retry) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
// if we might make multiple attempts, swap the body for an ioutil.NopCloser
|
||||
// if we might make multiple attempts, swap the body for an io.NopCloser
|
||||
// cf https://github.com/traefik/traefik/issues/1008
|
||||
if r.attempts > 1 {
|
||||
body := req.Body
|
||||
defer body.Close()
|
||||
req.Body = ioutil.NopCloser(body)
|
||||
req.Body = io.NopCloser(body)
|
||||
}
|
||||
|
||||
attempts := 1
|
||||
|
|
|
@ -3,10 +3,10 @@ package retry
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httptrace"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -16,10 +16,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/emptybackendhandler"
|
||||
"github.com/traefik/traefik/v2/pkg/testhelpers"
|
||||
"github.com/vulcand/oxy/forward"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
)
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
|
@ -32,14 +29,14 @@ func TestRetry(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
desc: "no retry on success",
|
||||
config: dynamic.Retry{Attempts: 1},
|
||||
config: dynamic.Retry{Attempts: 5},
|
||||
wantRetryAttempts: 0,
|
||||
wantResponseStatus: http.StatusOK,
|
||||
amountFaultyEndpoints: 0,
|
||||
},
|
||||
{
|
||||
desc: "no retry on success with backoff",
|
||||
config: dynamic.Retry{Attempts: 1, InitialInterval: ptypes.Duration(time.Microsecond * 50)},
|
||||
config: dynamic.Retry{Attempts: 5, InitialInterval: ptypes.Duration(time.Microsecond * 50)},
|
||||
wantRetryAttempts: 0,
|
||||
wantResponseStatus: http.StatusOK,
|
||||
amountFaultyEndpoints: 0,
|
||||
|
@ -102,42 +99,28 @@ func TestRetry(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
backendServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err := rw.Write([]byte("OK"))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}))
|
||||
|
||||
forwarder, err := forward.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
loadBalancer, err := roundrobin.New(forwarder)
|
||||
require.NoError(t, err)
|
||||
retryAttemps := 0
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
retryAttemps++
|
||||
|
||||
// out of range port
|
||||
basePort := 1133444
|
||||
for i := 0; i < test.amountFaultyEndpoints; i++ {
|
||||
// 192.0.2.0 is a non-routable IP for testing purposes.
|
||||
// See: https://stackoverflow.com/questions/528538/non-routable-ip-address/18436928#18436928
|
||||
// We only use the port specification here because the URL is used as identifier
|
||||
// in the load balancer and using the exact same URL would not add a new server.
|
||||
err = loadBalancer.UpsertServer(testhelpers.MustParseURL("http://192.0.2.0:" + strconv.Itoa(basePort+i)))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if retryAttemps > test.amountFaultyEndpoints {
|
||||
// calls WroteHeaders on httptrace.
|
||||
_ = r.Write(io.Discard)
|
||||
|
||||
// add the functioning server to the end of the load balancer list
|
||||
err = loadBalancer.UpsertServer(testhelpers.MustParseURL(backendServer.URL))
|
||||
require.NoError(t, err)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusBadGateway)
|
||||
})
|
||||
|
||||
retryListener := &countingRetryListener{}
|
||||
retry, err := New(context.Background(), loadBalancer, test.config, retryListener, "traefikTest")
|
||||
retry, err := New(context.Background(), next, test.config, retryListener, "traefikTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
@ -152,15 +135,9 @@ func TestRetry(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRetryEmptyServerList(t *testing.T) {
|
||||
forwarder, err := forward.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
loadBalancer, err := roundrobin.New(forwarder)
|
||||
require.NoError(t, err)
|
||||
|
||||
// The EmptyBackend middleware ensures that there is a 503
|
||||
// response status set when there is no backend server in the pool.
|
||||
next := emptybackendhandler.New(loadBalancer)
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.WriteHeader(http.StatusServiceUnavailable)
|
||||
})
|
||||
|
||||
retryListener := &countingRetryListener{}
|
||||
retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 3}, retryListener, "traefikTest")
|
||||
|
@ -293,47 +270,29 @@ func TestRetryWebsocket(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
forwarder, err := forward.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating forwarder: %v", err)
|
||||
}
|
||||
|
||||
backendServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
upgrader := websocket.Upgrader{}
|
||||
_, err := upgrader.Upgrade(rw, req, nil)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}))
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
loadBalancer, err := roundrobin.New(forwarder)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating load balancer: %v", err)
|
||||
}
|
||||
retryAttemps := 0
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
retryAttemps++
|
||||
|
||||
// out of range port
|
||||
basePort := 1133444
|
||||
for i := 0; i < test.amountFaultyEndpoints; i++ {
|
||||
// 192.0.2.0 is a non-routable IP for testing purposes.
|
||||
// See: https://stackoverflow.com/questions/528538/non-routable-ip-address/18436928#18436928
|
||||
// We only use the port specification here because the URL is used as identifier
|
||||
// in the load balancer and using the exact same URL would not add a new server.
|
||||
_ = loadBalancer.UpsertServer(testhelpers.MustParseURL("http://192.0.2.0:" + strconv.Itoa(basePort+i)))
|
||||
}
|
||||
if retryAttemps > test.amountFaultyEndpoints {
|
||||
upgrader := websocket.Upgrader{}
|
||||
_, err := upgrader.Upgrade(rw, r, nil)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// add the functioning server to the end of the load balancer list
|
||||
err = loadBalancer.UpsertServer(testhelpers.MustParseURL(backendServer.URL))
|
||||
if err != nil {
|
||||
t.Fatalf("Fail to upsert server: %v", err)
|
||||
}
|
||||
rw.WriteHeader(http.StatusBadGateway)
|
||||
})
|
||||
|
||||
retryListener := &countingRetryListener{}
|
||||
retryH, err := New(context.Background(), loadBalancer, dynamic.Retry{Attempts: test.maxRequestAttempts}, retryListener, "traefikTest")
|
||||
retryH, err := New(context.Background(), next, dynamic.Retry{Attempts: test.maxRequestAttempts}, retryListener, "traefikTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
retryServer := httptest.NewServer(retryH)
|
||||
|
|
|
@ -27,12 +27,12 @@ func (n MockTracer) Extract(format, carrier interface{}) (opentracing.SpanContex
|
|||
return nil, opentracing.ErrSpanContextNotFound
|
||||
}
|
||||
|
||||
// MockSpanContext.
|
||||
// MockSpanContext a span context mock.
|
||||
type MockSpanContext struct{}
|
||||
|
||||
func (n MockSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {}
|
||||
|
||||
// MockSpan.
|
||||
// MockSpan a span mock.
|
||||
type MockSpan struct {
|
||||
OpName string
|
||||
Tags map[string]interface{}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue