Skip to content

Commit e2f797c

Browse files
hdurand0710oktalz
authored andcommitted
MINOR: add annotation on Service and Ingress for timeout server
1 parent 5da8480 commit e2f797c

File tree

7 files changed

+309
-2
lines changed

7 files changed

+309
-2
lines changed

deploy/tests/integration/base-suite.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
package integration
1616

1717
import (
18+
"fmt"
1819
"os"
20+
"path/filepath"
21+
"strings"
1922

2023
"github.com/haproxytech/kubernetes-ingress/pkg/annotations"
2124
c "github.com/haproxytech/kubernetes-ingress/pkg/controller"
@@ -164,3 +167,14 @@ func (suite *BaseSuite) StopController() {
164167
testController.Store.Clean()
165168
close(testController.EventChan)
166169
}
170+
171+
func (suite *BaseSuite) ExpectHaproxyConfigContains(s string, count int) {
172+
testController := suite.TestControllers[suite.T().Name()]
173+
174+
content, err := os.ReadFile(filepath.Join(testController.TempDir, "haproxy.cfg"))
175+
if err != nil {
176+
suite.T().Error(err.Error())
177+
}
178+
c := strings.Count(string(content), s)
179+
suite.Exactly(count, c, fmt.Sprintf("%s is repeated %d times but expected %d", s, c, count))
180+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2023 HAProxy Technologies LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, softwarehaproxyConfig
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package timeoutserver
16+
17+
import (
18+
"testing"
19+
20+
"github.com/haproxytech/kubernetes-ingress/deploy/tests/integration"
21+
"github.com/haproxytech/kubernetes-ingress/pkg/k8s"
22+
"github.com/haproxytech/kubernetes-ingress/pkg/store"
23+
"github.com/stretchr/testify/suite"
24+
)
25+
26+
var (
27+
appNs = "appNs"
28+
serviceName = "appSvcName"
29+
ingressName = "appIngName"
30+
configMapNamespace = "haproxy-controller"
31+
configMapName = "haproxy-kubernetes-ingress"
32+
)
33+
34+
type TimeoutServerSuite struct {
35+
integration.BaseSuite
36+
}
37+
38+
func TestTimeoutServer(t *testing.T) {
39+
t.Parallel()
40+
suite.Run(t, new(TimeoutServerSuite))
41+
}
42+
43+
func (suite *TimeoutServerSuite) BeforeTest(suiteName, testName string) {
44+
suite.BaseSuite.BeforeTest(suiteName, testName)
45+
// Add any needed update to the controller setting
46+
// by updating suite.TestControllers[suite.T().Name()].XXXXX
47+
testController := suite.TestControllers[suite.T().Name()]
48+
testController.OSArgs.ConfigMap.Name = configMapName
49+
testController.OSArgs.ConfigMap.Namespace = configMapNamespace
50+
}
51+
52+
func newConfigMap() *store.ConfigMap {
53+
return &store.ConfigMap{
54+
Annotations: map[string]string{},
55+
Namespace: configMapNamespace,
56+
Name: configMapName,
57+
Status: store.ADDED,
58+
}
59+
}
60+
61+
func (suite *TimeoutServerSuite) setupTest() *store.ConfigMap {
62+
testController := suite.TestControllers[suite.T().Name()]
63+
64+
ns := store.Namespace{Name: appNs, Status: store.ADDED}
65+
cm := newConfigMap()
66+
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.NAMESPACE, Namespace: ns.Name, Data: &ns}
67+
testController.EventChan <- k8s.SyncDataEvent{
68+
SyncType: k8s.CONFIGMAP, Namespace: configMapNamespace, Name: configMapName, Data: newConfigMap(),
69+
}
70+
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.COMMAND}
71+
controllerHasWorked := make(chan struct{})
72+
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.COMMAND, EventProcessed: controllerHasWorked}
73+
<-controllerHasWorked
74+
return cm
75+
}
76+
77+
func (suite *TimeoutServerSuite) fixture(events ...k8s.SyncDataEvent) {
78+
testController := suite.TestControllers[suite.T().Name()]
79+
80+
// Now sending store events for test setup
81+
for _, e := range events {
82+
testController.EventChan <- e
83+
}
84+
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.COMMAND}
85+
controllerHasWorked := make(chan struct{})
86+
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.COMMAND, EventProcessed: controllerHasWorked}
87+
<-controllerHasWorked
88+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright 2019 HAProxy Technologies LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package timeoutserver
16+
17+
import (
18+
"github.com/haproxytech/kubernetes-ingress/pkg/k8s"
19+
"github.com/haproxytech/kubernetes-ingress/pkg/store"
20+
networkingv1 "k8s.io/api/networking/v1"
21+
)
22+
23+
func (suite *TimeoutServerSuite) TestTimeoutServerConfigMap() {
24+
// name := suite.T().Name()
25+
// testController := suite.TestControllers[name]
26+
27+
suite.StartController()
28+
cm := suite.setupTest()
29+
30+
///////////////////////////////////////
31+
// timeout server setup in:
32+
// - configmap only
33+
cm.Status = store.MODIFIED
34+
cm.Annotations["timeout-server"] = "77000"
35+
svc := newAppSvc()
36+
ing := newAppIngress()
37+
events := []k8s.SyncDataEvent{
38+
{SyncType: k8s.CONFIGMAP, Namespace: configMapNamespace, Name: configMapName, Data: cm},
39+
{SyncType: k8s.SERVICE, Namespace: appNs, Name: serviceName, Data: svc},
40+
{SyncType: k8s.INGRESS, Namespace: appNs, Name: ingressName, Data: ing},
41+
}
42+
suite.fixture(events...)
43+
// Expected occurrences of "timeout server 77000"
44+
// 1- defaults
45+
// 2- backend haproxy-controller_default-local-service_http
46+
// 3- backend appNs_appSvcName_https
47+
suite.ExpectHaproxyConfigContains("timeout server 77000", 3)
48+
49+
suite.StopController()
50+
}
51+
52+
func (suite *TimeoutServerSuite) TestTimeoutServerService() {
53+
// name := suite.T().Name()
54+
// testController := suite.TestControllers[name]
55+
56+
suite.StartController()
57+
cm := suite.setupTest()
58+
59+
///////////////////////////////////////
60+
// timeout server setup in:
61+
// - configmap (77000)
62+
// - ingress (76000)
63+
// - app svc (75000) (appNs_appSvcName_https)
64+
cm.Status = store.MODIFIED
65+
cm.Annotations["timeout-server"] = "77000"
66+
ing := newAppIngress()
67+
ing.Annotations["timeout-server"] = "76000"
68+
svc := newAppSvc()
69+
svc.Annotations["timeout-server"] = "75000"
70+
71+
events := []k8s.SyncDataEvent{
72+
{SyncType: k8s.CONFIGMAP, Namespace: configMapNamespace, Name: configMapName, Data: cm},
73+
{SyncType: k8s.SERVICE, Namespace: appNs, Name: serviceName, Data: svc},
74+
{SyncType: k8s.INGRESS, Namespace: appNs, Name: ingressName, Data: ing},
75+
}
76+
suite.fixture(events...)
77+
// Expected occurrences of "timeout server 77000": #2
78+
// 1- defaults
79+
// 2- backend haproxy-controller_default-local-service_http
80+
// Expected occurrences of "timeout server 75000": #1 (from service)
81+
// 1- backend appNs_appSvcName_https
82+
// Expected occurrences of "timeout server 76000": #0 (from ingress)
83+
suite.ExpectHaproxyConfigContains("timeout server 77000", 2)
84+
suite.ExpectHaproxyConfigContains("timeout server 75000", 1)
85+
suite.ExpectHaproxyConfigContains("timeout server 76000", 0)
86+
87+
suite.StopController()
88+
}
89+
90+
func (suite *TimeoutServerSuite) TestTimeoutServerIngress() {
91+
// name := suite.T().Name()
92+
// testController := suite.TestControllers[name]
93+
94+
suite.StartController()
95+
cm := suite.setupTest()
96+
97+
///////////////////////////////////////
98+
// timeout server setup in:
99+
// - configmap (77000)
100+
// - ingress (76000)
101+
cm.Status = store.MODIFIED
102+
cm.Annotations["timeout-server"] = "77000"
103+
ing := newAppIngress()
104+
ing.Annotations["timeout-server"] = "76000"
105+
svc := newAppSvc()
106+
107+
events := []k8s.SyncDataEvent{
108+
{SyncType: k8s.CONFIGMAP, Namespace: configMapNamespace, Name: configMapName, Data: cm},
109+
{SyncType: k8s.SERVICE, Namespace: appNs, Name: serviceName, Data: svc},
110+
{SyncType: k8s.INGRESS, Namespace: appNs, Name: ingressName, Data: ing},
111+
}
112+
suite.fixture(events...)
113+
// Expected occurrences of "timeout server 77000": #2
114+
// 1- defaults
115+
// 2- backend haproxy-controller_default-local-service_http
116+
// Expected occurrences of "timeout server 75000": #0 (from service)
117+
// Expected occurrences of "timeout server 76000": #1 (from ingress)
118+
// 1- backend appNs_appSvcName_https
119+
suite.ExpectHaproxyConfigContains("timeout server 77000", 2)
120+
suite.ExpectHaproxyConfigContains("timeout server 75000", 0)
121+
suite.ExpectHaproxyConfigContains("timeout server 76000", 1)
122+
123+
suite.StopController()
124+
}
125+
126+
func newAppSvc() *store.Service {
127+
return &store.Service{
128+
Annotations: map[string]string{},
129+
Name: serviceName,
130+
Namespace: appNs,
131+
Ports: []store.ServicePort{
132+
{
133+
Name: "https",
134+
Protocol: "TCP",
135+
Port: 443,
136+
Status: store.ADDED,
137+
},
138+
},
139+
Status: store.ADDED,
140+
}
141+
}
142+
143+
func newAppIngress() *store.Ingress {
144+
return &store.Ingress{
145+
IngressCore: store.IngressCore{
146+
APIVersion: store.NETWORKINGV1,
147+
Name: ingressName,
148+
Namespace: appNs,
149+
Annotations: map[string]string{},
150+
Rules: map[string]*store.IngressRule{
151+
"": {
152+
Paths: map[string]*store.IngressPath{
153+
string(networkingv1.PathTypePrefix) + "-/": {
154+
Path: "/",
155+
PathTypeMatch: string(networkingv1.PathTypePrefix),
156+
SvcNamespace: appNs,
157+
SvcPortString: "https",
158+
SvcName: serviceName,
159+
},
160+
},
161+
},
162+
},
163+
},
164+
Status: store.ADDED,
165+
}
166+
}

documentation/annotations.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ This is autogenerated from [doc.yaml](doc.yaml). Description can be found in [ge
8585
| [timeout-http-keep-alive](#timeouts) | [time](#time) | "1m" | |:large_blue_circle:|:white_circle:|:white_circle:|
8686
| [timeout-queue](#timeouts) | [time](#time) | "5s" | |:large_blue_circle:|:white_circle:|:white_circle:|
8787
| [timeout-server](#timeouts) | [time](#time) | "50s" | |:large_blue_circle:|:white_circle:|:white_circle:|
88-
| [timeout-server-fin](#timeouts) | [time](#time) | | |:large_blue_circle:|:white_circle:|:white_circle:|
88+
| [timeout-server-fin](#timeouts) | [time](#time) | | |:large_blue_circle:|:large_blue_circle:|:large_blue_circle:|
8989
| [timeout-tunnel](#timeouts) | [time](#time) | "1h" | |:large_blue_circle:|:white_circle:|:white_circle:|
9090
| [whitelist](#access-control) | IPs/CIDRs or pattern file | | |:large_blue_circle:|:large_blue_circle:|:white_circle:|
9191
| [allow-list](#access-control) | IPs/CIDRs or pattern file | | |:large_blue_circle:|:large_blue_circle:|:white_circle:|
@@ -1836,7 +1836,7 @@ timeout-server: 5s
18361836

18371837
Sets the inactivity timeout on the server side for half-closed connections.
18381838

1839-
Available on: `configmap`
1839+
Available on: `configmap` `ingress` `service`
18401840

18411841
Possible values:
18421842

documentation/doc.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1804,6 +1804,8 @@ annotations:
18041804
- An integer with a unit of time (1 second = 1s, 1 minute = 1m, 1h = 1 hour)
18051805
applies_to:
18061806
- configmap
1807+
- ingress
1808+
- service
18071809
version_min: "1.4"
18081810
example: ["timeout-server-fin: 5s"]
18091811
- title: timeout-tunnel

pkg/annotations/annotations.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ func (a annImpl) Backend(b *models.Backend, s store.K8s, c certs.Certificates) [
148148
annotations := []Annotation{
149149
service.NewAbortOnClose("abortonclose", b),
150150
service.NewTimeoutCheck("timeout-check", b),
151+
service.NewTimeoutServer("timeout-server", b),
151152
service.NewLoadBalance("load-balance", b),
152153
service.NewCheck("check", b),
153154
service.NewCheckInter("check-interval", b),
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package service
2+
3+
import (
4+
"github.com/haproxytech/client-native/v3/models"
5+
6+
"github.com/haproxytech/kubernetes-ingress/pkg/annotations/common"
7+
"github.com/haproxytech/kubernetes-ingress/pkg/store"
8+
"github.com/haproxytech/kubernetes-ingress/pkg/utils"
9+
)
10+
11+
type TimeoutServer struct {
12+
backend *models.Backend
13+
name string
14+
}
15+
16+
func NewTimeoutServer(n string, b *models.Backend) *TimeoutServer {
17+
return &TimeoutServer{name: n, backend: b}
18+
}
19+
20+
func (a *TimeoutServer) GetName() string {
21+
return a.name
22+
}
23+
24+
func (a *TimeoutServer) Process(k store.K8s, annotations ...map[string]string) error {
25+
input := common.GetValue(a.GetName(), annotations...)
26+
if input == "" {
27+
a.backend.ServerTimeout = nil
28+
return nil
29+
}
30+
timeout, err := utils.ParseTime(input)
31+
if err != nil {
32+
return err
33+
}
34+
a.backend.ServerTimeout = timeout
35+
return nil
36+
}

0 commit comments

Comments
 (0)