Ultimate Access log filter
This commit is contained in:
parent
f99363674b
commit
8d468925d3
24 changed files with 1722 additions and 683 deletions
185
types/logs.go
Normal file
185
types/logs.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// AccessLogKeep is the keep string value
|
||||
AccessLogKeep = "keep"
|
||||
// AccessLogDrop is the drop string value
|
||||
AccessLogDrop = "drop"
|
||||
// AccessLogRedact is the redact string value
|
||||
AccessLogRedact = "redact"
|
||||
)
|
||||
|
||||
// TraefikLog holds the configuration settings for the traefik logger.
|
||||
type TraefikLog struct {
|
||||
FilePath string `json:"file,omitempty" description:"Traefik log file path. Stdout is used when omitted or empty"`
|
||||
Format string `json:"format,omitempty" description:"Traefik log format: json | common"`
|
||||
}
|
||||
|
||||
// AccessLog holds the configuration settings for the access logger (middlewares/accesslog).
|
||||
type AccessLog struct {
|
||||
FilePath string `json:"file,omitempty" description:"Access log file path. Stdout is used when omitted or empty" export:"true"`
|
||||
Format string `json:"format,omitempty" description:"Access log format: json | common" export:"true"`
|
||||
Filters *AccessLogFilters `json:"filters,omitempty" description:"Access log filters, used to keep only specific access logs" export:"true"`
|
||||
Fields *AccessLogFields `json:"fields,omitempty" description:"AccessLogFields" export:"true"`
|
||||
}
|
||||
|
||||
// StatusCodes holds status codes ranges to filter access log
|
||||
type StatusCodes []string
|
||||
|
||||
// AccessLogFilters holds filters configuration
|
||||
type AccessLogFilters struct {
|
||||
StatusCodes StatusCodes `json:"statusCodes,omitempty" description:"Keep only specific ranges of HTTP Status codes" export:"true"`
|
||||
}
|
||||
|
||||
// FieldNames holds maps of fields with specific mode
|
||||
type FieldNames map[string]string
|
||||
|
||||
// AccessLogFields holds configuration for access log fields
|
||||
type AccessLogFields struct {
|
||||
DefaultMode string `json:"defaultMode,omitempty" description:"Default mode for fields: keep | drop" export:"true"`
|
||||
Names FieldNames `json:"names,omitempty" description:"Override mode for fields" export:"true"`
|
||||
Headers *FieldHeaders `json:"headers,omitempty" description:"Headers to keep, drop or redact" export:"true"`
|
||||
}
|
||||
|
||||
// FieldHeaderNames holds maps of fields with specific mode
|
||||
type FieldHeaderNames map[string]string
|
||||
|
||||
// FieldHeaders holds configuration for access log headers
|
||||
type FieldHeaders struct {
|
||||
DefaultMode string `json:"defaultMode,omitempty" description:"Default mode for fields: keep | drop | redact" export:"true"`
|
||||
Names FieldHeaderNames `json:"names,omitempty" description:"Override mode for headers" export:"true"`
|
||||
}
|
||||
|
||||
// Set adds strings elem into the the parser
|
||||
// it splits str on , and ;
|
||||
func (s *StatusCodes) Set(str string) error {
|
||||
fargs := func(c rune) bool {
|
||||
return c == ',' || c == ';'
|
||||
}
|
||||
// get function
|
||||
slice := strings.FieldsFunc(str, fargs)
|
||||
*s = append(*s, slice...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get StatusCodes
|
||||
func (s *StatusCodes) Get() interface{} { return *s }
|
||||
|
||||
// String return slice in a string
|
||||
func (s *StatusCodes) String() string { return fmt.Sprintf("%v", *s) }
|
||||
|
||||
// SetValue sets StatusCodes into the parser
|
||||
func (s *StatusCodes) SetValue(val interface{}) {
|
||||
*s = val.(StatusCodes)
|
||||
}
|
||||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
// The String method's output will be used in diagnostics.
|
||||
func (f *FieldNames) String() string {
|
||||
return fmt.Sprintf("%+v", *f)
|
||||
}
|
||||
|
||||
// Get return the FieldNames map
|
||||
func (f *FieldNames) Get() interface{} {
|
||||
return *f
|
||||
}
|
||||
|
||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a space-separated list, so we split it.
|
||||
func (f *FieldNames) Set(value string) error {
|
||||
fields := strings.Fields(value)
|
||||
|
||||
for _, field := range fields {
|
||||
n := strings.SplitN(field, "=", 2)
|
||||
if len(n) == 2 {
|
||||
(*f)[n[0]] = n[1]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetValue sets the FieldNames map with val
|
||||
func (f *FieldNames) SetValue(val interface{}) {
|
||||
*f = val.(FieldNames)
|
||||
}
|
||||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
// The String method's output will be used in diagnostics.
|
||||
func (f *FieldHeaderNames) String() string {
|
||||
return fmt.Sprintf("%+v", *f)
|
||||
}
|
||||
|
||||
// Get return the FieldHeaderNames map
|
||||
func (f *FieldHeaderNames) Get() interface{} {
|
||||
return *f
|
||||
}
|
||||
|
||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a space-separated list, so we split it.
|
||||
func (f *FieldHeaderNames) Set(value string) error {
|
||||
fields := strings.Fields(value)
|
||||
|
||||
for _, field := range fields {
|
||||
n := strings.SplitN(field, "=", 2)
|
||||
(*f)[n[0]] = n[1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetValue sets the FieldHeaderNames map with val
|
||||
func (f *FieldHeaderNames) SetValue(val interface{}) {
|
||||
*f = val.(FieldHeaderNames)
|
||||
}
|
||||
|
||||
// Keep check if the field need to be kept or dropped
|
||||
func (f *AccessLogFields) Keep(field string) bool {
|
||||
defaultKeep := true
|
||||
if f != nil {
|
||||
defaultKeep = checkFieldValue(f.DefaultMode, defaultKeep)
|
||||
|
||||
if v, ok := f.Names[field]; ok {
|
||||
return checkFieldValue(v, defaultKeep)
|
||||
}
|
||||
}
|
||||
return defaultKeep
|
||||
}
|
||||
|
||||
func checkFieldValue(value string, defaultKeep bool) bool {
|
||||
switch value {
|
||||
case AccessLogKeep:
|
||||
return true
|
||||
case AccessLogDrop:
|
||||
return false
|
||||
default:
|
||||
return defaultKeep
|
||||
}
|
||||
}
|
||||
|
||||
// KeepHeader checks if the headers need to be kept, dropped or redacted and returns the status
|
||||
func (f *AccessLogFields) KeepHeader(header string) string {
|
||||
defaultValue := AccessLogKeep
|
||||
if f != nil && f.Headers != nil {
|
||||
defaultValue = checkFieldHeaderValue(f.Headers.DefaultMode, defaultValue)
|
||||
|
||||
if v, ok := f.Headers.Names[header]; ok {
|
||||
return checkFieldHeaderValue(v, defaultValue)
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func checkFieldHeaderValue(value string, defaultValue string) string {
|
||||
if value == AccessLogKeep || value == AccessLogDrop || value == AccessLogRedact {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
411
types/logs_test.go
Normal file
411
types/logs_test.go
Normal file
|
@ -0,0 +1,411 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStatusCodesSet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
value string
|
||||
expected StatusCodes
|
||||
}{
|
||||
{
|
||||
desc: "One value should return StatusCodes of size 1",
|
||||
value: "200",
|
||||
expected: StatusCodes{"200"},
|
||||
},
|
||||
{
|
||||
desc: "Two values separated by comma should return StatusCodes of size 2",
|
||||
value: "200,400",
|
||||
expected: StatusCodes{"200", "400"},
|
||||
},
|
||||
{
|
||||
desc: "Two values separated by semicolon should return StatusCodes of size 2",
|
||||
value: "200;400",
|
||||
expected: StatusCodes{"200", "400"},
|
||||
},
|
||||
{
|
||||
desc: "Three values separated by comma and semicolon should return StatusCodes of size 3",
|
||||
value: "200,400;500",
|
||||
expected: StatusCodes{"200", "400", "500"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var statusCodes StatusCodes
|
||||
err := statusCodes.Set(test.value)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, test.expected, statusCodes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCodesGet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values StatusCodes
|
||||
expected StatusCodes
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: StatusCodes{"200"},
|
||||
expected: StatusCodes{"200"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 2 values",
|
||||
values: StatusCodes{"200", "400"},
|
||||
expected: StatusCodes{"200", "400"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 3 values",
|
||||
values: StatusCodes{"200", "400", "500"},
|
||||
expected: StatusCodes{"200", "400", "500"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := test.values.Get()
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCodesString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values StatusCodes
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: StatusCodes{"200"},
|
||||
expected: "[200]",
|
||||
},
|
||||
{
|
||||
desc: "Should return 2 values",
|
||||
values: StatusCodes{"200", "400"},
|
||||
expected: "[200 400]",
|
||||
},
|
||||
{
|
||||
desc: "Should return 3 values",
|
||||
values: StatusCodes{"200", "400", "500"},
|
||||
expected: "[200 400 500]",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := test.values.String()
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCodesSetValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values StatusCodes
|
||||
expected StatusCodes
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: StatusCodes{"200"},
|
||||
expected: StatusCodes{"200"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 2 values",
|
||||
values: StatusCodes{"200", "400"},
|
||||
expected: StatusCodes{"200", "400"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 3 values",
|
||||
values: StatusCodes{"200", "400", "500"},
|
||||
expected: StatusCodes{"200", "400", "500"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var slice StatusCodes
|
||||
slice.SetValue(test.values)
|
||||
assert.Equal(t, test.expected, slice)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsNamesSet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
value string
|
||||
expected *FieldNames
|
||||
}{
|
||||
{
|
||||
desc: "One value should return FieldNames of size 1",
|
||||
value: "field-1=foo",
|
||||
expected: &FieldNames{
|
||||
"field-1": "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Two values separated by space should return FieldNames of size 2",
|
||||
value: "field-1=foo field-2=bar",
|
||||
expected: &FieldNames{
|
||||
"field-1": "foo",
|
||||
"field-2": "bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fieldsNames := &FieldNames{}
|
||||
err := fieldsNames.Set(test.value)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, fieldsNames)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsNamesGet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values FieldNames
|
||||
expected FieldNames
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: FieldNames{"field-1": "foo"},
|
||||
expected: FieldNames{"field-1": "foo"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 2 values",
|
||||
values: FieldNames{"field-1": "foo", "field-2": "bar"},
|
||||
expected: FieldNames{"field-1": "foo", "field-2": "bar"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 3 values",
|
||||
values: FieldNames{"field-1": "foo", "field-2": "bar", "field-3": "powpow"},
|
||||
expected: FieldNames{"field-1": "foo", "field-2": "bar", "field-3": "powpow"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := test.values.Get()
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsNamesString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values FieldNames
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: FieldNames{"field-1": "foo"},
|
||||
expected: "map[field-1:foo]",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := test.values.String()
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsNamesSetValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values FieldNames
|
||||
expected *FieldNames
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: FieldNames{"field-1": "foo"},
|
||||
expected: &FieldNames{"field-1": "foo"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 2 values",
|
||||
values: FieldNames{"field-1": "foo", "field-2": "bar"},
|
||||
expected: &FieldNames{"field-1": "foo", "field-2": "bar"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 3 values",
|
||||
values: FieldNames{"field-1": "foo", "field-2": "bar", "field-3": "powpow"},
|
||||
expected: &FieldNames{"field-1": "foo", "field-2": "bar", "field-3": "powpow"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fieldsNames := &FieldNames{}
|
||||
fieldsNames.SetValue(test.values)
|
||||
assert.Equal(t, test.expected, fieldsNames)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsHeadersNamesSet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
value string
|
||||
expected *FieldHeaderNames
|
||||
}{
|
||||
{
|
||||
desc: "One value should return FieldNames of size 1",
|
||||
value: "X-HEADER-1=foo",
|
||||
expected: &FieldHeaderNames{
|
||||
"X-HEADER-1": "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Two values separated by space should return FieldNames of size 2",
|
||||
value: "X-HEADER-1=foo X-HEADER-2=bar",
|
||||
expected: &FieldHeaderNames{
|
||||
"X-HEADER-1": "foo",
|
||||
"X-HEADER-2": "bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
headersNames := &FieldHeaderNames{}
|
||||
err := headersNames.Set(test.value)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected, headersNames)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsHeadersNamesGet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values FieldHeaderNames
|
||||
expected FieldHeaderNames
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: FieldHeaderNames{"X-HEADER-1": "foo"},
|
||||
expected: FieldHeaderNames{"X-HEADER-1": "foo"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 2 values",
|
||||
values: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar"},
|
||||
expected: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 3 values",
|
||||
values: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar", "X-HEADER-3": "powpow"},
|
||||
expected: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar", "X-HEADER-3": "powpow"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := test.values.Get()
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsHeadersNamesString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values FieldHeaderNames
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: FieldHeaderNames{"X-HEADER-1": "foo"},
|
||||
expected: "map[X-HEADER-1:foo]",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := test.values.String()
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsHeadersNamesSetValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
values FieldHeaderNames
|
||||
expected *FieldHeaderNames
|
||||
}{
|
||||
{
|
||||
desc: "Should return 1 value",
|
||||
values: FieldHeaderNames{"X-HEADER-1": "foo"},
|
||||
expected: &FieldHeaderNames{"X-HEADER-1": "foo"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 2 values",
|
||||
values: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar"},
|
||||
expected: &FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar"},
|
||||
},
|
||||
{
|
||||
desc: "Should return 3 values",
|
||||
values: FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar", "X-HEADER-3": "powpow"},
|
||||
expected: &FieldHeaderNames{"X-HEADER-1": "foo", "X-HEADER-2": "bar", "X-HEADER-3": "powpow"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
headersNames := &FieldHeaderNames{}
|
||||
headersNames.SetValue(test.values)
|
||||
assert.Equal(t, test.expected, headersNames)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -461,18 +461,6 @@ func (b *Buckets) SetValue(val interface{}) {
|
|||
*b = val.(Buckets)
|
||||
}
|
||||
|
||||
// TraefikLog holds the configuration settings for the traefik logger.
|
||||
type TraefikLog struct {
|
||||
FilePath string `json:"file,omitempty" description:"Traefik log file path. Stdout is used when omitted or empty"`
|
||||
Format string `json:"format,omitempty" description:"Traefik log format: json | common"`
|
||||
}
|
||||
|
||||
// AccessLog holds the configuration settings for the access logger (middlewares/accesslog).
|
||||
type AccessLog struct {
|
||||
FilePath string `json:"file,omitempty" description:"Access log file path. Stdout is used when omitted or empty" export:"true"`
|
||||
Format string `json:"format,omitempty" description:"Access log format: json | common" export:"true"`
|
||||
}
|
||||
|
||||
// ClientTLS holds TLS specific configurations as client
|
||||
// CA, Cert and Key can be either path or file contents
|
||||
type ClientTLS struct {
|
||||
|
@ -497,7 +485,7 @@ func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
|
|||
if _, errCA := os.Stat(clientTLS.CA); errCA == nil {
|
||||
ca, err = ioutil.ReadFile(clientTLS.CA)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read CA. %s", err)
|
||||
return nil, fmt.Errorf("failed to read CA. %s", err)
|
||||
}
|
||||
} else {
|
||||
ca = []byte(clientTLS.CA)
|
||||
|
@ -522,7 +510,7 @@ func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
|
|||
if errKeyIsFile == nil {
|
||||
cert, err = tls.LoadX509KeyPair(clientTLS.Cert, clientTLS.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to load TLS keypair: %v", err)
|
||||
return nil, fmt.Errorf("failed to load TLS keypair: %v", err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("tls cert is a file, but tls key is not")
|
||||
|
@ -531,11 +519,11 @@ func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
|
|||
if errKeyIsFile != nil {
|
||||
cert, err = tls.X509KeyPair([]byte(clientTLS.Cert), []byte(clientTLS.Key))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to load TLS keypair: %v", err)
|
||||
return nil, fmt.Errorf("failed to load TLS keypair: %v", err)
|
||||
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("tls key is a file, but tls cert is not")
|
||||
return nil, fmt.Errorf("TLS key is a file, but tls cert is not")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -548,3 +536,30 @@ func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
|
|||
}
|
||||
return TLSConfig, nil
|
||||
}
|
||||
|
||||
// HTTPCodeRanges holds HTTP code ranges
|
||||
type HTTPCodeRanges [][2]int
|
||||
|
||||
// NewHTTPCodeRanges create a new NewHTTPCodeRanges from a given []string].
|
||||
// Break out the http status code ranges into a low int and high int
|
||||
// for ease of use at runtime
|
||||
func NewHTTPCodeRanges(strBlocks []string) (HTTPCodeRanges, error) {
|
||||
var blocks HTTPCodeRanges
|
||||
for _, block := range strBlocks {
|
||||
codes := strings.Split(block, "-")
|
||||
//if only a single HTTP code was configured, assume the best and create the correct configuration on the user's behalf
|
||||
if len(codes) == 1 {
|
||||
codes = append(codes, codes[0])
|
||||
}
|
||||
lowCode, err := strconv.Atoi(codes[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
highCode, err := strconv.Atoi(codes[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blocks = append(blocks, [2]int{lowCode, highCode})
|
||||
}
|
||||
return blocks, nil
|
||||
}
|
||||
|
|
|
@ -35,3 +35,61 @@ func TestHeaders_ShouldReturnTrueWhenHasSecureHeadersDefined(t *testing.T) {
|
|||
|
||||
assert.True(t, headers.HasSecureHeadersDefined())
|
||||
}
|
||||
|
||||
func TestNewHTTPCodeRanges(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
strBlocks []string
|
||||
expected HTTPCodeRanges
|
||||
errExpected bool
|
||||
}{
|
||||
{
|
||||
desc: "Should return 2 code range",
|
||||
strBlocks: []string{
|
||||
"200-500",
|
||||
"502",
|
||||
},
|
||||
expected: HTTPCodeRanges{[2]int{200, 500}, [2]int{502, 502}},
|
||||
errExpected: false,
|
||||
},
|
||||
{
|
||||
desc: "Should return 2 code range",
|
||||
strBlocks: []string{
|
||||
"200-500",
|
||||
"205",
|
||||
},
|
||||
expected: HTTPCodeRanges{[2]int{200, 500}, [2]int{205, 205}},
|
||||
errExpected: false,
|
||||
},
|
||||
{
|
||||
desc: "invalid code range",
|
||||
strBlocks: []string{
|
||||
"200-500",
|
||||
"aaa",
|
||||
},
|
||||
expected: nil,
|
||||
errExpected: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid code range nil",
|
||||
strBlocks: nil,
|
||||
expected: nil,
|
||||
errExpected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := NewHTTPCodeRanges(test.strBlocks)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
if test.errExpected {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue