Skip to content

Commit 6256972

Browse files
feloyclaude
andauthored
feat(containerfile): add fedora base image support (#32)
Add a fedora() Containerfile generator using registry.fedoraproject.org as the base registry and dnf for package installation, with the equivalent Fedora package names (bind-utils, iproute, iputils, nmap-ncat, procps-ng, which, etc.). Refactor integration tests to build each image variant (ubuntu, ubuntu+claude, fedora, fedora+claude) exactly once using OnceLock, and generate the per-variant test suite with a macro over a base_image × agent matrix. Signed-off-by: Philippe Martin <phmartin@redhat.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 633edd4 commit 6256972

4 files changed

Lines changed: 249 additions & 69 deletions

File tree

Cargo.lock

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

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ log = "0.4"
2727
serde = { version = "1", features = ["derive"] }
2828
tempfile = "3"
2929
toml = "0.8"
30+
31+
[dev-dependencies]
32+
ctor = "0.2"

src/containerfile.rs

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ impl std::error::Error for ContainerfileError {}
3636

3737
pub fn generate(config: &Config, agent: Option<&dyn Agent>) -> Result<String, ContainerfileError> {
3838
match config.base_image.image.as_str() {
39+
"fedora" => Ok(fedora(config, agent)),
3940
"ubuntu" => Ok(ubuntu(config, agent)),
4041
image => Err(ContainerfileError::NotSupported {
4142
image: image.to_string(),
@@ -93,6 +94,56 @@ USER sandbox
9394
)
9495
}
9596

97+
fn fedora(config: &Config, agent: Option<&dyn Agent>) -> String {
98+
let tag = &config.base_image.tag;
99+
let agent_section = agent
100+
.map(|a| format!("{}\n\n", a.install()))
101+
.unwrap_or_default();
102+
format!(
103+
r#"# System base
104+
FROM registry.fedoraproject.org/fedora:{tag} AS system
105+
106+
ENV PYTHONDONTWRITEBYTECODE=1 \
107+
PYTHONUNBUFFERED=1
108+
109+
WORKDIR /sandbox
110+
111+
# Core system dependencies
112+
RUN dnf install -y --setopt=install_weak_deps=False \
113+
bind-utils \
114+
ca-certificates \
115+
curl \
116+
iproute \
117+
iptables \
118+
iputils \
119+
net-tools \
120+
nftables \
121+
nmap-ncat \
122+
openssh-server \
123+
procps-ng \
124+
traceroute \
125+
which \
126+
&& dnf clean all
127+
128+
RUN groupadd -r supervisor && useradd -r -g supervisor -s /usr/sbin/nologin supervisor && \
129+
groupadd -r sandbox && useradd -r -g sandbox -d /sandbox -s /bin/bash sandbox
130+
131+
# Final base image
132+
FROM system AS final
133+
134+
RUN printf 'export PS1="\\u@\\h:\\w\\$ "\n' \
135+
> /sandbox/.bashrc && \
136+
printf '[ -f ~/.bashrc ] && . ~/.bashrc\n' > /sandbox/.profile && \
137+
chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile && \
138+
chown -R sandbox:sandbox /sandbox
139+
140+
USER sandbox
141+
142+
{agent_section}ENTRYPOINT ["/bin/bash"]
143+
"#
144+
)
145+
}
146+
96147
#[cfg(test)]
97148
mod tests {
98149
use super::*;
@@ -171,22 +222,51 @@ mod tests {
171222
}
172223

173224
#[test]
174-
fn fedora_returns_not_supported() {
175-
let err = generate(&fedora_config(), None).unwrap_err();
176-
assert_eq!(
177-
err,
178-
ContainerfileError::NotSupported {
179-
image: "fedora".to_string()
180-
}
225+
fn fedora_generates_successfully() {
226+
assert!(generate(&fedora_config(), None).is_ok());
227+
}
228+
229+
#[test]
230+
fn fedora_containerfile_contains_tag() {
231+
let content = generate(&fedora_config(), None).unwrap();
232+
assert!(content.contains("FROM registry.fedoraproject.org/fedora:latest AS system"));
233+
}
234+
235+
#[test]
236+
fn fedora_containerfile_tag_is_substituted() {
237+
let content = generate(&fedora_config(), None).unwrap();
238+
assert!(!content.contains("{tag}"));
239+
}
240+
241+
#[test]
242+
fn fedora_with_agent_includes_install() {
243+
let content = generate(&fedora_config(), Some(&MockAgent)).unwrap();
244+
assert!(content.contains("RUN echo mock-agent"));
245+
}
246+
247+
#[test]
248+
fn fedora_agent_install_runs_as_sandbox_user() {
249+
let content = generate(&fedora_config(), Some(&MockAgent)).unwrap();
250+
let user_pos = content.find("USER sandbox").unwrap();
251+
let install_pos = content.find("RUN echo mock-agent").unwrap();
252+
assert!(
253+
install_pos > user_pos,
254+
"agent install must appear after USER sandbox"
181255
);
182256
}
183257

258+
#[test]
259+
fn fedora_without_agent_omits_install() {
260+
let content = generate(&fedora_config(), None).unwrap();
261+
assert!(!content.contains("RUN echo mock-agent"));
262+
}
263+
184264
#[test]
185265
fn not_supported_error_message() {
186266
let err = ContainerfileError::NotSupported {
187-
image: "fedora".to_string(),
267+
image: "centos".to_string(),
188268
};
189-
assert_eq!(err.to_string(), "base image 'fedora' is not supported");
269+
assert_eq!(err.to_string(), "base image 'centos' is not supported");
190270
}
191271

192272
#[test]

0 commit comments

Comments
 (0)