Skip to content
This repository was archived by the owner on Jun 28, 2022. It is now read-only.

Commit e2ba316

Browse files
authored
Add basic TCPProxy support (#43)
Fixes #42. Signed-off-by: Nick Young <ynick@vmware.com>
1 parent fbb61d6 commit e2ba316

6 files changed

Lines changed: 164 additions & 21 deletions

File tree

cmd/ir2proxy/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func run() int {
7474
// See https://github.com/projectcontour/ir2proxy/issues/8 for more explanation here.
7575
outputYAML = bytes.ReplaceAll(outputYAML, []byte(" creationTimestamp: null\n"), []byte(""))
7676
outputWarnings := commentedWarnings(warnings)
77-
fmt.Printf("---\n%s%s", outputWarnings, outputYAML)
77+
fmt.Printf("---\n%s\n%s", outputWarnings, outputYAML)
7878
}
7979

8080
return 0

internal/translator/testdata/basic_tcpproxy/errors.txt

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
apiVersion: contour.heptio.com/v1beta1
3+
kind: IngressRoute
4+
metadata:
5+
name: tcpproxy-test
6+
spec:
7+
virtualhost:
8+
fqdn: "tcpproxy-test.domain.com"
9+
tls:
10+
secretName: "secret"
11+
tcpproxy:
12+
services:
13+
- name: s1
14+
port: 80
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
apiVersion: projectcontour.io/v1
3+
kind: HTTPProxy
4+
metadata:
5+
name: tcpproxy-test
6+
spec:
7+
tcpproxy:
8+
services:
9+
- name: s1
10+
port: 80
11+
virtualhost:
12+
fqdn: tcpproxy-test.domain.com
13+
tls:
14+
secretName: secret
15+
status: {}

internal/translator/translate.go

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,28 @@ func IngressRouteToHTTPProxy(ir *irv1beta1.IngressRoute) (*hpv1.HTTPProxy, []str
3636
var routeLCP string
3737
var warnings []string
3838

39+
var tcpproxy *hpv1.TCPProxy
40+
41+
var includes []hpv1.Include
42+
43+
if ir.Spec.TCPProxy != nil {
44+
if ir.Spec.VirtualHost == nil {
45+
return nil, nil, errors.New("invalid IngressRoute: tcpproxy must be in a root IngressRoute")
46+
}
47+
48+
// The compiler won't use the outer tcpproxy correctly if we
49+
// use := here.
50+
var tcpincludes []hpv1.Include
51+
var err error
52+
var tcpwarnings []string
53+
tcpproxy, tcpincludes, tcpwarnings, err = translateTCPProxy(ir.Spec.TCPProxy)
54+
if err != nil {
55+
return nil, nil, err
56+
}
57+
includes = append(includes, tcpincludes...)
58+
warnings = append(warnings, tcpwarnings...)
59+
}
60+
3961
if ir.Spec.VirtualHost == nil {
4062
routePrefixes := extractPrefixes(ir.Spec.Routes)
4163
routeLCP = longestCommonPathPrefix(routePrefixes)
@@ -54,8 +76,8 @@ func IngressRouteToHTTPProxy(ir *irv1beta1.IngressRoute) (*hpv1.HTTPProxy, []str
5476

5577
}
5678

57-
routes, includes, translateWarnings := translateRoutes(ir.Spec.Routes, routeLCP)
58-
79+
routes, routeIncludes, translateWarnings := translateRoutes(ir.Spec.Routes, routeLCP)
80+
includes = append(includes, routeIncludes...)
5981
warnings = append(warnings, translateWarnings...)
6082

6183
hp := &hpv1.HTTPProxy{
@@ -78,8 +100,10 @@ func IngressRouteToHTTPProxy(ir *irv1beta1.IngressRoute) (*hpv1.HTTPProxy, []str
78100
VirtualHost: ir.Spec.VirtualHost,
79101
Routes: routes,
80102
Includes: includes,
103+
TCPProxy: tcpproxy,
81104
},
82105
}
106+
83107
return hp, warnings, nil
84108
}
85109

@@ -123,50 +147,68 @@ func translateRoute(irRoute irv1beta1.Route, routeLCP string) (hpv1.Route, []str
123147
var seenHealthCheckServiceName string
124148
for _, irService := range irRoute.Services {
125149

126-
service := hpv1.Service{
127-
Name: irService.Name,
128-
Port: irService.Port,
129-
Weight: irService.Weight,
130-
}
150+
service, healthcheckPolicy, lbpolicy := translateService(irService)
131151

132-
if irService.Strategy != "" {
152+
if lbpolicy != nil {
133153
if seenLBStrategy == "" {
134154
// Copy the first strategy we encounter into the HP loadbalancerpolicy
135155
// and save that we've seen that one.
136156
seenLBStrategy = irService.Strategy
137-
route.LoadBalancerPolicy = &hpv1.LoadBalancerPolicy{
138-
Strategy: irService.Strategy,
139-
}
157+
route.LoadBalancerPolicy = lbpolicy
140158
} else {
141159
if seenLBStrategy != irService.Strategy {
142160
warnings = append(warnings, fmt.Sprintf("Strategy %s on Service %s could not be applied, HTTPProxy only supports a single load balancing policy across all services. %s is already applied.", irService.Strategy, irService.Name, seenLBStrategy))
143161
}
144162
}
145-
146163
}
147-
if irService.HealthCheck != nil {
164+
165+
if healthcheckPolicy != nil {
148166
if seenHealthCheckPolicy == nil {
149167
// Copy the first strategy we encounter into the HP HealthCheckPolicy
150168
// and save that we've seen that one.
151169
seenHealthCheckPolicy = irService.HealthCheck
152170
seenHealthCheckServiceName = irService.Name
153-
route.HealthCheckPolicy = &hpv1.HTTPHealthCheckPolicy{
154-
Path: irService.HealthCheck.Path,
155-
Host: irService.HealthCheck.Host,
156-
TimeoutSeconds: irService.HealthCheck.TimeoutSeconds,
157-
UnhealthyThresholdCount: irService.HealthCheck.UnhealthyThresholdCount,
158-
HealthyThresholdCount: irService.HealthCheck.HealthyThresholdCount,
159-
}
171+
route.HealthCheckPolicy = healthcheckPolicy
160172
} else {
161173
warnings = append(warnings, fmt.Sprintf("A healthcheck on service %s could not be applied, HTTPProxy only supports a single healthcheck across all services. A different healthcheck from service %s is already applied.", irService.Name, seenHealthCheckServiceName))
162174
}
163175
}
176+
164177
route.Services = append(route.Services, service)
165178
}
166179

167180
return route, warnings
168181
}
169182

183+
func translateService(irService irv1beta1.Service) (hpv1.Service, *hpv1.HTTPHealthCheckPolicy, *hpv1.LoadBalancerPolicy) {
184+
service := hpv1.Service{
185+
Name: irService.Name,
186+
Port: irService.Port,
187+
Weight: irService.Weight,
188+
}
189+
190+
var healthcheckPolicy *hpv1.HTTPHealthCheckPolicy
191+
var lbpolicy *hpv1.LoadBalancerPolicy
192+
193+
if irService.Strategy != "" {
194+
lbpolicy = &hpv1.LoadBalancerPolicy{
195+
Strategy: irService.Strategy,
196+
}
197+
}
198+
199+
if irService.HealthCheck != nil {
200+
healthcheckPolicy = &hpv1.HTTPHealthCheckPolicy{
201+
Path: irService.HealthCheck.Path,
202+
Host: irService.HealthCheck.Host,
203+
TimeoutSeconds: irService.HealthCheck.TimeoutSeconds,
204+
UnhealthyThresholdCount: irService.HealthCheck.UnhealthyThresholdCount,
205+
HealthyThresholdCount: irService.HealthCheck.HealthyThresholdCount,
206+
}
207+
}
208+
209+
return service, healthcheckPolicy, lbpolicy
210+
}
211+
170212
func translateInclude(irRoute irv1beta1.Route) *hpv1.Include {
171213

172214
if irRoute.Delegate == nil {
@@ -203,6 +245,39 @@ func translateRoutes(irRoutes []irv1beta1.Route, routeLCP string) ([]hpv1.Route,
203245
return routes, includes, warnings
204246
}
205247

248+
func translateTCPProxy(irTCPProxy *irv1beta1.TCPProxy) (*hpv1.TCPProxy, []hpv1.Include, []string, error) {
249+
250+
var includes []hpv1.Include
251+
var warnings []string
252+
253+
if irTCPProxy.Delegate != nil {
254+
if len(irTCPProxy.Services) > 0 {
255+
return nil, includes, warnings, errors.New("invalid IngressRoute: Delegate and Services can not both be set")
256+
}
257+
includes = append(includes, hpv1.Include{
258+
Name: irTCPProxy.Delegate.Name,
259+
Namespace: irTCPProxy.Delegate.Namespace,
260+
})
261+
return nil, includes, warnings, nil
262+
}
263+
264+
proxy := &hpv1.TCPProxy{}
265+
for _, irService := range irTCPProxy.Services {
266+
267+
hpService, healthcheckPolicy, lbpolicy := translateService(irService)
268+
269+
if healthcheckPolicy != nil {
270+
warnings = append(warnings, "Healthcheck policy of TCPProxy service has no effect, discarding")
271+
}
272+
273+
if lbpolicy != nil {
274+
proxy.LoadBalancerPolicy = lbpolicy
275+
}
276+
proxy.Services = append(proxy.Services, hpService)
277+
}
278+
return proxy, includes, warnings, nil
279+
}
280+
206281
func extractPrefixes(routes []irv1beta1.Route) []string {
207282

208283
var prefixes []string

internal/translator/translate_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,45 @@ spec:
122122
`),
123123
want: []string{"invalid IngressRoute: match clauses must share a common prefix"},
124124
},
125+
"tcpproxy IR, not root": {
126+
input: []byte(`
127+
---
128+
apiVersion: contour.heptio.com/v1beta1
129+
kind: IngressRoute
130+
metadata:
131+
name: nonroot-tcpproxy
132+
namespace: default
133+
spec:
134+
tcpproxy:
135+
services:
136+
- name: s1
137+
port: 80
138+
`),
139+
want: []string{"invalid IngressRoute: tcpproxy must be in a root IngressRoute"},
140+
},
141+
"tcpproxy IR, delegate and services set": {
142+
input: []byte(`
143+
---
144+
apiVersion: contour.heptio.com/v1beta1
145+
kind: IngressRoute
146+
metadata:
147+
name: nonroot-tcpproxy
148+
namespace: default
149+
spec:
150+
virtualhost:
151+
fqdn: "tcpproxy-test.domain.com"
152+
tls:
153+
secretName: "secret"
154+
tcpproxy:
155+
delegate:
156+
name: bad
157+
namespace: default
158+
services:
159+
- name: s1
160+
port: 80
161+
`),
162+
want: []string{"invalid IngressRoute: Delegate and Services can not both be set"},
163+
},
125164
}
126165

127166
for name, tc := range tests {

0 commit comments

Comments
 (0)