Skip to content

Commit

Permalink
Merge pull request #576 from jcmoraisjr/jm-tcp-check
Browse files Browse the repository at this point in the history
Add check interval on tcp service
  • Loading branch information
jcmoraisjr authored May 11, 2020
2 parents 1b79a77 + 08119d7 commit ae30cf2
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 10 deletions.
15 changes: 10 additions & 5 deletions docs/content/en/docs/configuration/command-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,35 +221,40 @@ Configure `--tcp-services-configmap` argument with `namespace/configmapname` res
services and ports that HAProxy should listen to. Use the HAProxy's port number as the key of the
ConfigMap.

The value of the ConfigMap entry is a colon separated list of the following items:
The value of the ConfigMap entry is a colon separated list of the following arguments:

1. `<namespace>/<service-name>`, mandatory, is the well known notation of the service that will receive incoming connections.
1. `<portnumber>`, mandatory, is the port number the upstream service is listening - this is not related to the listening port of HAProxy.
1. `<in-proxy>`, optional, should be defined as `PROXY` if HAProxy should expect requests using the [PROXY](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) protocol. Leave empty to not use PROXY protocol. This is usually used only if there is another load balancer in front of HAProxy which supports the PROXY protocol. PROXY protocol v1 and v2 are supported.
1. `<out-proxy>`, optional, should be defined as `PROXY` or `PROXY-V2` if the upstream service expect connections using the PROXY protocol v2. Use `PROXY-V1` instead if the upstream service only support v1 protocol. Leave empty to connect without using the PROXY protocol.
1. `<namespace/secret-name>`, optional, used to configure SSL/TLS over the TCP connection. Secret should have `tls.crt` and `tls.key` pair used on TLS handshake. Leave empty to not use ssl-offload.
1. `<check-interval>`, added in v0.10, optional and defaults to `2s`, configures a TCP check interval. Declare `-` (one single dash) as the time to disable it. Valid time is a number and a mandatory suffix: `us`, `ms`, `s`, `m`, `h` or `d`.

Optional fields should be skipped using two consecutive colons.
Optional fields can be skipped using consecutive colons.

In the example below:

```
...
data:
"5432": "default/pgsql:5432"
"3306": "default/mysql:3306::::-"
"5432": "default/pgsql:5432::::1s"
"8000": "system-prod/http:8000::PROXY-V1"
"9900": "system-prod/admin:9900:PROXY::system-prod/tcp-9900"
"9990": "system-prod/admin:9999::PROXY-V2"
"9999": "system-prod/admin:9999:PROXY:PROXY"
```

HAProxy will listen 5 new ports:
HAProxy will listen 6 new ports:

* `5432` will proxy to a `pgsql` service on `default` namespace.
* `3306` will proxy to a `mysql` service on `default` namespace. Check interval is disabled.
* `5432` will proxy to a `pgsql` service on `default` namespace. Check interval is defined to run on every second.
* `8000` will proxy to `http` service, port `8000`, on the `system-prod` namespace. The upstream service will expect connections using the PROXY protocol but it only supports v1.
* `9900` will proxy to `admin` service, port `9900`, on the `system-prod` namespace. Clients should connect using the PROXY protocol v1 or v2. Upcoming connections should be encrypted, HAProxy will ssl-offload data using crt/key provided by `system-prod/tcp-9900` secret.
* `9990` and `9999` will proxy to the same `admin` service and `9999` port and the upstream service will expect connections using the PROXY protocol v2. The HAProxy frontend, however, will only expect PROXY protocol v1 or v2 on it's port `9999`.

Note: Check interval was added in v0.10 and defaults to `2s`. All declared services has check interval enabled, except `3306` which disabled it.

---

## --verify-hostname
Expand Down
23 changes: 21 additions & 2 deletions pkg/converters/configmap/tcpservices.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package configmap

import (
"fmt"
"regexp"
"strconv"
"strings"

Expand Down Expand Up @@ -47,6 +48,8 @@ type tcpSvcConverter struct {
haproxy haproxy.Config
}

var regexValidTime = regexp.MustCompile(`^[0-9]+(us|ms|s|m|h|d)$`)

func (c *tcpSvcConverter) Sync(tcpservices map[string]string) {
// map[key]value is:
// - key => port to expose
Expand All @@ -56,6 +59,7 @@ func (c *tcpSvcConverter) Sync(tcpservices map[string]string) {
// - 2: "PROXY" means accept proxy protocol
// - 3: "PROXY[-V1|V2]" means send proxy protocol, defaults to V2
// - 4: namespace/name of crt/key secret if should ssl-offload
// - 5: check interval
for k, v := range tcpservices {
publicport, err := strconv.Atoi(k)
if err != nil {
Expand Down Expand Up @@ -90,12 +94,25 @@ func (c *tcpSvcConverter) Sync(tcpservices map[string]string) {
continue
}
}
checkInterval := "2s"
if svc.checkint != "" {
if svc.checkint == "-" {
checkInterval = ""
} else if regexValidTime.MatchString(svc.checkint) {
checkInterval = svc.checkint
} else {
c.logger.Warn(
"using default check interval '%s' due to an invalid time config on TCP service %d: %s",
checkInterval, publicport, svc.checkint)
}
}
servicename := fmt.Sprintf("%s_%s", service.Namespace, service.Name)
backend := c.haproxy.AcquireTCPBackend(servicename, publicport)
for _, addr := range addrs {
backend.AddEndpoint(addr.IP, addr.Port)
}
backend.ProxyProt.Decode = strings.ToLower(svc.inProxy) == "proxy"
backend.CheckInterval = checkInterval
switch strings.ToLower(svc.outProxy) {
case "proxy", "proxy-v2":
backend.ProxyProt.EncodeVersion = "v2"
Expand All @@ -112,12 +129,13 @@ type tcpSvc struct {
inProxy string
outProxy string
secret string
checkint string
}

func (c *tcpSvcConverter) parseService(service string) *tcpSvc {
svc := make([]string, 5)
svc := make([]string, 6)
for i, v := range strings.Split(service, ":") {
if i < 5 {
if i < 6 {
svc[i] = v
}
}
Expand All @@ -127,5 +145,6 @@ func (c *tcpSvcConverter) parseService(service string) *tcpSvc {
inProxy: svc[2],
outProxy: svc[3],
secret: svc[4],
checkint: svc[5],
}
}
43 changes: 40 additions & 3 deletions pkg/converters/configmap/tcpservices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func TestTCPSvcSync(t *testing.T) {
Endpoints: []*hatypes.TCPEndpoint{
{Name: "srv001", IP: "172.17.0.101", Port: 5432},
},
CheckInterval: "2s",
},
{
Name: "default_sendmail",
Expand All @@ -69,6 +70,7 @@ func TestTCPSvcSync(t *testing.T) {
{Name: "srv001", IP: "172.17.0.201", Port: 25},
{Name: "srv002", IP: "172.17.0.202", Port: 25},
},
CheckInterval: "2s",
},
},
},
Expand Down Expand Up @@ -101,8 +103,9 @@ func TestTCPSvcSync(t *testing.T) {
services: map[string]string{"5432": "default/pg:5432"},
expected: []*hatypes.TCPBackend{
{
Name: "default_pg",
Port: 5432,
Name: "default_pg",
Port: 5432,
CheckInterval: "2s",
},
},
},
Expand All @@ -118,6 +121,7 @@ func TestTCPSvcSync(t *testing.T) {
Endpoints: []*hatypes.TCPEndpoint{
{Name: "srv001", IP: "172.17.0.101", Port: 5432},
},
CheckInterval: "2s",
},
},
},
Expand All @@ -133,13 +137,14 @@ func TestTCPSvcSync(t *testing.T) {
Endpoints: []*hatypes.TCPEndpoint{
{Name: "srv001", IP: "172.17.0.101", Port: 5432},
},
CheckInterval: "2s",
},
},
},
// 10
{
svcmock: map[string]string{"default/pg:5432": "172.17.0.101"},
services: map[string]string{"5432": "default/pg:5432::proxy"},
services: map[string]string{"5432": "default/pg:5432::proxy::-"},
expected: []*hatypes.TCPBackend{
{
Name: "default_pg",
Expand Down Expand Up @@ -171,6 +176,38 @@ func TestTCPSvcSync(t *testing.T) {
Endpoints: []*hatypes.TCPEndpoint{
{Name: "srv001", IP: "172.17.0.101", Port: 5432},
},
CheckInterval: "2s",
},
},
},
// 13
{
svcmock: map[string]string{"default/pg:5432": "172.17.0.101"},
services: map[string]string{"5432": "default/pg:5432::::fail"},
expected: []*hatypes.TCPBackend{
{
Name: "default_pg",
Port: 5432,
Endpoints: []*hatypes.TCPEndpoint{
{Name: "srv001", IP: "172.17.0.101", Port: 5432},
},
CheckInterval: "2s",
},
},
logging: `WARN using default check interval '2s' due to an invalid time config on TCP service 5432: fail`,
},
// 14
{
svcmock: map[string]string{"default/pg:5432": "172.17.0.101"},
services: map[string]string{"5432": "default/pg:5432::::2s"},
expected: []*hatypes.TCPBackend{
{
Name: "default_pg",
Port: 5432,
Endpoints: []*hatypes.TCPEndpoint{
{Name: "srv001", IP: "172.17.0.101", Port: 5432},
},
CheckInterval: "2s",
},
},
},
Expand Down

0 comments on commit ae30cf2

Please sign in to comment.