From 1ad9856110a14de9d6a7e1e8675ee259763fca9d Mon Sep 17 00:00:00 2001 From: Viktor Date: Mon, 25 Sep 2017 19:20:43 +0200 Subject: [PATCH] Fixes #333 --- Dockerfile | 1 + docs/config.md | 1 + docs/usage.md | 16 ++++++------ proxy/types.go | 26 +++++++++++-------- proxy/types_test.go | 59 ++++++++++++++++++++++++++----------------- server/server_test.go | 1 + 6 files changed, 62 insertions(+), 42 deletions(-) diff --git a/Dockerfile b/Dockerfile index f06854a9..8c545ada 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,7 @@ ENV CERTS="" \ PROXY_INSTANCE_NAME="docker-flow" \ RELOAD_INTERVAL="5000" REPEAT_RELOAD=false \ RECONFIGURE_ATTEMPTS="20" \ + SEPARATOR="," \ SERVICE_NAME="proxy" SERVICE_DOMAIN_ALGO="hdr(host)" \ STATS_USER="" STATS_USER_ENV="STATS_USER" STATS_PASS="" STATS_PASS_ENV="STATS_PASS" STATS_URI="" STATS_URI_ENV="STATS_URI" STATS_PORT="" \ TIMEOUT_HTTP_REQUEST="5" TIMEOUT_HTTP_KEEP_ALIVE="15" TIMEOUT_CLIENT="20" TIMEOUT_CONNECT="5" TIMEOUT_QUEUE="30" TIMEOUT_SERVER="20" TIMEOUT_TUNNEL="3600" \ diff --git a/docs/config.md b/docs/config.md index 7926954f..cada0da1 100644 --- a/docs/config.md +++ b/docs/config.md @@ -35,6 +35,7 @@ PROXY_INSTANCE_NAME|The name of the proxy instance. Useful if multiple proxies a |RELOAD_INTERVAL |Defines the frequency (in milliseconds) between automatic config reloads from Swarm Listener.
**Default value:** `5000`| |REPEAT_RELOAD |If set to `true`, the proxy will periodically reload the config, using `RELOAD_INTERVAL` as pause between iterations.
**Example:** `true`
**Default value:** `false`| |RECONFIGURE_ATTEMPTS|The number of attempts the proxy will try to reconfigure itself before giving up and removing the offending service. The period between reconfigure attempts is 1 second.
**Example:** `15`
**Default value:** `20`| +|SEPARATOR |The character used to separate multiple values.
**Default value:** `,` (comma)| |SERVICE_DOMAIN_ALGO|The default algorithm applied to domain ACLs. It can be overwritten for a service through the `serviceDomainAlgo` parameter.
**Examples:**
`hdr(host)`: matches only if domain is the same as `serviceDomain`
`hdr_dom(host)`: matches the specified `serviceDomain` and any subdomain (a string either isolated or delimited by dots).
`req.ssl_sni`: matches Server Name TLS extension
**Default Value:** `hdr(host)`| |SKIP_ADDRESS_VALIDATION|Whether to skip validating service address before reconfiguring the proxy.
**Example:** false
**Default value:** `true`| |SSL_BIND_CIPHERS |Sets the default string describing the list of cipher algorithms ("cipher suite") that are negotiated during the SSL/TLS handshake for all "bind" lines which do not explicitly define theirs. The format of the string is defined in "man 1 ciphers" from OpenSSL man pages, and can be for instance a string such as `AES:ALL:!aNULL:!eNULL:+RC4:@STRENGTH`.
**Default value:** see [Dockerfile](https://github.com/vfarcic/docker-flow-proxy/blob/master/Dockerfile#L31)| diff --git a/docs/usage.md b/docs/usage.md index fc60eaf1..88657049 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -15,12 +15,12 @@ The following query parameters can be used to send a *reconfigure* request to *D |Query |Description | |---------------|------------------------------------------------------------------------------------------| |aclName |ACLs are ordered alphabetically by their names. If not specified, serviceName is used instead.
**Example:** `05-go-demo-acl`| -|addReqHeader |Additional headers that will be added to the request before forwarding it to the service. Multiple headers should be separated with comma (`,`). Please consult [Add a header to the request](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#add-a-header-to-the-request) for more info.
**Example:** `X-Forwarded-Port %[dst_port],X-Forwarded-Ssl on if { ssl_fc }`| -|addResHeader |Additional headers that will be added to the response before forwarding it to the client. Multiple headers should be separated with comma (`,`). Please consult [Add a header to the response](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#rewriting-http-responses) for more info.
**Example:** `X-Via %[env(HOSTNAME)],Server haproxy`| +|addReqHeader |Additional headers that will be added to the request before forwarding it to the service. Multiple headers should be separated with comma (`,`). Change the environment variable `SEPARATOR` if comma is to be used for other purposes. Please consult [Add a header to the request](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#add-a-header-to-the-request) for more info.
**Example:** `X-Forwarded-Port %[dst_port],X-Forwarded-Ssl on if { ssl_fc }`| +|addResHeader |Additional headers that will be added to the response before forwarding it to the client. Multiple headers should be separated with comma (`,`). Change the environment variable `SEPARATOR` if comma is to be used for other purposes. Please consult [Add a header to the response](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#rewriting-http-responses) for more info.
**Example:** `X-Via %[env(HOSTNAME)],Server haproxy`| |backendExtra |Additional configuration that will be added to the bottom of the service backend | |connectionMode |HAProxy supports 5 connection modes.

`http-keep-alive`: all requests and responses are processed.
`http-tunnel`: only the first request and response are processed, everything else is forwarded with no analysis.
`httpclose`: tunnel with "Connection: close" added in both directions.
`http-server-close`: the server-facing connection is closed after the response.
`forceclose`: the connection is actively closed after end of response.

In general, it is preferred to use `http-server-close` with application servers, and some static servers might benefit from `http-keep-alive`.
Connection mode is restricted to HTTP mode only. If specified, connection mode will be applied to the backend section.
**Example:** http-keep-alive| -|delReqHeader |Additional headers that will be deleted in the request before forwarding it to the service. Multiple headers should be separated with comma (`,`). Please consult [Delete a header in the request](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#delete-a-header-in-the-request) for more info.
**Example:** `X-Forwarded-For,Cookie`| -|delResHeader |Additional headers that will be deleted in the response before forwarding it to the client. Multiple headers should be separated with comma (`,`). Please consult [Delete a header in the response](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#delete-a-header-in-the-response) for more info.
**Example:** `X-Varnish,X-Cache`| +|delReqHeader |Additional headers that will be deleted in the request before forwarding it to the service. Multiple headers should be separated with comma (`,`). Change the environment variable `SEPARATOR` if comma is to be used for other purposes. Please consult [Delete a header in the request](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#delete-a-header-in-the-request) for more info.
**Example:** `X-Forwarded-For,Cookie`| +|delResHeader |Additional headers that will be deleted in the response before forwarding it to the client. Multiple headers should be separated with comma (`,`). Change the environment variable `SEPARATOR` if comma is to be used for other purposes. Please consult [Delete a header in the response](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#delete-a-header-in-the-response) for more info.
**Example:** `X-Varnish,X-Cache`| |distribute |Whether to distribute a request to all the instances of the proxy. Used only in the *swarm* mode.
**Example:** `true`|false| |httpsPort |The internal HTTPS port of a service that should be reconfigured. The port is used only in the `swarm` mode. If not specified, the `port` parameter will be used instead.
**Example:** `443`| |ignoreAuthorization|If set to true, the service destination will not require authorization. The parameter must be prefixed with the index of the service destion that should be excluded from authorization.
**Default:** `false`
**Example:** `true`| @@ -30,8 +30,8 @@ The following query parameters can be used to send a *reconfigure* request to *D |reqPathReplace |A regular expression to apply the modification.
**Example:** `/demo/`| |reqPathSearch |A regular expression to search the content to be replaced. If specified, `reqPathReplace` needs to be set as well.
**Example:** `/something/`| |serviceName |The name of the service. It must match the name of the Swarm service. This parameter is **mandatory**. If used through *Docker Flow Swarm Listener*, this parameter is added automatically.
**Example:** `go-demo`| -|setReqHeader |Additional headers that will be set to the request before forwarding it to the service. If a specified header exists, it will be replaced with the new one. Multiple headers should be separated with comma (`,`). Please consult [Set a header to the request](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#set-a-header-in-the-request) for more info.
**Example:** `X-Forwarded-Port %[dst_port],X-Forwarded-Ssl on if { ssl_fc }`| -|setResHeader |Additional headers that will be set to the response before forwarding it to the client. If a specified header exists, it will be replaced with the new one. Multiple headers should be separated with comma (`,`). Please consult [Set a header to the response](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#set-a-header-in-the-response) for more info.
**Example:** `X-Via %[env(HOSTNAME)],Server haproxy`| +|setReqHeader |Additional headers that will be set to the request before forwarding it to the service. If a specified header exists, it will be replaced with the new one. Multiple headers should be separated with comma (`,`). Change the environment variable `SEPARATOR` if comma is to be used for other purposes. Please consult [Set a header to the request](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#set-a-header-in-the-request) for more info.
**Example:** `X-Forwarded-Port %[dst_port],X-Forwarded-Ssl on if { ssl_fc }`| +|setResHeader |Additional headers that will be set to the response before forwarding it to the client. If a specified header exists, it will be replaced with the new one. Multiple headers should be separated with comma (`,`). Change the environment variable `SEPARATOR` if comma is to be used for other purposes. Please consult [Set a header to the response](https://www.haproxy.com/doc/aloha/7.0/haproxy/http_rewriting.html#set-a-header-in-the-response) for more info.
**Example:** `X-Via %[env(HOSTNAME)],Server haproxy`| |srcPort |The source (entry) port of a service. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `srcPort.1`, `srcPort.2`, and so on). The parameter is mandatory when specifying multiple destinations of a single service.
**Example:** `80`| |timeoutServer |The server timeout in seconds.
**Default:** `20`
**Example:** `60`| |timeoutTunnel |The tunnel timeout in seconds.
**Default:** `3600`
**Example:** `3600`| @@ -45,8 +45,8 @@ The following query parameters can be used only when `reqMode` is set to `http` |Query |Description | |-------------|--------------------------------------------------------------------------------| -|allowedMethods|The list of allowed methods. If specified, a request with a method that is not on the list will be denied. Multiple methods can be separated with comma (`,`). The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `allowedMethods.1`, `allowedMethods.2`, and so on).
**Example:** `GET,DELETE`| -|deniedMethods|The list of denied methods. If specified, a request with a method that is on the list will be denied. Multiple methods can be separated with comma (`,`). The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `deniedMethods.1`, `deniedMethods.2`, and so on).
**Example:** `PUT,POST`| +|allowedMethods|The list of allowed methods. If specified, a request with a method that is not on the list will be denied. Multiple methods can be separated with comma (`,`). Change the environment variable `SEPARATOR` if comma is to be used for other purposes. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `allowedMethods.1`, `allowedMethods.2`, and so on).
**Example:** `GET,DELETE`| +|deniedMethods|The list of denied methods. If specified, a request with a method that is on the list will be denied. Multiple methods can be separated with comma (`,`). Change the environment variable `SEPARATOR` if comma is to be used for other purposes. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `deniedMethods.1`, `deniedMethods.2`, and so on).
**Example:** `PUT,POST`| |denyHttp |Whether to deny HTTP requests thus allowing only HTTPS. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `denyHttp.1`, `denyHttp.2`, and so on).
**Example:** `true`
**Default Value:** `false`| |httpsOnly |If set to true, HTTP requests to the service will be redirected to HTTPS. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `httpsOnly.1`, `httpsOnly.2`, and so on).
**Example:** `true`
**Default Value:** `false`| |outboundHostname|The hostname where the service is running, for instance on a separate swarm. If specified, the proxy will dispatch requests to that domain.
**Example:** `ecme.com`| diff --git a/proxy/types.go b/proxy/types.go index 257476f1..86c5d91c 100644 --- a/proxy/types.go +++ b/proxy/types.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" "strings" + "os" ) var usersBasePath string = "/run/secrets/dfp_users_%s" @@ -264,6 +265,7 @@ func GetServiceFromMap(req *map[string]string) *Service { func GetServiceFromProvider(provider ServiceParameterProvider) *Service { sr := new(Service) provider.Fill(sr) + separator := os.Getenv("SEPARATOR") // TODO: Remove. It's added to maintain backwards compatibility with the deprecated parameter serviceDomainMatchAll (since July 2017) if strings.EqualFold(provider.GetString("serviceDomainMatchAll"), "true") { sr.ServiceDomainAlgo = "hdr_dom(host)" @@ -272,26 +274,26 @@ func GetServiceFromProvider(provider ServiceParameterProvider) *Service { sr.HttpsPort, _ = strconv.Atoi(provider.GetString("httpsPort")) } if len(provider.GetString("addReqHeader")) > 0 { - sr.AddReqHeader = strings.Split(provider.GetString("addReqHeader"), ",") + sr.AddReqHeader = strings.Split(provider.GetString("addReqHeader"), separator) } else if len(provider.GetString("addHeader")) > 0 { // TODO: Deprecated since Apr. 2017. - sr.AddReqHeader = strings.Split(provider.GetString("addHeader"), ",") + sr.AddReqHeader = strings.Split(provider.GetString("addHeader"), separator) } if len(provider.GetString("setReqHeader")) > 0 { - sr.SetReqHeader = strings.Split(provider.GetString("setReqHeader"), ",") + sr.SetReqHeader = strings.Split(provider.GetString("setReqHeader"), separator) } else if len(provider.GetString("setHeader")) > 0 { // TODO: Deprecated since Apr. 2017. - sr.SetReqHeader = strings.Split(provider.GetString("setHeader"), ",") + sr.SetReqHeader = strings.Split(provider.GetString("setHeader"), separator) } if len(provider.GetString("delReqHeader")) > 0 { - sr.DelReqHeader = strings.Split(provider.GetString("delReqHeader"), ",") + sr.DelReqHeader = strings.Split(provider.GetString("delReqHeader"), separator) } if len(provider.GetString("addResHeader")) > 0 { - sr.AddResHeader = strings.Split(provider.GetString("addResHeader"), ",") + sr.AddResHeader = strings.Split(provider.GetString("addResHeader"), separator) } if len(provider.GetString("setResHeader")) > 0 { - sr.SetResHeader = strings.Split(provider.GetString("setResHeader"), ",") + sr.SetResHeader = strings.Split(provider.GetString("setResHeader"), separator) } if len(provider.GetString("delResHeader")) > 0 { - sr.DelResHeader = strings.Split(provider.GetString("delResHeader"), ",") + sr.DelResHeader = strings.Split(provider.GetString("delResHeader"), separator) } if len(sr.SessionType) > 0 { sr.Tasks, _ = lookupHost("tasks." + sr.ServiceName) @@ -350,13 +352,14 @@ func getServiceDestList(sr *Service, provider ServiceParameterProvider) []Servic } func getServiceDest(sr *Service, provider ServiceParameterProvider, index int) ServiceDest { + separator := os.Getenv("SEPARATOR") suffix := "" if index > 0 { suffix = fmt.Sprintf(".%d", index) } userAgent := UserAgent{} if len(provider.GetString(fmt.Sprintf("userAgent%s", suffix))) > 0 { - userAgent.Value = strings.Split(provider.GetString(fmt.Sprintf("userAgent%s", suffix)), ",") + userAgent.Value = strings.Split(provider.GetString(fmt.Sprintf("userAgent%s", suffix)), separator) userAgent.AclName = replaceNonAlphabetAndNumbers(userAgent.Value) } reqMode := "http" @@ -367,7 +370,7 @@ func getServiceDest(sr *Service, provider ServiceParameterProvider, index int) S headerString := provider.GetString(fmt.Sprintf("serviceHeader%s", suffix)) header := map[string]string{} if len(headerString) > 0 { - for _, value := range strings.Split(headerString, ",") { + for _, value := range strings.Split(headerString, separator) { values := strings.Split(value, ":") if len(values) == 2 { header[strings.Trim(values[0], " ")] = strings.Trim(values[1], " ") @@ -397,9 +400,10 @@ func getServiceDest(sr *Service, provider ServiceParameterProvider, index int) S } func getSliceFromString(provider ServiceParameterProvider, key string) []string { + separator := os.Getenv("SEPARATOR") value := []string{} if len(provider.GetString(key)) > 0 { - value = strings.Split(provider.GetString(key), ",") + value = strings.Split(provider.GetString(key), separator) } return value } diff --git a/proxy/types_test.go b/proxy/types_test.go index 432b9dad..e0bbb447 100644 --- a/proxy/types_test.go +++ b/proxy/types_test.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" "testing" + "os" ) type TypesTestSuite struct { @@ -15,6 +16,11 @@ func (s *TypesTestSuite) SetupTest() { logPrintf = func(format string, v ...interface{}) {} } +func TestRunUnitTestSuite(t *testing.T) { + os.Setenv("SEPARATOR", ",") + suite.Run(t, new(TypesTestSuite)) +} + // mergeUsers func (s *TypesTestSuite) Test_UsersMerge_AllCases() { @@ -168,7 +174,7 @@ x:X`, false, false) func (s *TypesTestSuite) Test_GetServiceFromMap_ReturnsProxyService() { expected := s.getExpectedService() expected.ServiceDest[0].Index = 0 - serviceMap := s.getServiceMap(expected, "") + serviceMap := s.getServiceMap(expected, "", ",") actual := GetServiceFromMap(&serviceMap) s.Equal(expected, *actual) } @@ -177,7 +183,20 @@ func (s *TypesTestSuite) Test_GetServiceFromMap_ReturnsProxyService() { func (s *TypesTestSuite) Test_GetServiceFromProvider_ReturnsProxyServiceWithIndexedData() { expected := s.getExpectedService() - serviceMap := s.getServiceMap(expected, ".1") + serviceMap := s.getServiceMap(expected, ".1", ",") + provider := mapParameterProvider{&serviceMap} + + actual := GetServiceFromProvider(&provider) + + s.Equal(expected, *actual) +} + +func (s *TypesTestSuite) Test_GetServiceFromProvider_UsesSeparatorFromEnvVar() { + separatorOrig := os.Getenv("SEPARATOR") + defer func() { os.Setenv("SEPARATOR", separatorOrig) }() + os.Setenv("SEPARATOR", "@") + expected := s.getExpectedService() + serviceMap := s.getServiceMap(expected, ".1", "@") provider := mapParameterProvider{&serviceMap} actual := GetServiceFromProvider(&provider) @@ -189,7 +208,7 @@ func (s *TypesTestSuite) Test_GetServiceFromProvider_AddsTasksWhenSessionTypeIsN expected := s.getExpectedService() expected.SessionType = "sticky-server" expected.Tasks = []string{"1.2.3.4", "4.3.2.1"} - serviceMap := s.getServiceMap(expected, ".1") + serviceMap := s.getServiceMap(expected, ".1", ",") provider := mapParameterProvider{&serviceMap} actualHost := "" lookupHostOrig := lookupHost @@ -259,27 +278,21 @@ func (s *TypesTestSuite) Test_GetServiceFromProvider_MovesHttpsOnlyToIndexedEntr s.Equal(expected, *actual) } -// Suite - -func TestRunUnitTestSuite(t *testing.T) { - suite.Run(t, new(TypesTestSuite)) -} - // Util -func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix string) map[string]string { +func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix, separator string) map[string]string { header := "" for key, value := range expected.ServiceDest[0].ServiceHeader { - header += key + ":" + value + "," + header += key + ":" + value + separator } - header = strings.TrimRight(header, ",") + header = strings.TrimRight(header, separator) return map[string]string{ "aclName": expected.AclName, - "addReqHeader": strings.Join(expected.AddReqHeader, ","), - "addResHeader": strings.Join(expected.AddResHeader, ","), + "addReqHeader": strings.Join(expected.AddReqHeader, separator), + "addResHeader": strings.Join(expected.AddResHeader, separator), "backendExtra": expected.BackendExtra, - "delReqHeader": strings.Join(expected.DelReqHeader, ","), - "delResHeader": strings.Join(expected.DelResHeader, ","), + "delReqHeader": strings.Join(expected.DelReqHeader, separator), + "delResHeader": strings.Join(expected.DelResHeader, separator), "distribute": strconv.FormatBool(expected.Distribute), "httpsPort": strconv.Itoa(expected.HttpsPort), "isDefaultBackend": strconv.FormatBool(expected.IsDefaultBackend), @@ -292,8 +305,8 @@ func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix string) map "serviceDomainAlgo": expected.ServiceDomainAlgo, "serviceName": expected.ServiceName, "sessionType": expected.SessionType, - "setReqHeader": strings.Join(expected.SetReqHeader, ","), - "setResHeader": strings.Join(expected.SetResHeader, ","), + "setReqHeader": strings.Join(expected.SetReqHeader, separator), + "setResHeader": strings.Join(expected.SetResHeader, separator), "sslVerifyNone": strconv.FormatBool(expected.SslVerifyNone), "templateBePath": expected.TemplateBePath, "templateFePath": expected.TemplateFePath, @@ -303,17 +316,17 @@ func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix string) map "usersPassEncrypted": "true", "xForwardedProto": strconv.FormatBool(expected.XForwardedProto), // ServiceDest - "allowedMethods" + indexSuffix: strings.Join(expected.ServiceDest[0].AllowedMethods, ","), - "deniedMethods" + indexSuffix: strings.Join(expected.ServiceDest[0].DeniedMethods, ","), + "allowedMethods" + indexSuffix: strings.Join(expected.ServiceDest[0].AllowedMethods, separator), + "deniedMethods" + indexSuffix: strings.Join(expected.ServiceDest[0].DeniedMethods, separator), "denyHttp" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].DenyHttp), "httpsOnly" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].HttpsOnly), "ignoreAuthorization" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].IgnoreAuthorization), "port" + indexSuffix: expected.ServiceDest[0].Port, "reqMode" + indexSuffix: expected.ServiceDest[0].ReqMode, - "serviceDomain" + indexSuffix: strings.Join(expected.ServiceDest[0].ServiceDomain, ","), + "serviceDomain" + indexSuffix: strings.Join(expected.ServiceDest[0].ServiceDomain, separator), "serviceHeader" + indexSuffix: header, - "servicePath" + indexSuffix: strings.Join(expected.ServiceDest[0].ServicePath, ","), - "userAgent" + indexSuffix: strings.Join(expected.ServiceDest[0].UserAgent.Value, ","), + "servicePath" + indexSuffix: strings.Join(expected.ServiceDest[0].ServicePath, separator), + "userAgent" + indexSuffix: strings.Join(expected.ServiceDest[0].UserAgent.Value, separator), "verifyClientSsl" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].VerifyClientSsl), } } diff --git a/server/server_test.go b/server/server_test.go index 9743fd35..c2acfcdb 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -32,6 +32,7 @@ func TestServerUnitTestSuite(t *testing.T) { logPrintf = func(format string, v ...interface{}) {} os.Setenv("SKIP_ADDRESS_VALIDATION", "false") + os.Setenv("SEPARATOR", ",") suite.Run(t, s) }