Skip to content

Commit 9eec2d8

Browse files
committed
MAJOR: reread HAProxy runtime if path changes in config file
1 parent 342b241 commit 9eec2d8

3 files changed

Lines changed: 243 additions & 146 deletions

File tree

client-native/cn.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package cn
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strconv"
7+
"sync"
8+
9+
"github.com/haproxytech/client-native/v3/configuration"
10+
configuration_options "github.com/haproxytech/client-native/v3/configuration/options"
11+
runtime_api "github.com/haproxytech/client-native/v3/runtime"
12+
runtime_options "github.com/haproxytech/client-native/v3/runtime/options"
13+
parser "github.com/haproxytech/config-parser/v4"
14+
"github.com/haproxytech/config-parser/v4/types"
15+
16+
dataplaneapi_config "github.com/haproxytech/dataplaneapi/configuration"
17+
"github.com/haproxytech/dataplaneapi/log"
18+
"github.com/haproxytech/dataplaneapi/misc"
19+
)
20+
21+
var (
22+
socketsList = map[int]string{}
23+
muSocketsList sync.Mutex
24+
)
25+
26+
func ConfigureConfigurationClient(haproxyOptions dataplaneapi_config.HAProxyConfiguration, mWorker bool) (configuration.Configuration, error) {
27+
confClient, err := configuration.New(context.Background(),
28+
configuration_options.ConfigurationFile(haproxyOptions.ConfigFile),
29+
configuration_options.HAProxyBin(haproxyOptions.HAProxy),
30+
configuration_options.Backups(haproxyOptions.BackupsNumber),
31+
configuration_options.UsePersistentTransactions,
32+
configuration_options.TransactionsDir(haproxyOptions.TransactionDir),
33+
configuration_options.ValidateCmd(haproxyOptions.ValidateCmd),
34+
configuration_options.MasterWorker,
35+
configuration_options.UseMd5Hash,
36+
)
37+
if err != nil {
38+
return nil, fmt.Errorf("error setting up configuration client: %s", err.Error())
39+
}
40+
41+
p := confClient.Parser()
42+
comments, err := p.Get(parser.Comments, parser.CommentsSectionName, "#")
43+
insertDisclaimer := false
44+
if err != nil {
45+
insertDisclaimer = true
46+
}
47+
data, ok := comments.([]types.Comments)
48+
if !ok {
49+
insertDisclaimer = true
50+
} else if len(data) == 0 || data[0].Value != "Dataplaneapi managed File" {
51+
insertDisclaimer = true
52+
}
53+
if insertDisclaimer {
54+
commentsNew := types.Comments{Value: "Dataplaneapi managed File"}
55+
err = p.Insert(parser.Comments, parser.CommentsSectionName, "#", commentsNew, 0)
56+
if err != nil {
57+
return nil, fmt.Errorf("error setting up configuration client: %s", err.Error())
58+
}
59+
commentsNew = types.Comments{Value: "changing file directly can cause a conflict if dataplaneapi is running"}
60+
err = p.Insert(parser.Comments, parser.CommentsSectionName, "#", commentsNew, 1)
61+
if err != nil {
62+
return nil, fmt.Errorf("error setting up configuration client: %s", err.Error())
63+
}
64+
}
65+
66+
return confClient, nil
67+
}
68+
69+
func ConfigureRuntimeClient(ctx context.Context, confClient configuration.Configuration, haproxyOptions dataplaneapi_config.HAProxyConfiguration) runtime_api.Runtime {
70+
mapsDir := runtime_options.MapsDir(haproxyOptions.MapsDir)
71+
var runtimeClient runtime_api.Runtime
72+
73+
_, globalConf, err := confClient.GetGlobalConfiguration("")
74+
75+
// First try to setup master runtime socket
76+
if err == nil {
77+
var err error
78+
// If master socket is set and a valid unix socket, use only this
79+
if haproxyOptions.MasterRuntime != "" && misc.IsUnixSocketAddr(haproxyOptions.MasterRuntime) {
80+
masterSocket := haproxyOptions.MasterRuntime
81+
// if nbproc is set, set nbproc sockets
82+
if globalConf.Nbproc > 0 {
83+
nbproc := int(globalConf.Nbproc)
84+
ms := runtime_options.MasterSocket(masterSocket, nbproc)
85+
runtimeClient, err = runtime_api.New(ctx, mapsDir, ms)
86+
if err == nil {
87+
return runtimeClient
88+
}
89+
log.Warningf("Error setting up runtime client with master socket: %s : %s", masterSocket, err.Error())
90+
} else {
91+
// if nbproc is not set, use master socket with 1 process
92+
ms := runtime_options.MasterSocket(masterSocket, 1)
93+
runtimeClient, err = runtime_api.New(ctx, mapsDir, ms)
94+
if err == nil {
95+
return runtimeClient
96+
}
97+
log.Warningf("Error setting up runtime client with master socket: %s : %s", masterSocket, err.Error())
98+
}
99+
}
100+
runtimeAPIs := globalConf.RuntimeAPIs
101+
// if no master socket set, read from first valid socket if nbproc <= 1
102+
if globalConf.Nbproc <= 1 {
103+
sockets := make(map[int]string)
104+
for _, r := range runtimeAPIs {
105+
if misc.IsUnixSocketAddr(*r.Address) {
106+
sockets[1] = *r.Address
107+
socketsL := runtime_options.Sockets(sockets)
108+
runtimeClient, err = runtime_api.New(ctx, mapsDir, socketsL)
109+
if err == nil {
110+
muSocketsList.Lock()
111+
socketsList = sockets
112+
muSocketsList.Unlock()
113+
return runtimeClient
114+
}
115+
log.Warningf("Error setting up runtime client with socket: %s : %s", *r.Address, err.Error())
116+
}
117+
}
118+
} else {
119+
// else try to find process specific sockets and set them up
120+
sockets := make(map[int]string)
121+
for _, r := range runtimeAPIs {
122+
//nolint:govet
123+
if misc.IsUnixSocketAddr(*r.Address) && r.Process != "" {
124+
process, err := strconv.ParseInt(r.Process, 10, 64)
125+
if err == nil {
126+
sockets[int(process)] = *r.Address
127+
}
128+
}
129+
}
130+
// no process specific settings found, Issue a warning and return empty runtime client
131+
if len(sockets) == 0 {
132+
log.Warning("Runtime API not configured, found multiple processes and no stats sockets bound to them.")
133+
return runtimeClient
134+
// use only found process specific sockets issue a warning if not all processes have a socket configured
135+
}
136+
if len(sockets) < int(globalConf.Nbproc) {
137+
log.Warning("Runtime API not configured properly, there are more processes then configured sockets")
138+
}
139+
140+
socketLst := runtime_options.Sockets(sockets)
141+
runtimeClient, err = runtime_api.New(ctx, mapsDir, socketLst)
142+
if err == nil {
143+
return runtimeClient
144+
}
145+
log.Warningf("Error setting up runtime client with sockets: %v : %s", sockets, err.Error())
146+
147+
}
148+
if err != nil {
149+
log.Warning("Runtime API not configured, not using it: " + err.Error())
150+
} else {
151+
log.Warning("Runtime API not configured, not using it")
152+
}
153+
return runtimeClient
154+
}
155+
log.Warning("Cannot read runtime API configuration, not using it")
156+
return runtimeClient
157+
}

configure_data_plane.go

Lines changed: 5 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@ package dataplaneapi
2020
import (
2121
"context"
2222
"crypto/tls"
23-
"fmt"
2423
"io"
2524
"net/http"
2625
"os"
2726
"os/signal"
2827
"runtime/debug"
29-
"strconv"
3028
"strings"
3129
"sync"
3230
"syscall"
@@ -38,21 +36,16 @@ import (
3836
"github.com/go-openapi/runtime/middleware"
3937
"github.com/go-openapi/swag"
4038
client_native "github.com/haproxytech/client-native/v3"
41-
"github.com/haproxytech/client-native/v3/configuration"
42-
configuration_options "github.com/haproxytech/client-native/v3/configuration/options"
4339
"github.com/haproxytech/client-native/v3/models"
4440
"github.com/haproxytech/client-native/v3/options"
45-
runtime_api "github.com/haproxytech/client-native/v3/runtime"
46-
runtime_options "github.com/haproxytech/client-native/v3/runtime/options"
4741
"github.com/haproxytech/client-native/v3/spoe"
4842
"github.com/haproxytech/client-native/v3/storage"
49-
parser "github.com/haproxytech/config-parser/v4"
50-
"github.com/haproxytech/config-parser/v4/types"
5143
"github.com/haproxytech/dataplaneapi/log"
5244
jsoniter "github.com/json-iterator/go"
5345
"github.com/rs/cors"
5446

5547
"github.com/haproxytech/dataplaneapi/adapters"
48+
cn "github.com/haproxytech/dataplaneapi/client-native"
5649
dataplaneapi_config "github.com/haproxytech/dataplaneapi/configuration"
5750
service_discovery "github.com/haproxytech/dataplaneapi/discovery"
5851
"github.com/haproxytech/dataplaneapi/handlers"
@@ -837,12 +830,12 @@ func serverShutdown() {
837830

838831
func configureNativeClient(cyx context.Context, haproxyOptions dataplaneapi_config.HAProxyConfiguration, mWorker bool) client_native.HAProxyClient {
839832
// Initialize HAProxy native client
840-
confClient, err := configureConfigurationClient(haproxyOptions, mWorker)
833+
confClient, err := cn.ConfigureConfigurationClient(haproxyOptions, mWorker)
841834
if err != nil {
842835
log.Fatalf("Error initializing configuration client: %v", err)
843836
}
844837

845-
runtimeClient := configureRuntimeClient(cyx, confClient, haproxyOptions)
838+
runtimeClient := cn.ConfigureRuntimeClient(cyx, confClient, haproxyOptions)
846839

847840
opt := []options.Option{
848841
options.Configuration(confClient),
@@ -900,137 +893,6 @@ func configureNativeClient(cyx context.Context, haproxyOptions dataplaneapi_conf
900893
return client
901894
}
902895

903-
func configureConfigurationClient(haproxyOptions dataplaneapi_config.HAProxyConfiguration, mWorker bool) (configuration.Configuration, error) {
904-
confClient, err := configuration.New(context.Background(),
905-
configuration_options.ConfigurationFile(haproxyOptions.ConfigFile),
906-
configuration_options.HAProxyBin(haproxyOptions.HAProxy),
907-
configuration_options.Backups(haproxyOptions.BackupsNumber),
908-
// configuration_options.UseModelsValidation this is false
909-
configuration_options.UsePersistentTransactions,
910-
configuration_options.TransactionsDir(haproxyOptions.TransactionDir),
911-
configuration_options.ValidateCmd(haproxyOptions.ValidateCmd),
912-
configuration_options.MasterWorker,
913-
configuration_options.UseMd5Hash,
914-
)
915-
if err != nil {
916-
return nil, fmt.Errorf("error setting up configuration client: %s", err.Error())
917-
}
918-
919-
p := confClient.Parser()
920-
comments, err := p.Get(parser.Comments, parser.CommentsSectionName, "#")
921-
insertDisclaimer := false
922-
if err != nil {
923-
insertDisclaimer = true
924-
}
925-
data, ok := comments.([]types.Comments)
926-
if !ok {
927-
insertDisclaimer = true
928-
} else if len(data) == 0 || data[0].Value != "Dataplaneapi managed File" {
929-
insertDisclaimer = true
930-
}
931-
if insertDisclaimer {
932-
commentsNew := types.Comments{Value: "Dataplaneapi managed File"}
933-
err = p.Insert(parser.Comments, parser.CommentsSectionName, "#", commentsNew, 0)
934-
if err != nil {
935-
return nil, fmt.Errorf("error setting up configuration client: %s", err.Error())
936-
}
937-
commentsNew = types.Comments{Value: "changing file directly can cause a conflict if dataplaneapi is running"}
938-
err = p.Insert(parser.Comments, parser.CommentsSectionName, "#", commentsNew, 1)
939-
if err != nil {
940-
return nil, fmt.Errorf("error setting up configuration client: %s", err.Error())
941-
}
942-
}
943-
944-
return confClient, nil
945-
}
946-
947-
func configureRuntimeClient(ctx context.Context, confClient configuration.Configuration, haproxyOptions dataplaneapi_config.HAProxyConfiguration) runtime_api.Runtime {
948-
mapsDir := runtime_options.MapsDir(haproxyOptions.MapsDir)
949-
var runtimeClient runtime_api.Runtime
950-
951-
_, globalConf, err := confClient.GetGlobalConfiguration("")
952-
953-
// First try to setup master runtime socket
954-
if err == nil {
955-
var err error
956-
// If master socket is set and a valid unix socket, use only this
957-
if haproxyOptions.MasterRuntime != "" && misc.IsUnixSocketAddr(haproxyOptions.MasterRuntime) {
958-
masterSocket := haproxyOptions.MasterRuntime
959-
// if nbproc is set, set nbproc sockets
960-
if globalConf.Nbproc > 0 {
961-
nbproc := int(globalConf.Nbproc)
962-
ms := runtime_options.MasterSocket(masterSocket, nbproc)
963-
runtimeClient, err = runtime_api.New(ctx, mapsDir, ms)
964-
if err == nil {
965-
return runtimeClient
966-
}
967-
log.Warningf("Error setting up runtime client with master socket: %s : %s", masterSocket, err.Error())
968-
} else {
969-
// if nbproc is not set, use master socket with 1 process
970-
ms := runtime_options.MasterSocket(masterSocket, 1)
971-
runtimeClient, err = runtime_api.New(ctx, mapsDir, ms)
972-
if err == nil {
973-
return runtimeClient
974-
}
975-
log.Warningf("Error setting up runtime client with master socket: %s : %s", masterSocket, err.Error())
976-
}
977-
}
978-
runtimeAPIs := globalConf.RuntimeAPIs
979-
// if no master socket set, read from first valid socket if nbproc <= 1
980-
if globalConf.Nbproc <= 1 {
981-
socketList := make(map[int]string)
982-
for _, r := range runtimeAPIs {
983-
if misc.IsUnixSocketAddr(*r.Address) {
984-
socketList[1] = *r.Address
985-
sockets := runtime_options.Sockets(socketList)
986-
runtimeClient, err = runtime_api.New(ctx, mapsDir, sockets)
987-
if err == nil {
988-
return runtimeClient
989-
}
990-
log.Warningf("Error setting up runtime client with socket: %s : %s", *r.Address, err.Error())
991-
}
992-
}
993-
} else {
994-
// else try to find process specific sockets and set them up
995-
sockets := make(map[int]string)
996-
for _, r := range runtimeAPIs {
997-
//nolint:govet
998-
if misc.IsUnixSocketAddr(*r.Address) && r.Process != "" {
999-
process, err := strconv.ParseInt(r.Process, 10, 64)
1000-
if err == nil {
1001-
sockets[int(process)] = *r.Address
1002-
}
1003-
}
1004-
}
1005-
// no process specific settings found, Issue a warning and return empty runtime client
1006-
if len(sockets) == 0 {
1007-
log.Warning("Runtime API not configured, found multiple processes and no stats sockets bound to them.")
1008-
return runtimeClient
1009-
// use only found process specific sockets issue a warning if not all processes have a socket configured
1010-
}
1011-
if len(sockets) < int(globalConf.Nbproc) {
1012-
log.Warning("Runtime API not configured properly, there are more processes then configured sockets")
1013-
}
1014-
1015-
socketLst := runtime_options.Sockets(sockets)
1016-
runtimeClient, err = runtime_api.New(ctx, mapsDir, socketLst)
1017-
if err == nil {
1018-
return runtimeClient
1019-
}
1020-
log.Warningf("Error setting up runtime client with sockets: %v : %s", sockets, err.Error())
1021-
1022-
}
1023-
if err != nil {
1024-
log.Warning("Runtime API not configured, not using it: " + err.Error())
1025-
} else {
1026-
log.Warning("Runtime API not configured, not using it")
1027-
}
1028-
return runtimeClient
1029-
}
1030-
log.Warning("Cannot read runtime API configuration, not using it")
1031-
return runtimeClient
1032-
}
1033-
1034896
func handleSignals(ctx context.Context, cancel context.CancelFunc, sigs chan os.Signal, client client_native.HAProxyClient, haproxyOptions dataplaneapi_config.HAProxyConfiguration, users *dataplaneapi_config.Users) {
1035897
//nolint:gosimple
1036898
for {
@@ -1044,7 +906,7 @@ func handleSignals(ctx context.Context, cancel context.CancelFunc, sigs chan os.
1044906
if err != nil {
1045907
log.Infof("Unable to reload Data Plane API: %s", err.Error())
1046908
} else {
1047-
client.ReplaceRuntime(configureRuntimeClient(clientCtx, configuration, haproxyOptions))
909+
client.ReplaceRuntime(cn.ConfigureRuntimeClient(clientCtx, configuration, haproxyOptions))
1048910
log.Info("Reloaded Data Plane API")
1049911
}
1050912
} else if sig == syscall.SIGUSR2 {
@@ -1057,7 +919,7 @@ func handleSignals(ctx context.Context, cancel context.CancelFunc, sigs chan os.
1057919
}
1058920

1059921
func reloadConfigurationFile(client client_native.HAProxyClient, haproxyOptions dataplaneapi_config.HAProxyConfiguration, users *dataplaneapi_config.Users) {
1060-
confClient, err := configureConfigurationClient(haproxyOptions, mWorker)
922+
confClient, err := cn.ConfigureConfigurationClient(haproxyOptions, mWorker)
1061923
if err != nil {
1062924
log.Fatalf(err.Error())
1063925
}

0 commit comments

Comments
 (0)