Skip to content

Commit 7a5a472

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 6e77cef commit 7a5a472

File tree

3 files changed

+87
-24
lines changed

3 files changed

+87
-24
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: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,13 @@ func configureAPI(api *operations.DataPlaneAPI) http.Handler {
194194
signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2)
195195
go handleSignals(ctx, cancel, sigs, client, haproxyOptions, users)
196196

197+
ra := configureReloadAgent(ctx, haproxyOptions, client)
198+
197199
if !haproxyOptions.DisableInotify {
198-
if err := startWatcher(ctx, client, haproxyOptions, users); err != nil {
200+
if err := startWatcher(ctx, client, haproxyOptions, users, ra); err != nil {
199201
haproxyOptions.DisableInotify = true
200202
client = configureNativeClient(clientCtx, haproxyOptions, mWorker)
203+
ra = configureReloadAgent(ctx, haproxyOptions, client)
201204
}
202205
}
203206

@@ -206,24 +209,6 @@ func configureAPI(api *operations.DataPlaneAPI) http.Handler {
206209
go cfg.MapSync.SyncAll(client)
207210
}
208211

209-
// Initialize reload agent
210-
raParams := haproxy.ReloadAgentParams{
211-
Delay: haproxyOptions.ReloadDelay,
212-
ReloadCmd: haproxyOptions.ReloadCmd,
213-
RestartCmd: haproxyOptions.RestartCmd,
214-
StatusCmd: haproxyOptions.StatusCmd,
215-
ConfigFile: haproxyOptions.ConfigFile,
216-
BackupDir: haproxyOptions.BackupsDir,
217-
Retention: haproxyOptions.ReloadRetention,
218-
Ctx: ctx,
219-
}
220-
221-
ra, e := haproxy.NewReloadAgent(raParams)
222-
if e != nil {
223-
// nolint:gocritic
224-
log.Fatalf("Cannot initialize reload agent: %v", e)
225-
}
226-
227212
// setup discovery handlers
228213
api.DiscoveryGetAPIEndpointsHandler = discovery.GetAPIEndpointsHandlerFunc(func(params discovery.GetAPIEndpointsParams, principal interface{}) middleware.Responder {
229214
ends, err := misc.DiscoverChildPaths("", SwaggerJSON)
@@ -816,6 +801,26 @@ func configureAPI(api *operations.DataPlaneAPI) http.Handler {
816801
return setupGlobalMiddleware(api.Serve(setupMiddlewares), adpts...)
817802
}
818803

804+
func configureReloadAgent(ctx context.Context, haproxyOptions dataplaneapi_config.HAProxyConfiguration, client client_native.HAProxyClient) *haproxy.ReloadAgent {
805+
raParams := haproxy.ReloadAgentParams{
806+
Delay: haproxyOptions.ReloadDelay,
807+
ReloadCmd: haproxyOptions.ReloadCmd,
808+
RestartCmd: haproxyOptions.RestartCmd,
809+
StatusCmd: haproxyOptions.StatusCmd,
810+
ConfigFile: haproxyOptions.ConfigFile,
811+
BackupDir: haproxyOptions.BackupsDir,
812+
Retention: haproxyOptions.ReloadRetention,
813+
Ctx: ctx,
814+
}
815+
816+
ra, e := haproxy.NewReloadAgent(raParams)
817+
if e != nil {
818+
// nolint:gocritic
819+
log.Fatalf("Cannot initialize reload agent: %v", e)
820+
}
821+
return ra
822+
}
823+
819824
// The TLS configuration before HTTPS server starts.
820825
func configureTLS(tlsConfig *tls.Config) {
821826
// Make all necessary changes to the TLS configuration here.
@@ -962,12 +967,40 @@ func reloadConfigurationFile(client client_native.HAProxyClient, haproxyOptions
962967
client.ReplaceConfiguration(confClient)
963968
}
964969

965-
func startWatcher(ctx context.Context, client client_native.HAProxyClient, haproxyOptions dataplaneapi_config.HAProxyConfiguration, users *dataplaneapi_config.Users) error {
970+
func startWatcher(ctx context.Context, client client_native.HAProxyClient, haproxyOptions dataplaneapi_config.HAProxyConfiguration, users *dataplaneapi_config.Users, reloadAgent *haproxy.ReloadAgent) error {
966971
cb := func() {
967-
reloadConfigurationFile(client, haproxyOptions, users)
968972
configuration, err := client.Configuration()
969973
if err != nil {
970-
log.Warningf("Failed to increment configuration version: %v", err)
974+
log.Warningf("Failed to get configuration: %s", err)
975+
return
976+
}
977+
978+
// save old runtime configuration to know if the runtime client must be configured after the new configuration is
979+
// reloaded by HAProxy. Logic is done by cn.ReconfigureRuntime() function.
980+
_, globalConf, err := configuration.GetGlobalConfiguration("")
981+
if err != nil {
982+
log.Warningf("Failed to get global configuration section: %s", err)
983+
return
984+
}
985+
runtimeAPIsOld := globalConf.RuntimeAPIs
986+
987+
// reload configuration from config file.
988+
reloadConfigurationFile(client, haproxyOptions, users)
989+
990+
// reload runtime client if necessary.
991+
callbackNeeded, reconfigureFunc, err := cn.ReconfigureRuntime(client, runtimeAPIsOld)
992+
if err != nil {
993+
log.Warningf("Failed to check if native client need to be reloaded: %s", err)
994+
return
995+
}
996+
if callbackNeeded {
997+
reloadAgent.ReloadWithCallback(reconfigureFunc)
998+
}
999+
1000+
// get the last configuration which has been updated by reloadConfigurationFile and increment version in config file.
1001+
configuration, err = client.Configuration()
1002+
if err != nil {
1003+
log.Warningf("Failed to get configuration: %s", err)
9711004
return
9721005
}
9731006
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
@@ -88,3 +88,33 @@ load 'utils/_helpers'
8888
resource_get "$_RUNTIME_MAP_FILES_BASE_PATH" ""
8989
assert_equal "$SC" 200
9090
}
91+
92+
93+
@test "global: Manually replace a global configuration with socket path changed" {
94+
# check HAPRoxy is configured with the expected socket
95+
resource_get "$_GLOBAL_BASE_PATH" ""
96+
assert_equal "$SC" 200
97+
assert_equal "$(get_json_path "$BODY" '.data.runtime_apis[0].address')" "/var/lib/haproxy/stats"
98+
99+
pre_logs_count=$(dpa_docker_exec 'cat /var/log/dataplaneapi.log' | wc -l)
100+
101+
# manually change configuration
102+
run dpa_docker_exec "sed -i 's@/var/lib/haproxy/stats@/var/lib/haproxy/stats-new@' /etc/haproxy/haproxy.cfg"
103+
104+
sleep 5
105+
# check configuration has been reloaded
106+
resource_get "$_GLOBAL_BASE_PATH" ""
107+
assert_equal "$SC" 200
108+
assert_equal "$(get_json_path "$BODY" '.data.runtime_apis[0].address')" "/var/lib/haproxy/stats-new"
109+
110+
# check that runtime client has been reconfigured with the new socket
111+
post_logs_count=$(dpa_docker_exec 'sh /var/log/dataplaneapi.log' | wc -l)
112+
new_logs_count=$(( $pre_logs_count - $post_logs_count ))
113+
new_logs=$(dpa_docker_exec 'cat /var/log/dataplaneapi.log' | tail -n $new_logs_count)
114+
115+
echo "$new_logs" # this will help debugging if the test fails
116+
assert echo -e "$new_logs" | grep -q "reload callback completed, runtime API reconfigured"
117+
118+
resource_get "$_RUNTIME_MAP_FILES_BASE_PATH" ""
119+
assert_equal "$SC" 200
120+
}

0 commit comments

Comments
 (0)