From 105beafef6ea95b1355a0bbf8be874a98ff41383 Mon Sep 17 00:00:00 2001 From: Geoff Franks Date: Fri, 17 Jul 2026 13:13:25 -0400 Subject: [PATCH 1/2] fix(TNZ-106712): mount disk_health_check_paths as writable BPM volumes BPM containers only see paths explicitly listed as additional_volumes. When diego.rep.disk_health_check_paths is configured, rep's diskcheck runner calls statfs on each path at startup; if any path is absent from the container's mount namespace it gets ENOENT, which the runner treats as unhealthy and triggers immediate evacuation. Loop over disk_health_check_paths in bpm.yml.erb and emit each as a writable additional_volume so BPM mounts the real host path into the container before rep starts. ai-assisted=yes --- jobs/rep/templates/bpm.yml.erb | 4 +++ spec/rep_template_spec.rb | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/jobs/rep/templates/bpm.yml.erb b/jobs/rep/templates/bpm.yml.erb index 8303b30d9b..b800cca66f 100644 --- a/jobs/rep/templates/bpm.yml.erb +++ b/jobs/rep/templates/bpm.yml.erb @@ -17,3 +17,7 @@ processes: - path : /var/vcap/data/garden writable: true - path: <%= p("diego.rep.extra_root_fs_dir") %> +<% p("diego.rep.disk_health_check_paths").each do |path| -%> + - path: <%= path %> + writable: true +<% end -%> diff --git a/spec/rep_template_spec.rb b/spec/rep_template_spec.rb index acb52ee1d1..c3c3c2e61c 100644 --- a/spec/rep_template_spec.rb +++ b/spec/rep_template_spec.rb @@ -161,4 +161,66 @@ end end end + + describe 'bpm.yml.erb' do + let(:template) { job.template('config/bpm.yml') } + + def additional_volume_paths(rendered) + YAML.safe_load(rendered).dig('processes', 0, 'additional_volumes') + .map { |v| v['path'] } + end + + context 'when disk_health_check_paths is empty (default)' do + it 'adds no extra additional_volumes entries for disk health check paths' do + paths = additional_volume_paths(rendered_template) + # The only volumes present are the always-present ones; none of them + # should come from disk_health_check_paths (which defaults to []). + expect(paths).not_to be_empty # guard: other volumes still present + deployment_manifest_fragment['diego']['rep']['disk_health_check_paths'] = [] + paths_explicit_empty = additional_volume_paths(template.render(deployment_manifest_fragment)) + expect(paths_explicit_empty).to eq(paths) + end + end + + context 'when disk_health_check_paths is configured' do + before do + deployment_manifest_fragment['diego']['rep']['disk_health_check_paths'] = [ + '/var/vcap/data/tmp', + '/var/vcap/store' + ] + end + + it 'mounts each path as a writable additional_volume' do + volumes = YAML.safe_load(rendered_template).dig('processes', 0, 'additional_volumes') + check_volumes = volumes.select { |v| ['/var/vcap/data/tmp', '/var/vcap/store'].include?(v['path']) } + + expect(check_volumes.map { |v| v['path'] }).to contain_exactly('/var/vcap/data/tmp', '/var/vcap/store') + check_volumes.each do |vol| + expect(vol['writable']).to be true + end + end + + it 'does not affect the other always-present additional_volumes entries' do + volumes = YAML.safe_load(rendered_template).dig('processes', 0, 'additional_volumes') + paths = volumes.map { |v| v['path'] } + + expect(paths).to include('/var/vcap/data/voldrivers') + expect(paths).to include('/var/vcap/data/garden') + end + end + + context 'when a single disk_health_check_path is configured' do + before do + deployment_manifest_fragment['diego']['rep']['disk_health_check_paths'] = ['/var/vcap/data'] + end + + it 'mounts that single path as writable' do + volumes = YAML.safe_load(rendered_template).dig('processes', 0, 'additional_volumes') + data_vol = volumes.find { |v| v['path'] == '/var/vcap/data' } + + expect(data_vol).not_to be_nil + expect(data_vol['writable']).to be true + end + end + end end \ No newline at end of file From 347ea3e21fa5a8afccfd70fb06aa215449d3d3f7 Mon Sep 17 00:00:00 2001 From: Geoff Franks Date: Fri, 17 Jul 2026 13:17:40 -0400 Subject: [PATCH 2/2] fix: skip mounting "/" in bpm.yml disk_health_check_paths volumes Mounting root as a BPM additional_volume would expose the entire host filesystem inside the container. Guard against accidental misconfiguration by skipping any path that is exactly "/". ai-assisted=yes --- jobs/rep/templates/bpm.yml.erb | 1 + spec/rep_template_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/jobs/rep/templates/bpm.yml.erb b/jobs/rep/templates/bpm.yml.erb index b800cca66f..e79aee8c46 100644 --- a/jobs/rep/templates/bpm.yml.erb +++ b/jobs/rep/templates/bpm.yml.erb @@ -18,6 +18,7 @@ processes: writable: true - path: <%= p("diego.rep.extra_root_fs_dir") %> <% p("diego.rep.disk_health_check_paths").each do |path| -%> +<% next if path == "/" -%> - path: <%= path %> writable: true <% end -%> diff --git a/spec/rep_template_spec.rb b/spec/rep_template_spec.rb index c3c3c2e61c..82d9a51807 100644 --- a/spec/rep_template_spec.rb +++ b/spec/rep_template_spec.rb @@ -209,6 +209,27 @@ def additional_volume_paths(rendered) end end + context 'when "/" is included in disk_health_check_paths' do + before do + deployment_manifest_fragment['diego']['rep']['disk_health_check_paths'] = [ + '/', + '/var/vcap/data/tmp' + ] + end + + it 'does not mount "/" as an additional volume' do + volumes = YAML.safe_load(rendered_template).dig('processes', 0, 'additional_volumes') + expect(volumes.map { |v| v['path'] }).not_to include('/') + end + + it 'still mounts the other configured paths' do + volumes = YAML.safe_load(rendered_template).dig('processes', 0, 'additional_volumes') + data_tmp = volumes.find { |v| v['path'] == '/var/vcap/data/tmp' } + expect(data_tmp).not_to be_nil + expect(data_tmp['writable']).to be true + end + end + context 'when a single disk_health_check_path is configured' do before do deployment_manifest_fragment['diego']['rep']['disk_health_check_paths'] = ['/var/vcap/data']