Skip to content

Commit 349a7cf

Browse files
vgramermjuraga
authored andcommitted
BUG/MAJOR: reconfigure runtime client and HAPorxy if path changes in the configuration file
If the user manually changes the configuration, the file watcher will be notified, the configuration will be reloaded, and the runtime client will be reconfigured if needed. To be able to reconfigure the runtime client, HAProxy must be reloaded. Otherwise, the new socket does not exist yet, and reconfiguration fails. We use the Reload agent to schedule a reload and reconfigure the runtime client. Signed-off-by: Vincent Gramer <[email protected]>
1 parent 0fedfbd commit 349a7cf

File tree

3 files changed

+90
-26
lines changed

3 files changed

+90
-26
lines changed

client-native/cn.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
"sync"
88
"time"
99

10-
clientnative "github.com/haproxytech/client-native/v5"
11-
"github.com/haproxytech/client-native/v5/models"
10+
clientnative "github.com/haproxytech/client-native/v4"
11+
"github.com/haproxytech/client-native/v4/models"
1212

1313
"github.com/haproxytech/client-native/v4/configuration"
1414
configuration_options "github.com/haproxytech/client-native/v4/configuration/options"

configure_data_plane.go

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,13 @@ func configureAPI(api *operations.DataPlaneAPI) http.Handler {
201201
signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2)
202202
go handleSignals(ctx, cancel, sigs, client, haproxyOptions, users)
203203

204+
ra := configureReloadAgent(ctx, haproxyOptions, client)
205+
204206
if !haproxyOptions.DisableInotify {
205-
if err := startWatcher(ctx, client, haproxyOptions, users); err != nil {
207+
if err := startWatcher(ctx, client, haproxyOptions, users, ra); err != nil {
206208
haproxyOptions.DisableInotify = true
207209
client = configureNativeClient(clientCtx, haproxyOptions, mWorker)
210+
ra = configureReloadAgent(ctx, haproxyOptions, client)
208211
}
209212
}
210213

@@ -213,26 +216,6 @@ func configureAPI(api *operations.DataPlaneAPI) http.Handler {
213216
go cfg.MapSync.SyncAll(client)
214217
}
215218

216-
// Initialize reload agent
217-
raParams := haproxy.ReloadAgentParams{
218-
Delay: haproxyOptions.ReloadDelay,
219-
ReloadCmd: haproxyOptions.ReloadCmd,
220-
UseMasterSocket: canUseMasterSocketReload(&haproxyOptions, client),
221-
RestartCmd: haproxyOptions.RestartCmd,
222-
StatusCmd: haproxyOptions.StatusCmd,
223-
ConfigFile: haproxyOptions.ConfigFile,
224-
BackupDir: haproxyOptions.BackupsDir,
225-
Retention: haproxyOptions.ReloadRetention,
226-
Client: client,
227-
Ctx: ctx,
228-
}
229-
230-
ra, e := haproxy.NewReloadAgent(raParams)
231-
if e != nil {
232-
// nolint:gocritic
233-
log.Fatalf("Cannot initialize reload agent: %v", e)
234-
}
235-
236219
// setup discovery handlers
237220
api.DiscoveryGetAPIEndpointsHandler = discovery.GetAPIEndpointsHandlerFunc(func(params discovery.GetAPIEndpointsParams, principal interface{}) middleware.Responder {
238221
ends, err := misc.DiscoverChildPaths("", SwaggerJSON)
@@ -881,6 +864,29 @@ func configureAPI(api *operations.DataPlaneAPI) http.Handler {
881864
return setupGlobalMiddleware(api.Serve(setupMiddlewares), adpts...)
882865
}
883866

867+
func configureReloadAgent(ctx context.Context, haproxyOptions dataplaneapi_config.HAProxyConfiguration, client client_native.HAProxyClient) *haproxy.ReloadAgent {
868+
// Initialize reload agent
869+
raParams := haproxy.ReloadAgentParams{
870+
Delay: haproxyOptions.ReloadDelay,
871+
ReloadCmd: haproxyOptions.ReloadCmd,
872+
UseMasterSocket: canUseMasterSocketReload(&haproxyOptions, client),
873+
RestartCmd: haproxyOptions.RestartCmd,
874+
StatusCmd: haproxyOptions.StatusCmd,
875+
ConfigFile: haproxyOptions.ConfigFile,
876+
BackupDir: haproxyOptions.BackupsDir,
877+
Retention: haproxyOptions.ReloadRetention,
878+
Client: client,
879+
Ctx: ctx,
880+
}
881+
882+
ra, e := haproxy.NewReloadAgent(raParams)
883+
if e != nil {
884+
// nolint:gocritic
885+
log.Fatalf("Cannot initialize reload agent: %v", e)
886+
}
887+
return ra
888+
}
889+
884890
// The TLS configuration before HTTPS server starts.
885891
func configureTLS(tlsConfig *tls.Config) {
886892
// Make all necessary changes to the TLS configuration here.
@@ -1047,12 +1053,40 @@ func reloadConfigurationFile(client client_native.HAProxyClient, haproxyOptions
10471053
client.ReplaceConfiguration(confClient)
10481054
}
10491055

1050-
func startWatcher(ctx context.Context, client client_native.HAProxyClient, haproxyOptions dataplaneapi_config.HAProxyConfiguration, users *dataplaneapi_config.Users) error {
1056+
func startWatcher(ctx context.Context, client client_native.HAProxyClient, haproxyOptions dataplaneapi_config.HAProxyConfiguration, users *dataplaneapi_config.Users, reloadAgent *haproxy.ReloadAgent) error {
10511057
cb := func() {
1052-
reloadConfigurationFile(client, haproxyOptions, users)
10531058
configuration, err := client.Configuration()
10541059
if err != nil {
1055-
log.Warningf("Failed to increment configuration version: %v", err)
1060+
log.Warningf("Failed to get configuration: %s", err)
1061+
return
1062+
}
1063+
1064+
// save old runtime configuration to know if the runtime client must be configured after the new configuration is
1065+
// reloaded by HAProxy. Logic is done by cn.ReconfigureRuntime() function.
1066+
_, globalConf, err := configuration.GetGlobalConfiguration("")
1067+
if err != nil {
1068+
log.Warningf("Failed to get global configuration section: %s", err)
1069+
return
1070+
}
1071+
runtimeAPIsOld := globalConf.RuntimeAPIs
1072+
1073+
// reload configuration from config file.
1074+
reloadConfigurationFile(client, haproxyOptions, users)
1075+
1076+
// reload runtime client if necessary.
1077+
callbackNeeded, reconfigureFunc, err := cn.ReconfigureRuntime(client, runtimeAPIsOld)
1078+
if err != nil {
1079+
log.Warningf("Failed to check if native client need to be reloaded: %s", err)
1080+
return
1081+
}
1082+
if callbackNeeded {
1083+
reloadAgent.ReloadWithCallback(reconfigureFunc)
1084+
}
1085+
1086+
// get the last configuration which has been updated by reloadConfigurationFile and increment version in config file.
1087+
configuration, err = client.Configuration()
1088+
if err != nil {
1089+
log.Warningf("Failed to get configuration: %s", err)
10561090
return
10571091
}
10581092
if err := configuration.IncrementVersion(); err != nil {

e2e/tests/global/replace.bats

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,33 @@ load 'utils/_helpers'
8585
resource_get "$_RUNTIME_MAP_FILES_BASE_PATH" ""
8686
assert_equal "$SC" 200
8787
}
88+
89+
90+
@test "global: Manually replace a global configuration with socket path changed" {
91+
# check HAPRoxy is configured with the expected socket
92+
resource_get "$_GLOBAL_BASE_PATH" ""
93+
assert_equal "$SC" 200
94+
assert_equal "$(get_json_path "$BODY" '.data.runtime_apis[0].address')" "/var/lib/haproxy/stats"
95+
96+
pre_logs_count=$(dpa_docker_exec 'cat /var/log/dataplaneapi.log' | wc -l)
97+
98+
# manually change configuration
99+
run dpa_docker_exec "sed -i 's@/var/lib/haproxy/stats@/var/lib/haproxy/stats-new@' /etc/haproxy/haproxy.cfg"
100+
101+
sleep 5
102+
# check configuration has been reloaded
103+
resource_get "$_GLOBAL_BASE_PATH" ""
104+
assert_equal "$SC" 200
105+
assert_equal "$(get_json_path "$BODY" '.data.runtime_apis[0].address')" "/var/lib/haproxy/stats-new"
106+
107+
# check that runtime client has been reconfigured with the new socket
108+
post_logs_count=$(dpa_docker_exec 'sh /var/log/dataplaneapi.log' | wc -l)
109+
new_logs_count=$(( $pre_logs_count - $post_logs_count ))
110+
new_logs=$(dpa_docker_exec 'cat /var/log/dataplaneapi.log' | tail -n $new_logs_count)
111+
112+
echo "$new_logs" # this will help debugging if the test fails
113+
assert echo -e "$new_logs" | grep -q "reload callback completed, runtime API reconfigured"
114+
115+
resource_get "$_RUNTIME_MAP_FILES_BASE_PATH" ""
116+
assert_equal "$SC" 200
117+
}

0 commit comments

Comments
 (0)