Skip to content

Commit f219b5b

Browse files
committed
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 43efe63 commit f219b5b

File tree

3 files changed

+27
-18
lines changed

3 files changed

+27
-18
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
@@ -18,6 +18,7 @@ require (
1818
github.com/mitchellh/mapstructure v1.5.0
1919
github.com/sirkon/dst v0.26.4
2020
github.com/stretchr/testify v1.9.0
21+
golang.org/x/sync v0.8.0
2122
golang.org/x/text v0.19.0
2223
golang.org/x/tools v0.26.0
2324
gopkg.in/yaml.v3 v3.0.1
@@ -39,6 +40,5 @@ 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
)

runtime/runtime_client.go

+25-17
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,18 @@ import (
2323
"os"
2424
"path/filepath"
2525
"strings"
26-
"sync"
2726

2827
native_errors "github.com/haproxytech/client-native/v6/errors"
2928
"github.com/haproxytech/client-native/v6/misc"
3029
"github.com/haproxytech/client-native/v6/models"
3130
"github.com/haproxytech/client-native/v6/runtime/options"
31+
"golang.org/x/sync/singleflight"
3232
)
3333

3434
// Client handles multiple HAProxy clients
3535
type client struct {
36-
haproxyVersion *HAProxyVersion
37-
options options.RuntimeOptions
38-
runtime *SingleRuntime
36+
options options.RuntimeOptions
37+
runtime *SingleRuntime
3938
}
4039

4140
const (
@@ -90,50 +89,59 @@ func (c *client) GetInfo() (models.ProcessInfo, error) {
9089
return result, nil
9190
}
9291

93-
var versionSync sync.Once //nolint:gochecknoglobals
92+
var (
93+
haproxyVersion *HAProxyVersion //nolint:gochecknoglobals
94+
versionKey = "version" //nolint:gochecknoglobals
95+
versionSfg = singleflight.Group{} //nolint:gochecknoglobals
96+
)
9497

9598
// GetVersion returns info from the socket
9699
func (c *client) GetVersion() (HAProxyVersion, error) {
97100
var err error
98-
versionSync.Do(func() {
101+
if haproxyVersion != nil {
102+
return *haproxyVersion, nil
103+
}
104+
_, err, _ = versionSfg.Do(versionKey, func() (interface{}, error) {
99105
version := &HAProxyVersion{}
100106
var response string
101107
response, err = c.runtime.ExecuteRaw("show info")
102108
if err != nil {
103-
return
109+
return HAProxyVersion{}, err
104110
}
105111
for _, line := range strings.Split(response, "\n") {
106112
if strings.HasPrefix(line, "Version: ") {
107113
err = version.ParseHAProxyVersion(strings.TrimPrefix(line, "Version: "))
108114
if err != nil {
109-
return
115+
return HAProxyVersion{}, err
110116
}
111-
c.haproxyVersion = version
112-
return
117+
haproxyVersion = version
118+
return HAProxyVersion{}, err
113119
}
114120
// Starting with HAProxy 3.0, there is no more "Version:" prefix.
115121
if len(line) > 0 && line[0] >= '3' && line[0] <= '9' {
116122
err = version.ParseHAProxyVersion(line)
117123
if err == nil {
118-
c.haproxyVersion = version
124+
haproxyVersion = version
119125
}
120-
return
126+
return HAProxyVersion{}, err
121127
}
122128
}
123129
err = errors.New("version data not found")
130+
return HAProxyVersion{}, err // it's dereferenced in IsVersionBiggerOrEqual
124131
})
125132
if err != nil {
126133
return HAProxyVersion{}, err
127134
}
128135

129-
if c.haproxyVersion == nil {
136+
if haproxyVersion == nil {
130137
return HAProxyVersion{}, errors.New("version data not found")
131138
}
132-
return *c.haproxyVersion, err
139+
140+
return *haproxyVersion, err
133141
}
134142

135143
func (c *client) IsVersionBiggerOrEqual(minimumVersion *HAProxyVersion) bool {
136-
return IsBiggerOrEqual(minimumVersion, c.haproxyVersion)
144+
return IsBiggerOrEqual(minimumVersion, haproxyVersion)
137145
}
138146

139147
// Reloads HAProxy's configuration file. Similar to SIGUSR2. Returns the startup logs.
@@ -144,7 +152,7 @@ func (c *client) Reload() (string, error) {
144152
return "", errors.New("cannot reload: not connected to a master socket")
145153
}
146154
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 7}) {
147-
return "", errors.New("cannot reload: requires HAProxy 2.7 or later")
155+
return "", fmt.Errorf("cannot reload: requires HAProxy 2.7 or later but current version is %v", haproxyVersion)
148156
}
149157

150158
output, err := c.runtime.ExecuteMaster("reload")
@@ -218,7 +226,7 @@ func (c *client) AddServer(backend, name, attributes string) error {
218226
return errors.New("no valid runtime found")
219227
}
220228
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 6}) {
221-
return errors.New("this operation requires HAProxy 2.6 or later")
229+
return fmt.Errorf("this operation requires HAProxy 2.6 or later but current version is %v", haproxyVersion)
222230
}
223231
err := c.runtime.AddServer(backend, name, attributes)
224232
if err != nil {

0 commit comments

Comments
 (0)