Skip to content

Commit

Permalink
BUG/MEDIUM: fix haproxy version with multiple runtime clients
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hdurand0710 committed Nov 19, 2024
1 parent 43efe63 commit f219b5b
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.idea/
bin/golangci-lint
bin/swagger
.envrc
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/sirkon/dst v0.26.4
github.com/stretchr/testify v1.9.0
golang.org/x/sync v0.8.0
golang.org/x/text v0.19.0
golang.org/x/tools v0.26.0
gopkg.in/yaml.v3 v3.0.1
Expand All @@ -39,6 +40,5 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
go.mongodb.org/mongo-driver v1.17.1 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
)
42 changes: 25 additions & 17 deletions runtime/runtime_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,18 @@ import (
"os"
"path/filepath"
"strings"
"sync"

native_errors "github.com/haproxytech/client-native/v6/errors"
"github.com/haproxytech/client-native/v6/misc"
"github.com/haproxytech/client-native/v6/models"
"github.com/haproxytech/client-native/v6/runtime/options"
"golang.org/x/sync/singleflight"
)

// Client handles multiple HAProxy clients
type client struct {
haproxyVersion *HAProxyVersion
options options.RuntimeOptions
runtime *SingleRuntime
options options.RuntimeOptions
runtime *SingleRuntime
}

const (
Expand Down Expand Up @@ -90,50 +89,59 @@ func (c *client) GetInfo() (models.ProcessInfo, error) {
return result, nil
}

var versionSync sync.Once //nolint:gochecknoglobals
var (
haproxyVersion *HAProxyVersion //nolint:gochecknoglobals
versionKey = "version" //nolint:gochecknoglobals
versionSfg = singleflight.Group{} //nolint:gochecknoglobals
)

// GetVersion returns info from the socket
func (c *client) GetVersion() (HAProxyVersion, error) {
var err error
versionSync.Do(func() {
if haproxyVersion != nil {
return *haproxyVersion, nil
}
_, err, _ = versionSfg.Do(versionKey, func() (interface{}, error) {
version := &HAProxyVersion{}
var response string
response, err = c.runtime.ExecuteRaw("show info")
if err != nil {
return
return HAProxyVersion{}, err
}
for _, line := range strings.Split(response, "\n") {
if strings.HasPrefix(line, "Version: ") {
err = version.ParseHAProxyVersion(strings.TrimPrefix(line, "Version: "))
if err != nil {
return
return HAProxyVersion{}, err
}
c.haproxyVersion = version
return
haproxyVersion = version
return HAProxyVersion{}, err
}
// Starting with HAProxy 3.0, there is no more "Version:" prefix.
if len(line) > 0 && line[0] >= '3' && line[0] <= '9' {
err = version.ParseHAProxyVersion(line)
if err == nil {
c.haproxyVersion = version
haproxyVersion = version
}
return
return HAProxyVersion{}, err
}
}
err = errors.New("version data not found")
return HAProxyVersion{}, err // it's dereferenced in IsVersionBiggerOrEqual
})
if err != nil {
return HAProxyVersion{}, err
}

if c.haproxyVersion == nil {
if haproxyVersion == nil {
return HAProxyVersion{}, errors.New("version data not found")
}
return *c.haproxyVersion, err

return *haproxyVersion, err
}

func (c *client) IsVersionBiggerOrEqual(minimumVersion *HAProxyVersion) bool {
return IsBiggerOrEqual(minimumVersion, c.haproxyVersion)
return IsBiggerOrEqual(minimumVersion, haproxyVersion)
}

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

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

0 comments on commit f219b5b

Please sign in to comment.