Skip to content

Commit 9f6886e

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 8900180 commit 9f6886e

File tree

2 files changed

+87
-23
lines changed

2 files changed

+87
-23
lines changed

configure_data_plane.go

+57-23
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,13 @@ func configureAPI(api *operations.DataPlaneAPI) http.Handler { //nolint:cyclop,m
200200
signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2)
201201
go handleSignals(ctx, cancel, sigs, client, haproxyOptions, users)
202202

203+
ra := configureReloadAgent(haproxyOptions, client, ctx)
204+
203205
if !haproxyOptions.DisableInotify {
204-
if err := startWatcher(ctx, client, haproxyOptions, users); err != nil {
206+
if err := startWatcher(ctx, client, haproxyOptions, users, ra); err != nil {
205207
haproxyOptions.DisableInotify = true
206208
client = configureNativeClient(clientCtx, haproxyOptions, mWorker)
209+
ra = configureReloadAgent(haproxyOptions, client, ctx)
207210
}
208211
}
209212

@@ -212,25 +215,6 @@ func configureAPI(api *operations.DataPlaneAPI) http.Handler { //nolint:cyclop,m
212215
go cfg.MapSync.SyncAll(client)
213216
}
214217

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

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

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

e2e/tests/global/replace.bats

+30
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)