Skip to content

Commit 85c25d2

Browse files
committedNov 19, 2024·
BUG/MEDIUM: fix haproxy version with multiple runtime clients
As haproxy version was a field of the client struct and the version set in a sync.Once function, the version was nil for all runtime clients except the first one. It could also happend that the first call was done with DoNotCheckRuntimeOnInit option and then, it could also happen that even the first one was nil.
1 parent 75c1295 commit 85c25d2

File tree

3 files changed

+28
-20
lines changed

3 files changed

+28
-20
lines changed
 

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
.idea/
33
bin/golangci-lint
44
bin/swagger
5+
.envrc

‎go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/pkg/errors v0.9.1
2020
github.com/sirkon/dst v0.26.4
2121
github.com/stretchr/testify v1.9.0
22+
golang.org/x/sync v0.8.0
2223
golang.org/x/text v0.19.0
2324
golang.org/x/tools v0.26.0
2425
)
@@ -39,7 +40,6 @@ require (
3940
github.com/pmezard/go-difflib v1.0.0 // indirect
4041
go.mongodb.org/mongo-driver v1.17.1 // indirect
4142
golang.org/x/mod v0.21.0 // indirect
42-
golang.org/x/sync v0.8.0 // indirect
4343
golang.org/x/sys v0.26.0 // indirect
4444
gopkg.in/yaml.v3 v3.0.1 // indirect
4545
)

‎runtime/runtime_client.go

+26-19
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,20 @@ import (
2222
"os"
2323
"path/filepath"
2424
"strings"
25-
"sync"
2625

2726
"github.com/google/go-cmp/cmp"
28-
"github.com/pkg/errors"
29-
3027
native_errors "github.com/haproxytech/client-native/v5/errors"
3128
"github.com/haproxytech/client-native/v5/misc"
3229
"github.com/haproxytech/client-native/v5/models"
3330
"github.com/haproxytech/client-native/v5/runtime/options"
31+
"github.com/pkg/errors"
32+
"golang.org/x/sync/singleflight"
3433
)
3534

3635
// Client handles multiple HAProxy clients
3736
type client struct {
38-
haproxyVersion *HAProxyVersion
39-
options options.RuntimeOptions
40-
runtimes []*SingleRuntime
37+
options options.RuntimeOptions
38+
runtimes []*SingleRuntime
4139
}
4240

4341
const (
@@ -104,44 +102,53 @@ func (c *client) GetInfo() (models.ProcessInfos, error) {
104102
return result, nil
105103
}
106104

107-
var versionSync sync.Once //nolint:gochecknoglobals
105+
var (
106+
haproxyVersion *HAProxyVersion //nolint:gochecknoglobals
107+
versionKey = "version" //nolint:gochecknoglobals
108+
versionSfg = singleflight.Group{} //nolint:gochecknoglobals
109+
)
108110

109111
// GetVersion returns info from the socket
110112
func (c *client) GetVersion() (HAProxyVersion, error) {
111113
var err error
112-
versionSync.Do(func() {
114+
if haproxyVersion != nil {
115+
return *haproxyVersion, nil
116+
}
117+
_, err, _ = versionSfg.Do(versionKey, func() (interface{}, error) {
113118
version := &HAProxyVersion{}
114119
for _, runtime := range c.runtimes {
115120
var response string
116121
response, err = runtime.ExecuteRaw("show info")
117122
if err != nil {
118-
return
123+
return HAProxyVersion{}, err
119124
}
120125
for _, line := range strings.Split(response, "\n") {
121126
if strings.HasPrefix(line, "Version: ") {
122127
err = version.ParseHAProxyVersion(strings.TrimPrefix(line, "Version: "))
123128
if err != nil {
124-
return
129+
return HAProxyVersion{}, err
125130
}
126-
c.haproxyVersion = version
127-
return
131+
haproxyVersion = version
132+
return haproxyVersion, nil
128133
}
129134
}
130135
}
131-
err = fmt.Errorf("version data not found")
136+
err = errors.New("version data not found")
137+
return HAProxyVersion{}, err // it's dereferenced in IsVersionBiggerOrEqual
132138
})
133139
if err != nil {
134140
return HAProxyVersion{}, err
135141
}
136142

137-
if c.haproxyVersion == nil {
138-
return HAProxyVersion{}, fmt.Errorf("version data not found")
143+
if haproxyVersion == nil {
144+
return HAProxyVersion{}, errors.New("version data not found")
139145
}
140-
return *c.haproxyVersion, err
146+
147+
return *haproxyVersion, err
141148
}
142149

143150
func (c *client) IsVersionBiggerOrEqual(minimumVersion *HAProxyVersion) bool {
144-
return IsBiggerOrEqual(minimumVersion, c.haproxyVersion)
151+
return IsBiggerOrEqual(minimumVersion, haproxyVersion)
145152
}
146153

147154
// Reloads HAProxy's configuration file. Similar to SIGUSR2. Returns the startup logs.
@@ -152,7 +159,7 @@ func (c *client) Reload() (string, error) {
152159
return "", fmt.Errorf("cannot reload: not connected to a master socket")
153160
}
154161
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 7}) {
155-
return "", fmt.Errorf("cannot reload: requires HAProxy 2.7 or later")
162+
return "", fmt.Errorf("cannot reload: requires HAProxy 2.7 or later but current version is %v", haproxyVersion)
156163
}
157164

158165
for _, runtime := range c.runtimes {
@@ -229,7 +236,7 @@ func (c *client) AddServer(backend, name, attributes string) error {
229236
return fmt.Errorf("no valid runtimes found")
230237
}
231238
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 6}) {
232-
return fmt.Errorf("this operation requires HAProxy 2.6 or later")
239+
return fmt.Errorf("this operation requires HAProxy 2.6 or later but current version is %v", haproxyVersion)
233240
}
234241
for _, runtime := range c.runtimes {
235242
err := runtime.AddServer(backend, name, attributes)

0 commit comments

Comments
 (0)
Please sign in to comment.