Skip to content

Commit

Permalink
MINOR: add annotation on Service and Ingress for timeout server
Browse files Browse the repository at this point in the history
  • Loading branch information
hdurand0710 authored and oktalz committed Sep 25, 2023
1 parent 5da8480 commit e2f797c
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 2 deletions.
14 changes: 14 additions & 0 deletions deploy/tests/integration/base-suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
package integration

import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/haproxytech/kubernetes-ingress/pkg/annotations"
c "github.com/haproxytech/kubernetes-ingress/pkg/controller"
Expand Down Expand Up @@ -164,3 +167,14 @@ func (suite *BaseSuite) StopController() {
testController.Store.Clean()
close(testController.EventChan)
}

func (suite *BaseSuite) ExpectHaproxyConfigContains(s string, count int) {
testController := suite.TestControllers[suite.T().Name()]

content, err := os.ReadFile(filepath.Join(testController.TempDir, "haproxy.cfg"))
if err != nil {
suite.T().Error(err.Error())
}
c := strings.Count(string(content), s)
suite.Exactly(count, c, fmt.Sprintf("%s is repeated %d times but expected %d", s, c, count))
}
88 changes: 88 additions & 0 deletions deploy/tests/integration/timeout-server/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2023 HAProxy Technologies LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, softwarehaproxyConfig
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package timeoutserver

import (
"testing"

"github.com/haproxytech/kubernetes-ingress/deploy/tests/integration"
"github.com/haproxytech/kubernetes-ingress/pkg/k8s"
"github.com/haproxytech/kubernetes-ingress/pkg/store"
"github.com/stretchr/testify/suite"
)

var (
appNs = "appNs"
serviceName = "appSvcName"
ingressName = "appIngName"
configMapNamespace = "haproxy-controller"
configMapName = "haproxy-kubernetes-ingress"
)

type TimeoutServerSuite struct {
integration.BaseSuite
}

func TestTimeoutServer(t *testing.T) {
t.Parallel()
suite.Run(t, new(TimeoutServerSuite))
}

func (suite *TimeoutServerSuite) BeforeTest(suiteName, testName string) {
suite.BaseSuite.BeforeTest(suiteName, testName)
// Add any needed update to the controller setting
// by updating suite.TestControllers[suite.T().Name()].XXXXX
testController := suite.TestControllers[suite.T().Name()]
testController.OSArgs.ConfigMap.Name = configMapName
testController.OSArgs.ConfigMap.Namespace = configMapNamespace
}

func newConfigMap() *store.ConfigMap {
return &store.ConfigMap{
Annotations: map[string]string{},
Namespace: configMapNamespace,
Name: configMapName,
Status: store.ADDED,
}
}

func (suite *TimeoutServerSuite) setupTest() *store.ConfigMap {
testController := suite.TestControllers[suite.T().Name()]

ns := store.Namespace{Name: appNs, Status: store.ADDED}
cm := newConfigMap()
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.NAMESPACE, Namespace: ns.Name, Data: &ns}
testController.EventChan <- k8s.SyncDataEvent{
SyncType: k8s.CONFIGMAP, Namespace: configMapNamespace, Name: configMapName, Data: newConfigMap(),
}
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.COMMAND}
controllerHasWorked := make(chan struct{})
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.COMMAND, EventProcessed: controllerHasWorked}
<-controllerHasWorked
return cm
}

func (suite *TimeoutServerSuite) fixture(events ...k8s.SyncDataEvent) {
testController := suite.TestControllers[suite.T().Name()]

// Now sending store events for test setup
for _, e := range events {
testController.EventChan <- e
}
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.COMMAND}
controllerHasWorked := make(chan struct{})
testController.EventChan <- k8s.SyncDataEvent{SyncType: k8s.COMMAND, EventProcessed: controllerHasWorked}
<-controllerHasWorked
}
166 changes: 166 additions & 0 deletions deploy/tests/integration/timeout-server/timeoutserver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright 2019 HAProxy Technologies LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package timeoutserver

import (
"github.com/haproxytech/kubernetes-ingress/pkg/k8s"
"github.com/haproxytech/kubernetes-ingress/pkg/store"
networkingv1 "k8s.io/api/networking/v1"
)

func (suite *TimeoutServerSuite) TestTimeoutServerConfigMap() {
// name := suite.T().Name()
// testController := suite.TestControllers[name]

suite.StartController()
cm := suite.setupTest()

///////////////////////////////////////
// timeout server setup in:
// - configmap only
cm.Status = store.MODIFIED
cm.Annotations["timeout-server"] = "77000"
svc := newAppSvc()
ing := newAppIngress()
events := []k8s.SyncDataEvent{
{SyncType: k8s.CONFIGMAP, Namespace: configMapNamespace, Name: configMapName, Data: cm},
{SyncType: k8s.SERVICE, Namespace: appNs, Name: serviceName, Data: svc},
{SyncType: k8s.INGRESS, Namespace: appNs, Name: ingressName, Data: ing},
}
suite.fixture(events...)
// Expected occurrences of "timeout server 77000"
// 1- defaults
// 2- backend haproxy-controller_default-local-service_http
// 3- backend appNs_appSvcName_https
suite.ExpectHaproxyConfigContains("timeout server 77000", 3)

suite.StopController()
}

func (suite *TimeoutServerSuite) TestTimeoutServerService() {
// name := suite.T().Name()
// testController := suite.TestControllers[name]

suite.StartController()
cm := suite.setupTest()

///////////////////////////////////////
// timeout server setup in:
// - configmap (77000)
// - ingress (76000)
// - app svc (75000) (appNs_appSvcName_https)
cm.Status = store.MODIFIED
cm.Annotations["timeout-server"] = "77000"
ing := newAppIngress()
ing.Annotations["timeout-server"] = "76000"
svc := newAppSvc()
svc.Annotations["timeout-server"] = "75000"

events := []k8s.SyncDataEvent{
{SyncType: k8s.CONFIGMAP, Namespace: configMapNamespace, Name: configMapName, Data: cm},
{SyncType: k8s.SERVICE, Namespace: appNs, Name: serviceName, Data: svc},
{SyncType: k8s.INGRESS, Namespace: appNs, Name: ingressName, Data: ing},
}
suite.fixture(events...)
// Expected occurrences of "timeout server 77000": #2
// 1- defaults
// 2- backend haproxy-controller_default-local-service_http
// Expected occurrences of "timeout server 75000": #1 (from service)
// 1- backend appNs_appSvcName_https
// Expected occurrences of "timeout server 76000": #0 (from ingress)
suite.ExpectHaproxyConfigContains("timeout server 77000", 2)
suite.ExpectHaproxyConfigContains("timeout server 75000", 1)
suite.ExpectHaproxyConfigContains("timeout server 76000", 0)

suite.StopController()
}

func (suite *TimeoutServerSuite) TestTimeoutServerIngress() {
// name := suite.T().Name()
// testController := suite.TestControllers[name]

suite.StartController()
cm := suite.setupTest()

///////////////////////////////////////
// timeout server setup in:
// - configmap (77000)
// - ingress (76000)
cm.Status = store.MODIFIED
cm.Annotations["timeout-server"] = "77000"
ing := newAppIngress()
ing.Annotations["timeout-server"] = "76000"
svc := newAppSvc()

events := []k8s.SyncDataEvent{
{SyncType: k8s.CONFIGMAP, Namespace: configMapNamespace, Name: configMapName, Data: cm},
{SyncType: k8s.SERVICE, Namespace: appNs, Name: serviceName, Data: svc},
{SyncType: k8s.INGRESS, Namespace: appNs, Name: ingressName, Data: ing},
}
suite.fixture(events...)
// Expected occurrences of "timeout server 77000": #2
// 1- defaults
// 2- backend haproxy-controller_default-local-service_http
// Expected occurrences of "timeout server 75000": #0 (from service)
// Expected occurrences of "timeout server 76000": #1 (from ingress)
// 1- backend appNs_appSvcName_https
suite.ExpectHaproxyConfigContains("timeout server 77000", 2)
suite.ExpectHaproxyConfigContains("timeout server 75000", 0)
suite.ExpectHaproxyConfigContains("timeout server 76000", 1)

suite.StopController()
}

func newAppSvc() *store.Service {
return &store.Service{
Annotations: map[string]string{},
Name: serviceName,
Namespace: appNs,
Ports: []store.ServicePort{
{
Name: "https",
Protocol: "TCP",
Port: 443,
Status: store.ADDED,
},
},
Status: store.ADDED,
}
}

func newAppIngress() *store.Ingress {
return &store.Ingress{
IngressCore: store.IngressCore{
APIVersion: store.NETWORKINGV1,
Name: ingressName,
Namespace: appNs,
Annotations: map[string]string{},
Rules: map[string]*store.IngressRule{
"": {
Paths: map[string]*store.IngressPath{
string(networkingv1.PathTypePrefix) + "-/": {
Path: "/",
PathTypeMatch: string(networkingv1.PathTypePrefix),
SvcNamespace: appNs,
SvcPortString: "https",
SvcName: serviceName,
},
},
},
},
},
Status: store.ADDED,
}
}
4 changes: 2 additions & 2 deletions documentation/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ This is autogenerated from [doc.yaml](doc.yaml). Description can be found in [ge
| [timeout-http-keep-alive](#timeouts) | [time](#time) | "1m" | |:large_blue_circle:|:white_circle:|:white_circle:|
| [timeout-queue](#timeouts) | [time](#time) | "5s" | |:large_blue_circle:|:white_circle:|:white_circle:|
| [timeout-server](#timeouts) | [time](#time) | "50s" | |:large_blue_circle:|:white_circle:|:white_circle:|
| [timeout-server-fin](#timeouts) | [time](#time) | | |:large_blue_circle:|:white_circle:|:white_circle:|
| [timeout-server-fin](#timeouts) | [time](#time) | | |:large_blue_circle:|:large_blue_circle:|:large_blue_circle:|
| [timeout-tunnel](#timeouts) | [time](#time) | "1h" | |:large_blue_circle:|:white_circle:|:white_circle:|
| [whitelist](#access-control) | IPs/CIDRs or pattern file | | |:large_blue_circle:|:large_blue_circle:|:white_circle:|
| [allow-list](#access-control) | IPs/CIDRs or pattern file | | |:large_blue_circle:|:large_blue_circle:|:white_circle:|
Expand Down Expand Up @@ -1836,7 +1836,7 @@ timeout-server: 5s

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

Available on: `configmap`
Available on: `configmap` `ingress` `service`

Possible values:

Expand Down
2 changes: 2 additions & 0 deletions documentation/doc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1804,6 +1804,8 @@ annotations:
- An integer with a unit of time (1 second = 1s, 1 minute = 1m, 1h = 1 hour)
applies_to:
- configmap
- ingress
- service
version_min: "1.4"
example: ["timeout-server-fin: 5s"]
- title: timeout-tunnel
Expand Down
1 change: 1 addition & 0 deletions pkg/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ func (a annImpl) Backend(b *models.Backend, s store.K8s, c certs.Certificates) [
annotations := []Annotation{
service.NewAbortOnClose("abortonclose", b),
service.NewTimeoutCheck("timeout-check", b),
service.NewTimeoutServer("timeout-server", b),
service.NewLoadBalance("load-balance", b),
service.NewCheck("check", b),
service.NewCheckInter("check-interval", b),
Expand Down
36 changes: 36 additions & 0 deletions pkg/annotations/service/timeoutServer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package service

import (
"github.com/haproxytech/client-native/v3/models"

"github.com/haproxytech/kubernetes-ingress/pkg/annotations/common"
"github.com/haproxytech/kubernetes-ingress/pkg/store"
"github.com/haproxytech/kubernetes-ingress/pkg/utils"
)

type TimeoutServer struct {
backend *models.Backend
name string
}

func NewTimeoutServer(n string, b *models.Backend) *TimeoutServer {
return &TimeoutServer{name: n, backend: b}
}

func (a *TimeoutServer) GetName() string {
return a.name
}

func (a *TimeoutServer) Process(k store.K8s, annotations ...map[string]string) error {
input := common.GetValue(a.GetName(), annotations...)
if input == "" {
a.backend.ServerTimeout = nil
return nil
}
timeout, err := utils.ParseTime(input)
if err != nil {
return err
}
a.backend.ServerTimeout = timeout
return nil
}

0 comments on commit e2f797c

Please sign in to comment.