Skip to content

Commit 7e5bab4

Browse files
committed
BUG/MEDIUM: Expose new client-native option validate_files_before
As well as validate_files_after. These options are useful when HAProxy reads from several configuration files. They allow dataplaneapi to launch HAProxy with the correct -f flags to validate the configuration. For example, a common practice is to put the global section into a separate file called global.def. To make sure this file is included before the main configuration file, you would add it to to "validate_files_before", which is a list of filenames.
1 parent 9d7c7e6 commit 7e5bab4

File tree

10 files changed

+174
-8
lines changed

10 files changed

+174
-8
lines changed

client-native/cn.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func ConfigureConfigurationClient(haproxyOptions dataplaneapi_config.HAProxyConf
3434
configuration_options.UsePersistentTransactions,
3535
configuration_options.TransactionsDir(haproxyOptions.TransactionDir),
3636
configuration_options.ValidateCmd(haproxyOptions.ValidateCmd),
37+
configuration_options.ValidateConfigFiles(haproxyOptions.ValidateFilesBefore, haproxyOptions.ValidateFilesAfter),
3738
configuration_options.MasterWorker,
3839
configuration_options.UseMd5Hash,
3940
configuration_options.PreferredTimeSuffix(haproxyOptions.PreferredTimeSuffix),

configuration/configuration.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ type HAProxyConfiguration struct {
4949
ReloadStrategy string `long:"reload-strategy" description:"Either systemd, s6 or custom" default:"custom" group:"reload"`
5050
TransactionDir string `short:"t" long:"transaction-dir" description:"Path to the transaction directory" default:"/tmp/haproxy" group:"transaction"`
5151
ValidateCmd string `long:"validate-cmd" description:"Executes a custom command to perform the HAProxy configuration check" group:"reload"`
52+
ValidateFilesBefore []string `long:"validate-files-before" description:"A list of configuration files to be loaded before the main file for validation" group:"reload"`
53+
ValidateFilesAfter []string `long:"validate-files-after" description:"A list of configuration files to be loaded after the main file for validation" group:"reload"`
5254
BackupsDir string `long:"backups-dir" description:"Path to directory in which to place backup files" group:"transaction"`
5355
MapsDir string `short:"p" long:"maps-dir" description:"Path to directory of map files managed by dataplane" default:"/etc/haproxy/maps" group:"resources"`
5456
SpoeTransactionDir string `long:"spoe-transaction-dir" description:"Path to the SPOE transaction directory" default:"/tmp/spoe-haproxy" group:"resources"`

configuration/configuration_storage.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,16 @@ type configTypeUserlist struct {
8181
}
8282

8383
type configTypeReload struct {
84-
ReloadDelay *int `yaml:"reload_delay,omitempty"`
85-
ReloadCmd *string `yaml:"reload_cmd,omitempty"`
86-
RestartCmd *string `yaml:"restart_cmd,omitempty"`
87-
StatusCmd *string `yaml:"status_cmd,omitempty"`
88-
ServiceName *string `yaml:"service_name,omitempty"`
89-
ReloadRetention *int `yaml:"reload_retention,omitempty"`
90-
ReloadStrategy *string `yaml:"reload_strategy,omitempty"`
91-
ValidateCmd *string `yaml:"validate_cmd,omitempty"`
84+
ReloadDelay *int `yaml:"reload_delay,omitempty"`
85+
ReloadCmd *string `yaml:"reload_cmd,omitempty"`
86+
RestartCmd *string `yaml:"restart_cmd,omitempty"`
87+
StatusCmd *string `yaml:"status_cmd,omitempty"`
88+
ServiceName *string `yaml:"service_name,omitempty"`
89+
ReloadRetention *int `yaml:"reload_retention,omitempty"`
90+
ReloadStrategy *string `yaml:"reload_strategy,omitempty"`
91+
ValidateCmd *string `yaml:"validate_cmd,omitempty"`
92+
ValidateFilesBefore *[]string `yaml:"validate_files_before,omitempty"`
93+
ValidateFilesAfter *[]string `yaml:"validate_files_after,omitempty"`
9294
}
9395

9496
type configTypeTransaction struct {
@@ -252,6 +254,12 @@ func copyToConfiguration(cfg *Configuration) { //nolint:cyclop,maintidx
252254
if cfgStorage.Haproxy != nil && cfgStorage.Haproxy.Reload != nil && cfgStorage.Haproxy.Reload.ValidateCmd != nil && !misc.HasOSArg("", "validate-cmd", "") {
253255
cfg.HAProxy.ValidateCmd = *cfgStorage.Haproxy.Reload.ValidateCmd
254256
}
257+
if cfgStorage.Haproxy != nil && cfgStorage.Haproxy.Reload != nil && cfgStorage.Haproxy.Reload.ValidateFilesBefore != nil && !misc.HasOSArg("", "validate-files-before", "") {
258+
cfg.HAProxy.ValidateFilesBefore = *cfgStorage.Haproxy.Reload.ValidateFilesBefore
259+
}
260+
if cfgStorage.Haproxy != nil && cfgStorage.Haproxy.Reload != nil && cfgStorage.Haproxy.Reload.ValidateFilesAfter != nil && !misc.HasOSArg("", "validate-files-after", "") {
261+
cfg.HAProxy.ValidateFilesAfter = *cfgStorage.Haproxy.Reload.ValidateFilesAfter
262+
}
255263
if cfgStorage.Dataplaneapi != nil && cfgStorage.Dataplaneapi.Transaction != nil && cfgStorage.Dataplaneapi.Transaction.TransactionDir != nil && !misc.HasOSArg("t", "transaction-dir", "") {
256264
cfg.HAProxy.TransactionDir = *cfgStorage.Dataplaneapi.Transaction.TransactionDir
257265
}

configuration/examples/example-full.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ haproxy:
7272
reload_retention: 1 # int 2
7373
reload_strategy: custom
7474
validate_cmd: null # string 2
75+
validate_files_before:
76+
- "/etc/haproxy/global.def"
77+
validate_files_after: [] # []string
7578
cluster: # Deprecated starting 3.0, see ./README.md for more information
7679
cluster_tls_dir: null # string
7780
id: null # string
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDciXjkAHqi+B+X
3+
jgFTEDlGrz6OAISOZTY7vA1eqTW2wnUHyHiaj73xnGegxp9rgZ05lqg/ZU4fO0lt
4+
9XWh1h8+pjwG3KoClS9yr2AK6+tpc7cjN6ZwkQo/3I+ulh9SfbIwW4TSay1yFp9v
5+
aDX2MIicRzh308Z0fw92jBEcMAfewXXEIr/+PSwHLj2WLS3Adme3GNCJwzAbcU/C
6+
OWKlSWrgbOEsYcCGI7Fv8GFeJp7sLAeN/G/ichXEQfJIn3Enb30zBr+B3VxgyYQV
7+
tiOxiT+LTKX/zG16chOJQzKLYfT+E7m4o6fQbfEjScEx0/4JHAfn6OydMMrw3g+f
8+
M10NhkRHAgMBAAECggEAUMFOMT8zqQVunBJ1zbK9JnL5Vwo9f97z8v+zbZxMfPXL
9+
4OO5te84wIZjM+5HZhh6OCJAzaYM60bMZqVhQ7eijVBV3rVi07tJOpeZdaZZ961V
10+
vGGeYs3ZkPT08BsssQox+58njd2NMJ+0Fhl02QeAnqk9tjMoEnSMdv5nLYkw+JH0
11+
Uw3cYD7ZjBzA/VYoD9nuZDhN/xiqhLksnOOYnucSleQI0feVbG/QSYfzQeGo42Om
12+
MjlVJr0LeJMebOcT34o3fQHoz6pQZt0WP4xNwRrk72ZqBl8HSkLGfrqzg2hlaldQ
13+
JfB7DuWkYRrUF0GKs7CbFo4yRsABVtp8DF6xiCL5AQKBgQD/dLPG8Garlc9liJG9
14+
ovCE2/kAkeni/Qjc14CFX4qkgcnBIaTTl79xeRmN5dp/WnTinNNMQTamaCJaKEgN
15+
5A28axTsE9Ec/9sGrv/Dt3fXtS+rBPYS9Hc1k5LQrnBlD6lEe5+Oqhc4Mzw5sglo
16+
sfiChEzojV9Vnyj/Y0m5q8t9hwKBgQDdAbqiHMJoAP7IEnsOw1xjA6zFaeY2jYDS
17+
F5ZvSN0IlWXX2Aqc941ws1qKZiGjC9pqaRcjlh1OvhaEIpY7ksTShM8453mstWGl
18+
GV3iYiI9E+KijwqFhWyuiXxi0m0l0u2vXPgj0u6txX9suQQab9+f2oSEXG34wXDu
19+
R9+s/rqzQQKBgFID9OgtLLlwGqsdgrUgyBnPyg0Ny8qttJe6tK+dchTI+q6AD7xD
20+
XxqeZ77wCguTTi2nbgtwcIxSqJzLi/6xtltFAe2dmyi1WGu36bO7hsWBjXFZ4WtK
21+
g6921s8bAkjgE1dCXYLfRx8rC+32JCEx6nh044BSS0ZhGDeOeBAdgPKnAoGBAIIa
22+
w3kt/xBlDZhQsNr3DUtI3Yv2FM2mreCAfFIVDfJAqQzRJSZU4ZIoM7Pn/gNTNgiQ
23+
x0tu0uAJLY4qIlD9zRq1jpxMQKf4u3wLG+bqqIdWToQuOx5xdpKlY3F1uUWcD8q9
24+
q2LDiTkJXENwA8dgdsBPTtXw59iaYFYWP8pCxzxBAoGANmDkR5qJBJMeQgD8pVKu
25+
xLwV7gInuVMLqfF67aBTCsQKAk+b+zWKUbPLpaldCXREj0YLsTEmp2srIP4KKSMH
26+
N/yjXtYxY4fmCsrHAzqqzpZiYWRYScXHwCSudvV0w7PFNwKndS68lqDj7JKyGkBe
27+
rAMB+WGlOTcNKJrsrq7mnPc=
28+
-----END PRIVATE KEY-----
29+
-----BEGIN CERTIFICATE-----
30+
MIID3TCCAsWgAwIBAgIUSUdc/Tj4WL90KtoPif0wGpgEGlowDQYJKoZIhvcNAQEL
31+
BQAwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM
32+
DU1vdW50YWluIFZpZXcxGjAYBgNVBAoMEVlvdXIgT3JnYW5pemF0aW9uMRIwEAYD
33+
VQQLDAlZb3VyIFVuaXQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzA1MjQxMDIy
34+
MjJaFw0zMzA1MjExMDIyMjJaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
35+
Zm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRowGAYDVQQKDBFZb3VyIE9y
36+
Z2FuaXphdGlvbjESMBAGA1UECwwJWW91ciBVbml0MRIwEAYDVQQDDAlsb2NhbGhv
37+
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDciXjkAHqi+B+XjgFT
38+
EDlGrz6OAISOZTY7vA1eqTW2wnUHyHiaj73xnGegxp9rgZ05lqg/ZU4fO0lt9XWh
39+
1h8+pjwG3KoClS9yr2AK6+tpc7cjN6ZwkQo/3I+ulh9SfbIwW4TSay1yFp9vaDX2
40+
MIicRzh308Z0fw92jBEcMAfewXXEIr/+PSwHLj2WLS3Adme3GNCJwzAbcU/COWKl
41+
SWrgbOEsYcCGI7Fv8GFeJp7sLAeN/G/ichXEQfJIn3Enb30zBr+B3VxgyYQVtiOx
42+
iT+LTKX/zG16chOJQzKLYfT+E7m4o6fQbfEjScEx0/4JHAfn6OydMMrw3g+fM10N
43+
hkRHAgMBAAGjUzBRMB0GA1UdDgQWBBQyAsLJnbNGf7ME+DokcSeSMYMN5jAfBgNV
44+
HSMEGDAWgBQyAsLJnbNGf7ME+DokcSeSMYMN5jAPBgNVHRMBAf8EBTADAQH/MA0G
45+
CSqGSIb3DQEBCwUAA4IBAQB62/lsOhVrIwUx07C4r3eGu6EmughelFJgqwijnOSS
46+
JmICWvLtfu+X8DQkl0ls5esnK8FZk2i5zBMqhdkD0vb9qa0iI++M6jcjbDrW/bZ/
47+
oLa/zvEvUeEQS3FqS8p3OUczm4T88cBze3MX4iDDo/QgK6B/46t2UeXByuIZEXsK
48+
6OzBfoX31qrZ+DvvKBSLQG1f13vkp9WDL2u60IQ4XaIgyr+O1R/x157ic0WPaWoV
49+
EwPYP7ds1d8Zz9z8u6LFNi+as33zkRhIBQble277U8vT7AOicXHxnf7y2rToO41h
50+
mi+hA0kvDDgbr4r/K/Lq909m8OUKWBZ5U+c9c7FdMqT+
51+
-----END CERTIFICATE-----
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
global
2+
chroot /var/lib/haproxy
3+
user haproxy
4+
group haproxy
5+
maxconn 4000
6+
pidfile /var/run/haproxy.pid
7+
stats socket /var/lib/haproxy/stats level admin
8+
log 127.0.0.1 local2
9+
crt-base /etc/haproxy
10+
ca-base /etc/haproxy
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# global is defined in global.def
2+
3+
defaults mydefaults
4+
mode http
5+
maxconn 3000
6+
log global
7+
option httplog
8+
option redispatch
9+
option dontlognull
10+
option http-server-close
11+
option forwardfor except 127.0.0.0/8
12+
timeout http-request 10s
13+
timeout check 10s
14+
timeout connect 10s
15+
timeout client 1m
16+
timeout queue 1m
17+
timeout server 1m
18+
timeout http-keep-alive 10s
19+
retries 3
20+
21+
backend test_backend
22+
mode http
23+
balance roundrobin
24+
option forwardfor
25+
server server_01 10.1.1.1:8080 check weight 80
26+
server server_02 10.1.1.2:8080 check weight 80
27+
server server_03 10.1.1.2:8080 check weight 80
28+
29+
frontend test_frontend
30+
bind :1443 ssl crt default
31+
use_backend test_backend
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"# global is defined in global.def\n\ndefaults mydefaults\n mode http\n maxconn 3000\n log global\n option httplog\n option redispatch\n option dontlognull\n option http-server-close\n option forwardfor except 127.0.0.0/8\n timeout http-request 10s\n timeout check 10s\n timeout connect 10s\n timeout client 1m\n timeout queue 1m\n timeout server 1m\n timeout http-keep-alive 10s\n retries 3\n\nbackend test_backend\n mode http\n balance roundrobin\n option forwardfor\n server server_01 10.1.1.1:8080 check weight 80\n server server_02 10.1.1.2:8080 check weight 80\n server server_03 10.1.1.2:8080 check weight 80\n\nfrontend test_frontend\n bind :1443 ssl crt default\n use_backend test_backend\n"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Same config as dataplaneapi-master-socket.yaml
2+
# but with an extra "validate_files_before".
3+
4+
name: famous_condor
5+
dataplaneapi:
6+
host: 0.0.0.0
7+
port: 8080
8+
userlist:
9+
userlist_file: /etc/haproxy/userlist.cfg
10+
resources:
11+
maps_dir: /etc/haproxy/maps
12+
ssl_certs_dir: /etc/haproxy/ssl
13+
general_storage_dir: /etc/haproxy/general
14+
dataplane_storage_dir: /etc/haproxy/dataplane
15+
spoe_dir: /etc/haproxy/spoe
16+
haproxy:
17+
config_file: /etc/haproxy/haproxy.cfg
18+
haproxy_bin: /usr/local/sbin/haproxy
19+
master_runtime: /var/lib/haproxy/master
20+
master_worker_mode: true
21+
reload:
22+
reload_cmd: kill -s 12 1
23+
restart_cmd: kill -s 12 1
24+
validate_files_before:
25+
- /etc/haproxy/global.def
26+
log:
27+
log_to: file
28+
log_file: /var/log/dataplaneapi.log
29+
log_level: debug

e2e/tests/raw_multi/validate.bats

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bats
2+
#
3+
# Copyright 2025 HAProxy Technologies
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http:#www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
load '../../libs/dataplaneapi'
19+
load '../../libs/debug'
20+
load "../../libs/get_json_path"
21+
load '../../libs/haproxy_config_setup'
22+
load '../../libs/resource_client'
23+
load '../../libs/version'
24+
25+
_RAW_BASE_PATH="/services/haproxy/configuration/raw"
26+
27+
@test "raw_multi: Validate a new configuration which depends on global.def" {
28+
resource_post "$_RAW_BASE_PATH" 'data/haproxy.cfg.json' 'only_validate=1'
29+
assert_equal "$SC" 202
30+
}

0 commit comments

Comments
 (0)