TUN-5801: Add custom wrapper for OriginConfig for JSON serde
This commit is contained in:
parent
9552bb7bc7
commit
5352b3cf04
|
@ -1,12 +1,14 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
homedir "github.com/mitchellh/go-homedir"
|
homedir "github.com/mitchellh/go-homedir"
|
||||||
|
@ -190,17 +192,17 @@ type UnvalidatedIngressRule struct {
|
||||||
// - To specify a time.Duration in json, use int64 of the nanoseconds
|
// - To specify a time.Duration in json, use int64 of the nanoseconds
|
||||||
type OriginRequestConfig struct {
|
type OriginRequestConfig struct {
|
||||||
// HTTP proxy timeout for establishing a new connection
|
// HTTP proxy timeout for establishing a new connection
|
||||||
ConnectTimeout *time.Duration `yaml:"connectTimeout" json:"connectTimeout"`
|
ConnectTimeout *CustomDuration `yaml:"connectTimeout" json:"connectTimeout"`
|
||||||
// HTTP proxy timeout for completing a TLS handshake
|
// HTTP proxy timeout for completing a TLS handshake
|
||||||
TLSTimeout *time.Duration `yaml:"tlsTimeout" json:"tlsTimeout"`
|
TLSTimeout *CustomDuration `yaml:"tlsTimeout" json:"tlsTimeout"`
|
||||||
// HTTP proxy TCP keepalive duration
|
// HTTP proxy TCP keepalive duration
|
||||||
TCPKeepAlive *time.Duration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"`
|
TCPKeepAlive *CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"`
|
||||||
// HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback
|
// HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback
|
||||||
NoHappyEyeballs *bool `yaml:"noHappyEyeballs" json:"noHappyEyeballs"`
|
NoHappyEyeballs *bool `yaml:"noHappyEyeballs" json:"noHappyEyeballs"`
|
||||||
// HTTP proxy maximum keepalive connection pool size
|
// HTTP proxy maximum keepalive connection pool size
|
||||||
KeepAliveConnections *int `yaml:"keepAliveConnections" json:"keepAliveConnections"`
|
KeepAliveConnections *int `yaml:"keepAliveConnections" json:"keepAliveConnections"`
|
||||||
// HTTP proxy timeout for closing an idle connection
|
// HTTP proxy timeout for closing an idle connection
|
||||||
KeepAliveTimeout *time.Duration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"`
|
KeepAliveTimeout *CustomDuration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"`
|
||||||
// Sets the HTTP Host header for the local webserver.
|
// Sets the HTTP Host header for the local webserver.
|
||||||
HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader"`
|
HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader"`
|
||||||
// Hostname on the origin server certificate.
|
// Hostname on the origin server certificate.
|
||||||
|
@ -399,3 +401,34 @@ func ReadConfigFile(c *cli.Context, log *zerolog.Logger) (settings *configFileSe
|
||||||
|
|
||||||
return &configuration, warnings, nil
|
return &configuration, warnings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A CustomDuration is a Duration that has custom serialization for JSON.
|
||||||
|
// JSON in Javascript assumes that int fields are 32 bits and Duration fields are deserialized assuming that numbers
|
||||||
|
// are in nanoseconds, which in 32bit integers limits to just 2 seconds.
|
||||||
|
// This type assumes that when serializing/deserializing from JSON, that the number is in seconds, while it maintains
|
||||||
|
// the YAML serde assumptions.
|
||||||
|
type CustomDuration struct {
|
||||||
|
time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomDuration) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(s.Duration.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomDuration) UnmarshalJSON(data []byte) error {
|
||||||
|
seconds, err := strconv.ParseInt(string(data), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Duration = time.Duration(seconds * int64(time.Second))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomDuration) MarshalYAML() (interface{}, error) {
|
||||||
|
return s.Duration.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomDuration) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
return unmarshal(&s.Duration)
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,14 +111,13 @@ counters:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalOriginRequestConfig(t *testing.T) {
|
var rawConfig = []byte(`
|
||||||
raw := []byte(`
|
|
||||||
{
|
{
|
||||||
"connectTimeout": 10000000000,
|
"connectTimeout": 10,
|
||||||
"tlsTimeout": 30000000000,
|
"tlsTimeout": 30,
|
||||||
"tcpKeepAlive": 30000000000,
|
"tcpKeepAlive": 30,
|
||||||
"noHappyEyeballs": true,
|
"noHappyEyeballs": true,
|
||||||
"keepAliveTimeout": 60000000000,
|
"keepAliveTimeout": 60,
|
||||||
"keepAliveConnections": 10,
|
"keepAliveConnections": 10,
|
||||||
"httpHostHeader": "app.tunnel.com",
|
"httpHostHeader": "app.tunnel.com",
|
||||||
"originServerName": "app.tunnel.com",
|
"originServerName": "app.tunnel.com",
|
||||||
|
@ -142,13 +142,41 @@ func TestUnmarshalOriginRequestConfig(t *testing.T) {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalOriginRequest(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
marshalFunc func(in interface{}) (out []byte, err error)
|
||||||
|
unMarshalFunc func(in []byte, out interface{}) (err error)
|
||||||
|
baseUnit time.Duration
|
||||||
|
}{
|
||||||
|
{"json", json.Marshal, json.Unmarshal, time.Second},
|
||||||
|
{"yaml", yaml.Marshal, yaml.Unmarshal, time.Nanosecond},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assertConfig(t, tc.marshalFunc, tc.unMarshalFunc, tc.baseUnit)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertConfig(
|
||||||
|
t *testing.T,
|
||||||
|
marshalFunc func(in interface{}) (out []byte, err error),
|
||||||
|
unMarshalFunc func(in []byte, out interface{}) (err error),
|
||||||
|
baseUnit time.Duration,
|
||||||
|
) {
|
||||||
var config OriginRequestConfig
|
var config OriginRequestConfig
|
||||||
assert.NoError(t, json.Unmarshal(raw, &config))
|
var config2 OriginRequestConfig
|
||||||
assert.Equal(t, time.Second*10, *config.ConnectTimeout)
|
|
||||||
assert.Equal(t, time.Second*30, *config.TLSTimeout)
|
assert.NoError(t, unMarshalFunc(rawConfig, &config))
|
||||||
assert.Equal(t, time.Second*30, *config.TCPKeepAlive)
|
|
||||||
|
assert.Equal(t, baseUnit*10, config.ConnectTimeout.Duration)
|
||||||
|
assert.Equal(t, baseUnit*30, config.TLSTimeout.Duration)
|
||||||
|
assert.Equal(t, baseUnit*30, config.TCPKeepAlive.Duration)
|
||||||
assert.Equal(t, true, *config.NoHappyEyeballs)
|
assert.Equal(t, true, *config.NoHappyEyeballs)
|
||||||
assert.Equal(t, time.Second*60, *config.KeepAliveTimeout)
|
assert.Equal(t, baseUnit*60, config.KeepAliveTimeout.Duration)
|
||||||
assert.Equal(t, 10, *config.KeepAliveConnections)
|
assert.Equal(t, 10, *config.KeepAliveConnections)
|
||||||
assert.Equal(t, "app.tunnel.com", *config.HTTPHostHeader)
|
assert.Equal(t, "app.tunnel.com", *config.HTTPHostHeader)
|
||||||
assert.Equal(t, "app.tunnel.com", *config.OriginServerName)
|
assert.Equal(t, "app.tunnel.com", *config.OriginServerName)
|
||||||
|
@ -176,4 +204,12 @@ func TestUnmarshalOriginRequestConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.Equal(t, ipRules, config.IPRules)
|
assert.Equal(t, ipRules, config.IPRules)
|
||||||
|
|
||||||
|
// validate that serializing and deserializing again matches the deserialization from raw string
|
||||||
|
result, err := marshalFunc(config)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = unMarshalFunc(result, &config2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, config2, config)
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,13 +158,13 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
|
||||||
ProxyAddress: defaultProxyAddress,
|
ProxyAddress: defaultProxyAddress,
|
||||||
}
|
}
|
||||||
if c.ConnectTimeout != nil {
|
if c.ConnectTimeout != nil {
|
||||||
out.ConnectTimeout = *c.ConnectTimeout
|
out.ConnectTimeout = c.ConnectTimeout.Duration
|
||||||
}
|
}
|
||||||
if c.TLSTimeout != nil {
|
if c.TLSTimeout != nil {
|
||||||
out.TLSTimeout = *c.TLSTimeout
|
out.TLSTimeout = c.TLSTimeout.Duration
|
||||||
}
|
}
|
||||||
if c.TCPKeepAlive != nil {
|
if c.TCPKeepAlive != nil {
|
||||||
out.TCPKeepAlive = *c.TCPKeepAlive
|
out.TCPKeepAlive = c.TCPKeepAlive.Duration
|
||||||
}
|
}
|
||||||
if c.NoHappyEyeballs != nil {
|
if c.NoHappyEyeballs != nil {
|
||||||
out.NoHappyEyeballs = *c.NoHappyEyeballs
|
out.NoHappyEyeballs = *c.NoHappyEyeballs
|
||||||
|
@ -173,7 +173,7 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
|
||||||
out.KeepAliveConnections = *c.KeepAliveConnections
|
out.KeepAliveConnections = *c.KeepAliveConnections
|
||||||
}
|
}
|
||||||
if c.KeepAliveTimeout != nil {
|
if c.KeepAliveTimeout != nil {
|
||||||
out.KeepAliveTimeout = *c.KeepAliveTimeout
|
out.KeepAliveTimeout = c.KeepAliveTimeout.Duration
|
||||||
}
|
}
|
||||||
if c.HTTPHostHeader != nil {
|
if c.HTTPHostHeader != nil {
|
||||||
out.HTTPHostHeader = *c.HTTPHostHeader
|
out.HTTPHostHeader = *c.HTTPHostHeader
|
||||||
|
@ -257,13 +257,13 @@ type OriginRequestConfig struct {
|
||||||
|
|
||||||
func (defaults *OriginRequestConfig) setConnectTimeout(overrides config.OriginRequestConfig) {
|
func (defaults *OriginRequestConfig) setConnectTimeout(overrides config.OriginRequestConfig) {
|
||||||
if val := overrides.ConnectTimeout; val != nil {
|
if val := overrides.ConnectTimeout; val != nil {
|
||||||
defaults.ConnectTimeout = *val
|
defaults.ConnectTimeout = val.Duration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (defaults *OriginRequestConfig) setTLSTimeout(overrides config.OriginRequestConfig) {
|
func (defaults *OriginRequestConfig) setTLSTimeout(overrides config.OriginRequestConfig) {
|
||||||
if val := overrides.TLSTimeout; val != nil {
|
if val := overrides.TLSTimeout; val != nil {
|
||||||
defaults.TLSTimeout = *val
|
defaults.TLSTimeout = val.Duration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,13 +281,13 @@ func (defaults *OriginRequestConfig) setKeepAliveConnections(overrides config.Or
|
||||||
|
|
||||||
func (defaults *OriginRequestConfig) setKeepAliveTimeout(overrides config.OriginRequestConfig) {
|
func (defaults *OriginRequestConfig) setKeepAliveTimeout(overrides config.OriginRequestConfig) {
|
||||||
if val := overrides.KeepAliveTimeout; val != nil {
|
if val := overrides.KeepAliveTimeout; val != nil {
|
||||||
defaults.KeepAliveTimeout = *val
|
defaults.KeepAliveTimeout = val.Duration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (defaults *OriginRequestConfig) setTCPKeepAlive(overrides config.OriginRequestConfig) {
|
func (defaults *OriginRequestConfig) setTCPKeepAlive(overrides config.OriginRequestConfig) {
|
||||||
if val := overrides.TCPKeepAlive; val != nil {
|
if val := overrides.TCPKeepAlive; val != nil {
|
||||||
defaults.TCPKeepAlive = *val
|
defaults.TCPKeepAlive = val.Duration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,12 +191,12 @@ ingress:
|
||||||
rawConfig := []byte(`
|
rawConfig := []byte(`
|
||||||
{
|
{
|
||||||
"originRequest": {
|
"originRequest": {
|
||||||
"connectTimeout": 60000000000,
|
"connectTimeout": 60,
|
||||||
"tlsTimeout": 1000000000,
|
"tlsTimeout": 1,
|
||||||
"noHappyEyeballs": true,
|
"noHappyEyeballs": true,
|
||||||
"tcpKeepAlive": 1000000000,
|
"tcpKeepAlive": 1,
|
||||||
"keepAliveConnections": 1,
|
"keepAliveConnections": 1,
|
||||||
"keepAliveTimeout": 1000000000,
|
"keepAliveTimeout": 1,
|
||||||
"httpHostHeader": "abc",
|
"httpHostHeader": "abc",
|
||||||
"originServerName": "a1",
|
"originServerName": "a1",
|
||||||
"caPool": "/tmp/path0",
|
"caPool": "/tmp/path0",
|
||||||
|
@ -228,12 +228,12 @@ ingress:
|
||||||
"hostname": "*",
|
"hostname": "*",
|
||||||
"service": "https://localhost:8001",
|
"service": "https://localhost:8001",
|
||||||
"originRequest": {
|
"originRequest": {
|
||||||
"connectTimeout": 120000000000,
|
"connectTimeout": 120,
|
||||||
"tlsTimeout": 2000000000,
|
"tlsTimeout": 2,
|
||||||
"noHappyEyeballs": false,
|
"noHappyEyeballs": false,
|
||||||
"tcpKeepAlive": 2000000000,
|
"tcpKeepAlive": 2,
|
||||||
"keepAliveConnections": 2,
|
"keepAliveConnections": 2,
|
||||||
"keepAliveTimeout": 2000000000,
|
"keepAliveTimeout": 2,
|
||||||
"httpHostHeader": "def",
|
"httpHostHeader": "def",
|
||||||
"originServerName": "b2",
|
"originServerName": "b2",
|
||||||
"caPool": "/tmp/path1",
|
"caPool": "/tmp/path1",
|
||||||
|
@ -360,12 +360,12 @@ ingress:
|
||||||
"hostname": "*",
|
"hostname": "*",
|
||||||
"service": "https://localhost:8001",
|
"service": "https://localhost:8001",
|
||||||
"originRequest": {
|
"originRequest": {
|
||||||
"connectTimeout": 120000000000,
|
"connectTimeout": 120,
|
||||||
"tlsTimeout": 2000000000,
|
"tlsTimeout": 2,
|
||||||
"noHappyEyeballs": false,
|
"noHappyEyeballs": false,
|
||||||
"tcpKeepAlive": 2000000000,
|
"tcpKeepAlive": 2,
|
||||||
"keepAliveConnections": 2,
|
"keepAliveConnections": 2,
|
||||||
"keepAliveTimeout": 2000000000,
|
"keepAliveTimeout": 2,
|
||||||
"httpHostHeader": "def",
|
"httpHostHeader": "def",
|
||||||
"originServerName": "b2",
|
"originServerName": "b2",
|
||||||
"caPool": "/tmp/path1",
|
"caPool": "/tmp/path1",
|
||||||
|
|
|
@ -58,7 +58,7 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||||
{
|
{
|
||||||
"unknown_field": "not_deserialized",
|
"unknown_field": "not_deserialized",
|
||||||
"originRequest": {
|
"originRequest": {
|
||||||
"connectTimeout": 90000000000,
|
"connectTimeout": 90,
|
||||||
"noHappyEyeballs": true
|
"noHappyEyeballs": true
|
||||||
},
|
},
|
||||||
"ingress": [
|
"ingress": [
|
||||||
|
@ -68,7 +68,7 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||||
"service": "http://192.16.19.1:443",
|
"service": "http://192.16.19.1:443",
|
||||||
"originRequest": {
|
"originRequest": {
|
||||||
"noTLSVerify": true,
|
"noTLSVerify": true,
|
||||||
"connectTimeout": 10000000000
|
"connectTimeout": 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -76,7 +76,7 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||||
"service": "http://172.32.20.6:80",
|
"service": "http://172.32.20.6:80",
|
||||||
"originRequest": {
|
"originRequest": {
|
||||||
"noTLSVerify": true,
|
"noTLSVerify": true,
|
||||||
"connectTimeout": 30000000000
|
"connectTimeout": 30
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -192,7 +192,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
||||||
configJSONV1 = []byte(fmt.Sprintf(`
|
configJSONV1 = []byte(fmt.Sprintf(`
|
||||||
{
|
{
|
||||||
"originRequest": {
|
"originRequest": {
|
||||||
"connectTimeout": 90000000000,
|
"connectTimeout": 90,
|
||||||
"noHappyEyeballs": true
|
"noHappyEyeballs": true
|
||||||
},
|
},
|
||||||
"ingress": [
|
"ingress": [
|
||||||
|
@ -201,7 +201,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
||||||
"service": "%s",
|
"service": "%s",
|
||||||
"originRequest": {
|
"originRequest": {
|
||||||
"httpHostHeader": "%s",
|
"httpHostHeader": "%s",
|
||||||
"connectTimeout": 10000000000
|
"connectTimeout": 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue