Skip to content

Commit 37d2cb4

Browse files
authored
Merge pull request #548 from ginglis13/write-once-config-file
configuration-files: add option for write-once on boot
2 parents e0690a4 + c7720f0 commit 37d2cb4

File tree

6 files changed

+107
-1
lines changed

6 files changed

+107
-1
lines changed

sources/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sources/api/apiserver/src/server/controller.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,7 @@ mod test {
10531053
path: "file".try_into().unwrap(),
10541054
template_path: "template".try_into().unwrap(),
10551055
mode: None,
1056+
overwrite_path_if_present: None
10561057
})
10571058
);
10581059

sources/api/thar-be-settings/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ generate-readme.workspace = true
2929

3030
[dev-dependencies]
3131
maplit.workspace = true
32+
tempfile.workspace = true

sources/api/thar-be-settings/src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ pub async fn render_config_files(
6161
// Go write all the configuration files from template
6262
let mut rendered_configs = Vec::new();
6363
for (name, metadata) in config_files {
64+
if !metadata.should_render() {
65+
info!(
66+
"File {} already exists and overwrite-path-if-present=false, skipping render of config file",
67+
&metadata.path,
68+
);
69+
continue;
70+
}
6471
debug!("Rendering {}", &name);
6572

6673
let try_rendered =

sources/models/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ bottlerocket-settings-plugin.workspace = true
1919
bottlerocket-settings-models.workspace = true
2020
serde_plain.workspace = true
2121

22+
[dev-dependencies]
23+
tempfile.workspace = true
24+
2225
[build-dependencies]
2326
generate-readme.workspace = true
2427

sources/models/src/lib.rs

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use bottlerocket_release::BottlerocketRelease;
3131
use bottlerocket_settings_models::model_derive::model;
3232
use bottlerocket_settings_plugin::BottlerocketSettings;
3333
use serde::{Deserialize, Serialize};
34-
use std::collections::HashMap;
34+
use std::{collections::HashMap, path::Path};
3535

3636
use bottlerocket_settings_models::modeled_types::SingleLineString;
3737

@@ -77,6 +77,16 @@ struct ConfigurationFile {
7777
template_path: SingleLineString,
7878
#[serde(skip_serializing_if = "Option::is_none")]
7979
mode: Option<String>,
80+
#[serde(skip_serializing_if = "Option::is_none")]
81+
overwrite_path_if_present: Option<bool>,
82+
}
83+
84+
impl ConfigurationFile {
85+
/// Checks whether the configuration file should be written if the path is present.
86+
/// and overwrite_path_if_present is set.
87+
pub fn should_render(&self) -> bool {
88+
self.overwrite_path_if_present != Some(false) || !Path::new(&self.path.as_ref()).exists()
89+
}
8090
}
8191

8292
///// Metadata
@@ -93,3 +103,85 @@ struct Report {
93103
name: String,
94104
description: String,
95105
}
106+
107+
#[cfg(test)]
108+
mod tests {
109+
use super::*;
110+
use std::fs;
111+
use tempfile::TempDir;
112+
113+
#[test]
114+
fn test_should_render_with_existing_file() {
115+
let temp_dir = TempDir::new().unwrap();
116+
let temp_path = temp_dir.path();
117+
let existing_file_path = temp_path.join("existing_file.conf");
118+
119+
// Create an existing file.
120+
fs::write(&existing_file_path, "existing content").unwrap();
121+
122+
// Test case 1: overwrite_path_if_present = Some(false) with existing file
123+
// Should return false (don't overwrite)
124+
let config_file_no_overwrite = ConfigurationFile {
125+
path: existing_file_path.to_str().unwrap().try_into().unwrap(),
126+
template_path: "/mock/template.toml".try_into().unwrap(),
127+
mode: None,
128+
overwrite_path_if_present: Some(false),
129+
};
130+
assert!(!config_file_no_overwrite.should_render());
131+
132+
// Test case 2: overwrite_path_if_present = Some(true) with existing file
133+
// Should return true (do overwrite)
134+
let config_file_overwrite = ConfigurationFile {
135+
path: existing_file_path.to_str().unwrap().try_into().unwrap(),
136+
template_path: "/mock/template.toml".try_into().unwrap(),
137+
mode: None,
138+
overwrite_path_if_present: Some(true),
139+
};
140+
assert!(config_file_overwrite.should_render());
141+
142+
// Test case 3: overwrite_path_if_present = None with existing file
143+
// Should return true (default behavior is to overwrite)
144+
let config_file_default = ConfigurationFile {
145+
path: existing_file_path.to_str().unwrap().try_into().unwrap(),
146+
template_path: "/mock/template.toml".try_into().unwrap(),
147+
mode: None,
148+
overwrite_path_if_present: None,
149+
};
150+
assert!(config_file_default.should_render());
151+
}
152+
153+
#[test]
154+
fn test_should_render_with_non_existing_file() {
155+
let non_existing_file_path = "fake_file.conf";
156+
157+
// Test case 1: overwrite_path_if_present = Some(false) with non-existing file
158+
// Should return true (file doesn't exist, so create it)
159+
let config_file_no_overwrite = ConfigurationFile {
160+
path: non_existing_file_path.try_into().unwrap(),
161+
template_path: "/mock/template.toml".try_into().unwrap(),
162+
mode: None,
163+
overwrite_path_if_present: Some(false),
164+
};
165+
assert!(config_file_no_overwrite.should_render());
166+
167+
// Test case 2: overwrite_path_if_present = Some(true) with non-existing file
168+
// Should return true (do overwrite/create)
169+
let config_file_overwrite = ConfigurationFile {
170+
path: non_existing_file_path.try_into().unwrap(),
171+
template_path: "/mock/template.toml".try_into().unwrap(),
172+
mode: None,
173+
overwrite_path_if_present: Some(true),
174+
};
175+
assert!(config_file_overwrite.should_render());
176+
177+
// Test case 3: overwrite_path_if_present = None with non-existing file
178+
// Should return true (default behavior is to create)
179+
let config_file_default = ConfigurationFile {
180+
path: non_existing_file_path.try_into().unwrap(),
181+
template_path: "/mock/template.toml".try_into().unwrap(),
182+
mode: None,
183+
overwrite_path_if_present: None,
184+
};
185+
assert!(config_file_default.should_render());
186+
}
187+
}

0 commit comments

Comments
 (0)