Skip to content

Commit ba7d329

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 b55ad94 commit ba7d329

7 files changed

Lines changed: 109 additions & 184 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/v6
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.1

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: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"path/filepath"
2525
"strings"
2626

27+
"github.com/Masterminds/semver/v3"
2728
native_errors "github.com/haproxytech/client-native/v6/errors"
2829
"github.com/haproxytech/client-native/v6/misc"
2930
"github.com/haproxytech/client-native/v6/models"
@@ -163,7 +164,12 @@ func (c *client) Reload() (string, error) {
163164
if c.options.MasterSocketData == nil {
164165
return "", errors.New("cannot reload: not connected to a master socket")
165166
}
166-
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 7}) {
167+
// Init-time version probe is fire-and-forget; retry here so a transient
168+
// startup failure doesn't permanently disable the gate.
169+
if haproxyVersion == nil {
170+
_, _ = c.GetVersion()
171+
}
172+
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Version: semver.MustParse("2.7")}) {
167173
return "", fmt.Errorf("cannot reload: requires HAProxy 2.7 or later but current version is %v", haproxyVersion)
168174
}
169175

@@ -242,7 +248,7 @@ func (c *client) AddServer(backend, name, attributes string) error {
242248
if !c.runtime.IsValid() {
243249
return errors.New("no valid runtime found")
244250
}
245-
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 6}) {
251+
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Version: semver.MustParse("2.6")}) {
246252
return fmt.Errorf("this operation requires HAProxy 2.6 or later but current version is %v", haproxyVersion)
247253
}
248254
err := c.runtime.AddServer(backend, name, attributes)
@@ -257,7 +263,7 @@ func (c *client) DeleteServer(backend, name string) error {
257263
if !c.runtime.IsValid() {
258264
return errors.New("no valid runtime found")
259265
}
260-
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Major: 2, Minor: 6}) {
266+
if !c.IsVersionBiggerOrEqual(&HAProxyVersion{Version: semver.MustParse("2.6")}) {
261267
return errors.New("this operation requires HAProxy 2.6 or later")
262268
}
263269
err := c.runtime.DeleteServer(backend, name)
@@ -711,7 +717,7 @@ func (c *client) AddMapPayloadVersioned(name string, entries models.MapEntries)
711717
return fmt.Errorf("%s %w", c.runtime.socketPath, err)
712718
}
713719
canAtomicUpdate := false
714-
v := HAProxyVersion{Major: 2, Minor: 4}
720+
v := HAProxyVersion{Version: semver.MustParse("2.4")}
715721
if c.IsVersionBiggerOrEqual(&v) {
716722
canAtomicUpdate = true
717723
}
@@ -953,7 +959,7 @@ func (c *client) AddACLAtomic(aclID string, entries models.ACLFilesEntries) erro
953959
if !c.runtime.IsValid() {
954960
return errors.New("no valid runtime found")
955961
}
956-
v := HAProxyVersion{Major: 2, Minor: 4}
962+
v := HAProxyVersion{Version: semver.MustParse("2.4")}
957963
if !c.IsVersionBiggerOrEqual(&v) {
958964
return fmt.Errorf("not supported for HAProxy versions lower than 2.4 %w", native_errors.ErrGeneral)
959965
}

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)