Skip to content

Commit 4fdff2a

Browse files
oktalzGopher Bot
authored andcommitted
BUG/MINOR: prevent continuous reloads on default log config
The assignment of the log target list is now done after env.SetGlobal is executed. Previously, env.SetGlobal injected a default log target into the log list after it was already assigned to the global config. This discrepancy caused the global configuration diff to continuously detect a change, triggering an unnecessary HAProxy reload on every sync.
1 parent 8b1bbac commit 4fdff2a

2 files changed

Lines changed: 92 additions & 1 deletion

File tree

pkg/controller/global.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ func (c *HAProxyController) globalCfg() {
8181
if newGlobal.TuneSslOptions == nil {
8282
newGlobal.TuneSslOptions = &models.TuneSslOptions{}
8383
}
84-
newGlobal.LogTargetList = newLg
8584

8685
env.SetGlobal(newGlobal, &newLg, c.haproxy.Env)
86+
// SetGlobal may inject a default log target into newLg, so assign it afterwards;
87+
// otherwise the comparison misses it and triggers a reload on every sync.
88+
newGlobal.LogTargetList = newLg
8789
diff := newGlobal.Diff(*global)
8890
if len(diff) != 0 {
8991
err := c.haproxy.GlobalPushConfiguration(*newGlobal)

pkg/controller/global_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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 controller
16+
17+
import (
18+
"os"
19+
"testing"
20+
21+
"github.com/jessevdk/go-flags"
22+
"github.com/stretchr/testify/require"
23+
24+
"github.com/haproxytech/kubernetes-ingress/pkg/annotations"
25+
"github.com/haproxytech/kubernetes-ingress/pkg/haproxy"
26+
"github.com/haproxytech/kubernetes-ingress/pkg/haproxy/env"
27+
"github.com/haproxytech/kubernetes-ingress/pkg/haproxy/instance"
28+
"github.com/haproxytech/kubernetes-ingress/pkg/haproxy/rules"
29+
"github.com/haproxytech/kubernetes-ingress/pkg/store"
30+
"github.com/haproxytech/kubernetes-ingress/pkg/utils"
31+
)
32+
33+
// TestGlobalCfgConvergesWithoutLogConfig reproduces the periodic-reload bug: a fresh
34+
// deployment with no log configuration reloaded on every sync because the injected
35+
// default log target kept the global diff non-empty forever.
36+
func TestGlobalCfgConvergesWithoutLogConfig(t *testing.T) {
37+
c := buildGlobalTestController(t)
38+
39+
// First sync legitimately reloads (sets defaults + default log target).
40+
require.NoError(t, c.haproxy.APIStartTransaction())
41+
c.globalCfg()
42+
require.NoError(t, c.haproxy.APICommitTransaction())
43+
c.haproxy.APIDisposeTransaction()
44+
instance.Reset()
45+
46+
// Steady-state sync: nothing changed, so no reload must be required.
47+
require.NoError(t, c.haproxy.APIStartTransaction())
48+
c.globalCfg()
49+
require.NoError(t, c.haproxy.APICommitTransaction())
50+
c.haproxy.APIDisposeTransaction()
51+
52+
require.False(t, instance.NeedReload(),
53+
"steady-state globalCfg must not request a reload when nothing changed")
54+
}
55+
56+
func buildGlobalTestController(t *testing.T) *HAProxyController {
57+
t.Helper()
58+
tempDir := t.TempDir()
59+
60+
var osArgs utils.OSArgs
61+
os.Args = []string{os.Args[0], "-e", "-t", "--config-dir=" + tempDir}
62+
parser := flags.NewParser(&osArgs, flags.IgnoreUnknown)
63+
_, err := parser.Parse()
64+
require.NoError(t, err)
65+
66+
cfg, err := os.ReadFile("../../fs/usr/local/etc/haproxy/haproxy.cfg")
67+
require.NoError(t, err)
68+
69+
haproxyEnv := env.Env{
70+
CfgDir: tempDir,
71+
Proxies: env.Proxies{
72+
FrontHTTP: "http",
73+
FrontHTTPS: "https",
74+
FrontSSL: "ssl",
75+
BackSSL: "ssl-backend",
76+
},
77+
}
78+
79+
h, err := haproxy.New(osArgs, haproxyEnv, cfg, nil, nil, rules.New())
80+
require.NoError(t, err)
81+
82+
return &HAProxyController{
83+
osArgs: osArgs,
84+
haproxy: h,
85+
podNamespace: os.Getenv("POD_NAMESPACE"),
86+
store: store.NewK8sStore(osArgs),
87+
annotations: annotations.New(),
88+
}
89+
}

0 commit comments

Comments
 (0)