From 8c00f756fdd11ee9562cd58c1af685cbeabe0c39 Mon Sep 17 00:00:00 2001 From: kinjelom Date: Wed, 17 Apr 2024 16:57:24 +0200 Subject: [PATCH 1/3] Proper formatting of indented multiline configurations, new raw blocks: listen, frontend, backend. --- jobs/haproxy/spec | 46 +++++++++++++++++--- jobs/haproxy/templates/haproxy.config.erb | 53 ++++++++++++++++++----- 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/jobs/haproxy/spec b/jobs/haproxy/spec index 02205703..e940e574 100644 --- a/jobs/haproxy/spec +++ b/jobs/haproxy/spec @@ -635,19 +635,19 @@ properties: ha_proxy.global_config: description: | - A block of raw HAProxy config that will be added to the HA proxy global section + Raw HAProxy config that will be added to the HA proxy global section, provided either as a multiline text blob or as an array of lines. ha_proxy.default_config: description: | - A block of raw HAProxy config that will be added to the HA proxy default section + Raw HAProxy config that will be added to the HA proxy default section, provided either as a multiline text blob or as an array of lines. ha_proxy.frontend_config: description: | - A block of raw HAProxy config that will be added to each HA proxy frontend definition + Raw HAProxy config that will be added to each HA proxy frontend definition, provided either as a multiline text blob or as an array of lines. ha_proxy.backend_config: description: | - A block of raw HAProxy config that will be added to the default HTTP + routed HTTP backend definitions + Raw HAProxy config that will be added to the default HTTP + routed HTTP backend definitions, provided either as a multiline text blob or as an array of lines. ha_proxy.custom_http_error_files: description: | @@ -664,13 +664,49 @@ properties: ha_proxy.tcp_backend_config: description: | - A block of raw HAProxy config that will be added to the CF TCP Router + Generic TCP backend definitions + Raw HAProxy config that will be added to the CF TCP Router + Generic TCP backend definitions, provided either as a multiline text blob or as an array of lines. ha_proxy.raw_config: description: | A multiline text blob of an entire haproxy config. Overrides every other option available, so you can provide your own config, and do whatever you want. Use at your own risk. + ha_proxy.raw_listen_blocks: + description: | + A hash of 'listen' blocks with the configuration provided either as a multiline text blob or as an array of lines. + Use at your own risk. + example: + my-listen-x: | + bind :81 + mode http + server-template srv 1-3 q-s0.web.default.deployment-x.bosh:8080 check inter 1000 + my-listen-y: + - bind :82 + - mode http + - server-template srv 1-3 q-s0.web.default.deployment-y.bosh:8080 check inter 1000 + ha_proxy.raw_frontend_blocks: + description: | + A hash of 'frontend' blocks with the configuration provided either as a multiline text blob or as an array of lines. + Use at your own risk. + example: + my-frontend-x: | + bind :81 + use_backend my-backend-x if { hdr(host) -i x.example.com } + my-frontend-y: + - bind :82 + - use_backend my-backend-y if { hdr(host) -i y.example.com } + ha_proxy.raw_backend_blocks: + description: | + A hash of 'backend' blocks with the configuration provided either as a multiline text blob or as an array of lines. + Use at your own risk. + example: + my-backend-x: | + mode http + server-template srv-x 1-3 q-s0.web.default.deployment-x.bosh:8080 check inter 1000 + my-backend-y: + - mode http + - server-template srv-y 1-3 q-s0.web.default.deployment-y.bosh:8080 check inter 1000 + ha_proxy.max_open_files: description: The number of file descriptors HAProxy can have open at one time default: 256000 diff --git a/jobs/haproxy/templates/haproxy.config.erb b/jobs/haproxy/templates/haproxy.config.erb index 7497781e..9489e816 100644 --- a/jobs/haproxy/templates/haproxy.config.erb +++ b/jobs/haproxy/templates/haproxy.config.erb @@ -1,5 +1,21 @@ <%- # see https://bosh.io/docs/jobs/#properties for documentation of bosh ERB templates -%> -<% if properties.ha_proxy.raw_config -%> +<% + def format_indented_multiline_config(raw_config) + ident = " " + if raw_config + if raw_config.is_a?(Array) + out = "" + raw_config.each do |line| + out = out + line + "\n"+ident + end + return out + else + raw_config.gsub(/\n/, "\n"+ident) + end + end + end + + if properties.ha_proxy.raw_config -%> <%= p("ha_proxy.raw_config") %> <%- else -%> <%- @@ -228,7 +244,7 @@ global log <%= p('ha_proxy.syslog_server') %> len <%= p('ha_proxy.log_max_length') %> format <%= p('ha_proxy.log_format') %> syslog <%= p('ha_proxy.log_level') %> daemon <%- if properties.ha_proxy.global_config -%> - <%= p("ha_proxy.global_config") %> + <%= format_indented_multiline_config(p("ha_proxy.global_config")) %> <%- end -%> <%- if p("ha_proxy.nbthread") > 1 -%> nbthread <%= p("ha_proxy.nbthread") %> @@ -300,7 +316,7 @@ defaults timeout http-request <%= (p("ha_proxy.request_timeout").to_f * 1000).to_i %>ms timeout queue <%= (p("ha_proxy.queue_timeout").to_f * 1000).to_i %>ms <%- if properties.ha_proxy.default_config -%> - <%= p("ha_proxy.default_config") %> + <%= format_indented_multiline_config(p("ha_proxy.default_config")) %> <%- end -%> <% if p("ha_proxy.stats_enable") -%> @@ -355,7 +371,7 @@ frontend http-in mode http bind <%= p("ha_proxy.binding_ip") %>:80 <%= accept_proxy %> <%= v4v6 %> <%- if properties.ha_proxy.frontend_config -%> - <%= p("ha_proxy.frontend_config") %> + <%= format_indented_multiline_config(p("ha_proxy.frontend_config")) %> <%- end -%> <%- if_p("ha_proxy.connections_rate_limit.table_size", "ha_proxy.connections_rate_limit.window_size") do -%> tcp-request connection track-sc0 src table st_tcp_conn_rate @@ -468,7 +484,7 @@ frontend https-in <%- end -%> <%- end -%> <%- if properties.ha_proxy.frontend_config -%> - <%= p("ha_proxy.frontend_config") %> + <%= format_indented_multiline_config(p("ha_proxy.frontend_config")) %> <%- end -%> <%- if_p("ha_proxy.connections_rate_limit.table_size", "ha_proxy.connections_rate_limit.window_size") do -%> tcp-request connection track-sc0 src table st_tcp_conn_rate @@ -599,7 +615,6 @@ frontend https-in <%- end -%> acl xfp_exists hdr_cnt(X-Forwarded-Proto) gt 0 http-request add-header X-Forwarded-Proto "https" if ! xfp_exists - <%- if p("ha_proxy.disable_backend_http2_websockets") -%> # Send websockets to a backend that forces HTTP/1.1. This avoids bugs in Go & Gorouter's HTTP/2 websocket support # https://github.com/cloudfoundry/routing-release/issues/230 @@ -638,7 +653,7 @@ frontend wss-in <%- end -%> <%- end -%> <%- if properties.ha_proxy.frontend_config -%> - <%= p("ha_proxy.frontend_config") %> + <%= format_indented_multiline_config(p("ha_proxy.frontend_config")) %> <%- end -%> <%- if_p("ha_proxy.cidr_whitelist") do -%> acl whitelist src -f /var/vcap/jobs/haproxy/config/whitelist_cidrs.txt @@ -753,7 +768,6 @@ frontend wss-in <%- end -%> acl xfp_exists hdr_cnt(X-Forwarded-Proto) gt 0 http-request add-header X-Forwarded-Proto "https" if ! xfp_exists - <%- if p("ha_proxy.disable_backend_http2_websockets") -%> # Send websockets to a backend that forces HTTP/1.1. This avoids bugs in Go & Gorouter's HTTP/2 websocket support # https://github.com/cloudfoundry/routing-release/issues/230 @@ -784,7 +798,7 @@ backend <%= backend[:name] %> compression type <%= p("ha_proxy.compress_types") %> <%- end -%> <%- if properties.ha_proxy.backend_config -%> - <%= p("ha_proxy.backend_config") %> + <%= format_indented_multiline_config(p("ha_proxy.backend_config")) %> <%- end -%> <%- p('ha_proxy.custom_http_error_files', {}).keys.each do |status_code| -%> errorfile <%= status_code %> /var/vcap/jobs/haproxy/errorfiles/custom<%=status_code%>.http @@ -815,7 +829,7 @@ backend http-routed-backend-<%= prefix_hash %> compression type <%= p("ha_proxy.compress_types") %> <%- end -%> <%- if properties.ha_proxy.backend_config -%> - <%= p("ha_proxy.backend_config") %> + <%= format_indented_multiline_config(p("ha_proxy.backend_config")) %> <%- end -%> <% resolvers = "" @@ -872,7 +886,7 @@ frontend cf_tcp_routing backend cf_tcp_routers mode tcp <%- if properties.ha_proxy.tcp_backend_config -%> - <%= p("ha_proxy.tcp_backend_config") %> + <%= format_indented_multiline_config(p("ha_proxy.tcp_backend_config")) %> <%- end -%> option httpchk GET /health <% tcp_router.instances.each_with_index do |instance, index| %> @@ -906,7 +920,7 @@ frontend tcp-frontend_<%= tcp_proxy["name"]%> backend tcp-<%= tcp_proxy["name"] %> mode tcp <%- if properties.ha_proxy.tcp_backend_config -%> - <%= p("ha_proxy.tcp_backend_config") %> + <%= format_indented_multiline_config(p("ha_proxy.tcp_backend_config")) %> <%- end -%> <%- if tcp_proxy["balance"] -%> balance <%= tcp_proxy["balance"] %> @@ -959,3 +973,18 @@ listen health_check_http_tcp-<%= tcp_proxy["name"] %> # }}} <% end -%> + +# raw blocks {{{ +<%- p('ha_proxy.raw_listen_blocks', {}).each do |block_id, block_raw_config| -%> +listen <%= block_id %> + <%= format_indented_multiline_config(block_raw_config) %> +<%- end -%> +<%- p('ha_proxy.raw_frontend_blocks', {}).each do |block_id, block_raw_config| -%> +frontend <%= block_id %> + <%= format_indented_multiline_config(block_raw_config) %> +<%- end -%> +<%- p('ha_proxy.raw_backend_blocks', {}).each do |block_id, block_raw_config| -%> +backend <%= block_id %> + <%= format_indented_multiline_config(block_raw_config) %> +<%- end -%> +# }}} From e0497bd8b7c4f7cc8e47af1088ba76094e8e9f71 Mon Sep 17 00:00:00 2001 From: kinjelom Date: Fri, 19 Apr 2024 14:26:49 +0200 Subject: [PATCH 2/3] raw_blocks config generalization + unit tests --- jobs/haproxy/spec | 59 +++++++++---------- jobs/haproxy/templates/haproxy.config.erb | 34 ++++++----- .../haproxy_config/raw_blocks_spec.rb | 59 +++++++++++++++++++ 3 files changed, 106 insertions(+), 46 deletions(-) create mode 100644 spec/haproxy/templates/haproxy_config/raw_blocks_spec.rb diff --git a/jobs/haproxy/spec b/jobs/haproxy/spec index e940e574..c411db05 100644 --- a/jobs/haproxy/spec +++ b/jobs/haproxy/spec @@ -671,41 +671,36 @@ properties: A multiline text blob of an entire haproxy config. Overrides every other option available, so you can provide your own config, and do whatever you want. Use at your own risk. - ha_proxy.raw_listen_blocks: + ha_proxy.raw_blocks: description: | - A hash of 'listen' blocks with the configuration provided either as a multiline text blob or as an array of lines. + A hash of block types, where each type contains a hash of specific block names with their respective configurations. + The configurations are provided as either multiline text blobs or arrays of lines. + This structure will be appended to the end of the HAProxy configuration file. Use at your own risk. example: - my-listen-x: | - bind :81 - mode http - server-template srv 1-3 q-s0.web.default.deployment-x.bosh:8080 check inter 1000 - my-listen-y: - - bind :82 - - mode http - - server-template srv 1-3 q-s0.web.default.deployment-y.bosh:8080 check inter 1000 - ha_proxy.raw_frontend_blocks: - description: | - A hash of 'frontend' blocks with the configuration provided either as a multiline text blob or as an array of lines. - Use at your own risk. - example: - my-frontend-x: | - bind :81 - use_backend my-backend-x if { hdr(host) -i x.example.com } - my-frontend-y: - - bind :82 - - use_backend my-backend-y if { hdr(host) -i y.example.com } - ha_proxy.raw_backend_blocks: - description: | - A hash of 'backend' blocks with the configuration provided either as a multiline text blob or as an array of lines. - Use at your own risk. - example: - my-backend-x: | - mode http - server-template srv-x 1-3 q-s0.web.default.deployment-x.bosh:8080 check inter 1000 - my-backend-y: - - mode http - - server-template srv-y 1-3 q-s0.web.default.deployment-y.bosh:8080 check inter 1000 + listen: + my-listen-x: | + bind :81 + mode http + server-template srv 1-3 q-s0.web.default.deployment-x.bosh:8080 check inter 1000 + my-listen-y: + - bind :82 + - mode http + - server-template srv 1-3 q-s0.web.default.deployment-y.bosh:8080 check inter 1000 + frontend: + my-frontend-x: | + bind :81 + use_backend my-backend-x if { hdr(host) -i x.example.com } + my-frontend-y: + - bind :82 + - use_backend my-backend-y if { hdr(host) -i y.example.com } + backend: + my-backend-x: | + mode http + server-template srv-x 1-3 q-s0.web.default.deployment-x.bosh:8080 check inter 1000 + my-backend-y: + - mode http + - server-template srv-y 1-3 q-s0.web.default.deployment-y.bosh:8080 check inter 1000 ha_proxy.max_open_files: description: The number of file descriptors HAProxy can have open at one time diff --git a/jobs/haproxy/templates/haproxy.config.erb b/jobs/haproxy/templates/haproxy.config.erb index 9489e816..151030c0 100644 --- a/jobs/haproxy/templates/haproxy.config.erb +++ b/jobs/haproxy/templates/haproxy.config.erb @@ -5,12 +5,14 @@ if raw_config if raw_config.is_a?(Array) out = "" + sep = "" raw_config.each do |line| - out = out + line + "\n"+ident + out = out + sep+ line + sep = "\n"+ident end return out else - raw_config.gsub(/\n/, "\n"+ident) + raw_config.strip.gsub(/\n/, "\n"+ident) end end end @@ -973,18 +975,22 @@ listen health_check_http_tcp-<%= tcp_proxy["name"] %> # }}} <% end -%> - +<%- if properties.ha_proxy.raw_blocks && !properties.ha_proxy.raw_blocks.empty? -%> # raw blocks {{{ -<%- p('ha_proxy.raw_listen_blocks', {}).each do |block_id, block_raw_config| -%> -listen <%= block_id %> - <%= format_indented_multiline_config(block_raw_config) %> -<%- end -%> -<%- p('ha_proxy.raw_frontend_blocks', {}).each do |block_id, block_raw_config| -%> -frontend <%= block_id %> - <%= format_indented_multiline_config(block_raw_config) %> -<%- end -%> -<%- p('ha_proxy.raw_backend_blocks', {}).each do |block_id, block_raw_config| -%> -backend <%= block_id %> +<%- + correct_types_order = %w[global defaults listen frontend backend resolvers peers mailers] + raw_blocks = p('ha_proxy.raw_blocks', {}) + ordered_blocks = correct_types_order.select { |type| raw_blocks.key?(type) } + additional_types = raw_blocks.keys - correct_types_order + all_found_types = ordered_blocks + additional_types + all_found_types.each do |block_type| + raw_blocks[block_type].each do |block_id, block_raw_config| +-%> +<%= block_type %> <%= block_id %> <%= format_indented_multiline_config(block_raw_config) %> -<%- end -%> +<%- + end + end +-%> # }}} +<%- end -%> \ No newline at end of file diff --git a/spec/haproxy/templates/haproxy_config/raw_blocks_spec.rb b/spec/haproxy/templates/haproxy_config/raw_blocks_spec.rb new file mode 100644 index 00000000..09b318d7 --- /dev/null +++ b/spec/haproxy/templates/haproxy_config/raw_blocks_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'rspec' + +describe 'config/haproxy.config ha_proxy.raw_blocks' do + let(:haproxy_conf) do + parse_haproxy_config(template.render({ 'ha_proxy' => properties })) + end + + context 'when multiline configurations are provided for some raw blocks' do + let(:properties) do + { + 'raw_blocks' => { + 'some' => { + 'raw-block-1' => "line 1\nline 2\nline 3", + 'raw-block-2' => "\n\nline 1\nline 2\nline 3\n\n", + 'raw-block-3' => ['line 1', 'line 2', 'line 3'] + } + } + } + end + + it 'formats the configuration as expected' do + expected_block_content = ['line 1', 'line 2', 'line 3'] + expect(haproxy_conf['some raw-block-1']).to eq(expected_block_content) + expect(haproxy_conf['some raw-block-2']).to eq(expected_block_content) + expect(haproxy_conf['some raw-block-3']).to eq(expected_block_content) + end + end + + context 'when there are many types of raw blocks' do + let(:properties) do + { + 'raw_blocks' => { + 'unknown' => { + 'raw-test-1' => 'test', + 'raw-test-2' => 'test' + }, + 'mailers' => { 'raw-test' => 'test' }, + 'peers' => { 'raw-test' => 'test' }, + 'resolvers' => { 'raw-test' => 'test' }, + 'backend' => { 'raw-test' => 'test' }, + 'frontend' => { 'raw-test' => 'test' }, + 'listen' => { 'raw-test' => 'test' }, + 'defaults' => { '# raw-test' => 'test' }, + 'global' => { '# raw-test' => 'test' } + } + } + end + + it 'arranges them all in the correct order' do + raw_keys = haproxy_conf.keys.select { |key| key.include?('raw-test') } + expect(raw_keys).to eq(['global # raw-test', 'defaults # raw-test', + 'listen raw-test', 'frontend raw-test', 'backend raw-test', + 'resolvers raw-test', 'peers raw-test', 'mailers raw-test', + 'unknown raw-test-1', 'unknown raw-test-2']) + end + end +end From e1dc0f89a2be6cd7600b85a956eeb1a5845cd123 Mon Sep 17 00:00:00 2001 From: kinjelom Date: Fri, 19 Apr 2024 15:10:47 +0200 Subject: [PATCH 3/3] formatting and an example config small fixes --- jobs/haproxy/spec | 4 ++-- jobs/haproxy/templates/haproxy.config.erb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jobs/haproxy/spec b/jobs/haproxy/spec index c411db05..0df72fe7 100644 --- a/jobs/haproxy/spec +++ b/jobs/haproxy/spec @@ -689,10 +689,10 @@ properties: - server-template srv 1-3 q-s0.web.default.deployment-y.bosh:8080 check inter 1000 frontend: my-frontend-x: | - bind :81 + bind :83 use_backend my-backend-x if { hdr(host) -i x.example.com } my-frontend-y: - - bind :82 + - bind :84 - use_backend my-backend-y if { hdr(host) -i y.example.com } backend: my-backend-x: | diff --git a/jobs/haproxy/templates/haproxy.config.erb b/jobs/haproxy/templates/haproxy.config.erb index 151030c0..9018b04e 100644 --- a/jobs/haproxy/templates/haproxy.config.erb +++ b/jobs/haproxy/templates/haproxy.config.erb @@ -985,12 +985,12 @@ listen health_check_http_tcp-<%= tcp_proxy["name"] %> all_found_types = ordered_blocks + additional_types all_found_types.each do |block_type| raw_blocks[block_type].each do |block_id, block_raw_config| --%> +%> <%= block_type %> <%= block_id %> <%= format_indented_multiline_config(block_raw_config) %> <%- end end --%> +%> # }}} <%- end -%> \ No newline at end of file