Skip to content

Commit f870ed6

Browse files
committed
MINOR: improve HAProxy runtime version checks
- Add a fallback in Reload() to retry fetching the HAProxy version if the initial startup probe failed, preventing permanent gate failure. - Replace explicit struct instantiations in runtime_client.go with semver.MustParse() for cleaner and more robust version comparisons.
1 parent 2ceaaaa commit f870ed6

7 files changed

Lines changed: 110 additions & 185 deletions

File tree

.aspell.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ allowed:
101101
- infos
102102
- ipam
103103
- istanbul
104+
- instantiations
104105
- jose
105106
- json
106107
- jsonpath

e2e/runtime/version/version_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ func (s *MajorVersionInRuntime) TestExample() {
3030
if err != nil {
3131
s.FailNow(err.Error())
3232
}
33-
versionStr := fmt.Sprintf("%d.%d", version.Major, version.Minor)
33+
versionStr := fmt.Sprintf("%d.%d", version.Major(), version.Minor())
3434
s.Equal(versionStr, s.haproxy_version)
3535
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/haproxytech/client-native/v5
33
go 1.25.0
44

55
require (
6+
github.com/Masterminds/semver/v3 v3.5.0
67
github.com/go-faker/faker/v4 v4.7.0
78
github.com/go-openapi/errors v0.22.7
89
github.com/go-openapi/strfmt v0.26.2

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
2+
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
13
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
24
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
35
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=

runtime/runtime_client.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"path/filepath"
2424
"strings"
2525

26+
"github.com/Masterminds/semver/v3"
2627
"github.com/google/go-cmp/cmp"
2728
native_errors "github.com/haproxytech/client-native/v5/errors"
2829
"github.com/haproxytech/client-native/v5/misc"
@@ -158,7 +159,12 @@ func (c *client) Reload() (string, error) {
158159
if c.options.MasterSocketData == nil {
159160
return "", fmt.Errorf("cannot reload: not connected to a master socket")
160161
}
161-
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 7}) {
162+
// Init-time version probe is fire-and-forget; retry here so a transient
163+
// startup failure doesn't permanently disable the gate.
164+
if haproxyVersion == nil {
165+
_, _ = c.GetVersion()
166+
}
167+
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Version: semver.MustParse("2.7")}) {
162168
return "", fmt.Errorf("cannot reload: requires HAProxy 2.7 or later but current version is %v", haproxyVersion)
163169
}
164170

@@ -237,7 +243,7 @@ func (c *client) AddServer(backend, name, attributes string) error {
237243
if len(c.runtimes) == 0 {
238244
return fmt.Errorf("no valid runtimes found")
239245
}
240-
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 6}) {
246+
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Version: semver.MustParse("2.6")}) {
241247
return fmt.Errorf("this operation requires HAProxy 2.6 or later but current version is %v", haproxyVersion)
242248
}
243249
for _, runtime := range c.runtimes {
@@ -254,8 +260,8 @@ func (c *client) DeleteServer(backend, name string) error {
254260
if len(c.runtimes) == 0 {
255261
return fmt.Errorf("no valid runtimes found")
256262
}
257-
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 6}) {
258-
return fmt.Errorf("this operation requires HAProxy 2.6 or later")
263+
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Version: semver.MustParse("2.6")}) {
264+
return errors.New("this operation requires HAProxy 2.6 or later")
259265
}
260266
for _, runtime := range c.runtimes {
261267
err := runtime.DeleteServer(backend, name)
@@ -839,7 +845,7 @@ func (c *client) AddMapPayloadVersioned(name string, entries models.MapEntries)
839845
return err
840846
}
841847
canAtomicUpdate := false
842-
v := HAProxyVersion{Major: 2, Minor: 4}
848+
v := HAProxyVersion{Version: semver.MustParse("2.4")}
843849
if c.IsVersionBiggerOrEqual(&v) {
844850
canAtomicUpdate = true
845851
}
@@ -1142,7 +1148,7 @@ func (c *client) AddACLAtomic(aclID string, entries models.ACLFilesEntries) erro
11421148
if len(c.runtimes) == 0 {
11431149
return fmt.Errorf("no valid runtimes found")
11441150
}
1145-
v := HAProxyVersion{Major: 2, Minor: 4}
1151+
v := HAProxyVersion{Version: semver.MustParse("2.4")}
11461152
if !c.IsVersionBiggerOrEqual(&v) {
11471153
return fmt.Errorf("not supported for HAProxy versions lower than 2.4 %w", native_errors.ErrGeneral)
11481154
}

runtime/version.go

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,66 +16,42 @@
1616
package runtime
1717

1818
import (
19-
"fmt"
20-
"strconv"
2119
"strings"
20+
21+
"github.com/Masterminds/semver/v3"
2222
)
2323

2424
type HAProxyVersion struct {
25-
Commit string
26-
Version string
27-
Major int
28-
Minor int
29-
Patch int
25+
*semver.Version
26+
27+
Commit string
3028
}
3129

3230
func (v *HAProxyVersion) ParseHAProxyVersion(version string) error {
33-
v.Version = version
34-
35-
parts := strings.SplitN(version, "-", 2)
36-
data := strings.SplitN(parts[0], ".", 3)
37-
major, err := strconv.Atoi(data[0])
38-
if err == nil {
39-
v.Major = major
40-
}
41-
if len(data) > 1 {
42-
minor, err := strconv.Atoi(data[1])
43-
if err == nil {
44-
v.Minor = minor
45-
}
46-
}
47-
if len(data) > 2 {
48-
patch, err := strconv.Atoi(data[2])
49-
if err == nil {
50-
v.Patch = patch
31+
sv, err := semver.NewVersion(version)
32+
if err != nil {
33+
return err
34+
}
35+
v.Version = sv
36+
// Commit lives in the prerelease tail: "dev6-2f6f36-13" → "2f6f36",
37+
// "a42e6c-11" → "a42e6c", "34b2b10" → "34b2b10".
38+
if pre := sv.Prerelease(); pre != "" {
39+
parts := strings.Split(pre, "-")
40+
if len(parts) < 2 {
41+
v.Commit = parts[0]
42+
} else {
43+
v.Commit = parts[len(parts)-2]
5144
}
5245
}
53-
if len(parts) < 2 {
54-
return fmt.Errorf("version is not in correct format [%s]", version)
55-
}
56-
data = strings.Split(parts[1], "-")
57-
if len(data) < 2 {
58-
v.Commit = data[0]
59-
} else {
60-
v.Commit = data[len(data)-2]
61-
}
6246
return nil
6347
}
6448

6549
func IsBiggerOrEqual(minimum, current *HAProxyVersion) bool {
66-
if current == nil {
50+
if current == nil || current.Version == nil {
6751
return false
6852
}
69-
if current.Major > minimum.Major {
53+
if minimum == nil || minimum.Version == nil {
7054
return true
7155
}
72-
if current.Major == minimum.Major {
73-
switch {
74-
case current.Minor > minimum.Minor:
75-
return true
76-
case current.Minor == minimum.Minor:
77-
return current.Patch >= minimum.Patch
78-
}
79-
}
80-
return false
56+
return current.GreaterThanEqual(minimum.Version)
8157
}

0 commit comments

Comments
 (0)