Skip to content

Commit 75b35bd

Browse files
committed
Update build artifact extraction for new enum types
Signed-off-by: Robert Detjens <github@detjens.dev>
1 parent 78289a5 commit 75b35bd

File tree

7 files changed

+109
-69
lines changed

7 files changed

+109
-69
lines changed

src/access_handlers/docker.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub async fn check(profile_name: &str) -> Result<()> {
2323
let registry_config = &get_config()?.registry;
2424

2525
let client = docker().await?;
26+
2627
// build test image string
2728
// registry.example.com/somerepo/testimage:pleaseignore
2829
let test_image = format!("{}/credstestimage", registry_config.domain);

src/builder/artifacts.rs

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::configparser::challenge::{ChallengeConfig, ProvideConfig};
1818
pub async fn extract_asset(
1919
chal: &ChallengeConfig,
2020
provide: &ProvideConfig,
21-
// pod_containers:
21+
profile_name: &str,
2222
) -> Result<Vec<PathBuf>> {
2323
// This needs to handle three cases * 2 sources:
2424
// - single or multiple files without renaming (no as: field)
@@ -31,36 +31,104 @@ pub async fn extract_asset(
3131
// TODO: since this puts artifacts in the repo source folder, this should
3232
// try to not overwrite any existing files.
3333

34-
// debug!("extracting assets from container {}", &container.name);
34+
debug!(
35+
"extracting assets for challenge {:?} provide {:?}",
36+
chal.directory, &provide
37+
);
3538

3639
let docker = docker().await?;
3740

3841
match provide {
42+
// Repo file paths are relative to the challenge directory, so prepend chal dir
43+
3944
// No action necessary, return path as-is
40-
ProvideConfig::FromRepo { files } => Ok(files.clone()),
45+
ProvideConfig::FromRepo { files } => {
46+
Ok(files.iter().map(|f| chal.directory.join(f)).collect_vec())
47+
}
4148
ProvideConfig::FromRepoRename { from, to } => {
42-
std::fs::rename(from, to)?;
49+
std::fs::copy(chal.directory.join(from), chal.directory.join(to))
50+
.with_context(|| format!("could not copy repo file {from:?} to {to:?}"))?;
4351
Ok(vec![to.clone()])
4452
}
4553
ProvideConfig::FromRepoArchive {
4654
files,
4755
archive_name,
4856
} => {
49-
zip_files(archive_name, files)?;
57+
zip_files(
58+
&chal.directory.join(archive_name),
59+
&files.iter().map(|f| chal.directory.join(f)).collect_vec(),
60+
)
61+
.with_context(|| format!("could not create archive {archive_name:?}"))?;
5062
Ok(vec![archive_name.clone()])
5163
}
5264

53-
ProvideConfig::FromContainer { container, files } => extract_files(chal, container, files),
65+
ProvideConfig::FromContainer {
66+
container: container_name,
67+
files,
68+
} => {
69+
let tag = chal.container_tag_for_pod(profile_name, container_name)?;
70+
71+
let name = format!(
72+
"asset-container-{}-{}",
73+
chal.directory.to_string_lossy().replace("/", "-"),
74+
container_name
75+
);
76+
let container = docker::create_container(&tag, &name).await?;
77+
78+
let files = extract_files(chal, &container, files).await;
79+
80+
docker::remove_container(container).await?;
81+
82+
files
83+
}
84+
.with_context(|| format!("could not copy files {files:?} from container {container_name}")),
85+
5486
ProvideConfig::FromContainerRename {
55-
container,
87+
container: container_name,
5688
from,
5789
to,
58-
} => extract_rename(chal, container, from, to),
90+
} => {
91+
let tag = chal.container_tag_for_pod(profile_name, container_name)?;
92+
93+
let name = format!(
94+
"asset-container-{}-{}",
95+
chal.directory.to_string_lossy().replace("/", "-"),
96+
container_name
97+
);
98+
let container = docker::create_container(&tag, &name).await?;
99+
100+
let files = extract_rename(chal, &container, from, &chal.directory.join(to)).await;
101+
102+
docker::remove_container(container).await?;
103+
104+
files
105+
}
106+
.with_context(|| format!("could not copy file {from:?} from container {container_name}")),
107+
59108
ProvideConfig::FromContainerArchive {
60-
container,
109+
container: container_name,
61110
files,
62111
archive_name,
63-
} => extract_archive(chal, container, files, archive_name),
112+
} => {
113+
let tag = chal.container_tag_for_pod(profile_name, container_name)?;
114+
115+
let name = format!(
116+
"asset-container-{}-{}",
117+
chal.directory.to_string_lossy().replace("/", "-"),
118+
container_name
119+
);
120+
let container = docker::create_container(&tag, &name).await?;
121+
122+
let files =
123+
extract_archive(chal, &container, files, &chal.directory.join(archive_name)).await;
124+
125+
docker::remove_container(container).await?;
126+
127+
files
128+
}
129+
.with_context(|| {
130+
format!("could not create archive {archive_name:?} from container {container_name}")
131+
}),
64132
}
65133
}
66134

@@ -114,14 +182,17 @@ async fn extract_archive(
114182
);
115183

116184
// copy all listed files to tempdir
117-
let tempdir = tempdir_in(".")?;
185+
let tempdir = tempfile::Builder::new()
186+
.prefix(".beavercds-archive-")
187+
.tempdir_in(".")?;
118188
let copied_files = try_join_all(files.iter().map(|from| async {
119189
let to = tempdir.path().join(from.file_name().unwrap());
120190
docker::copy_file(container, from, &to).await
121191
}))
122192
.await?;
123193

124-
zip_files(&chal.directory.join(archive_name), &copied_files)?;
194+
// archive_name already has the chal dir prepended
195+
zip_files(archive_name, &copied_files)?;
125196

126197
Ok(vec![chal.directory.join(archive_name)])
127198
}

src/builder/docker.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ pub async fn push_image(image_tag: &str, creds: &UserPass) -> Result<String> {
122122
Ok(tag.to_string())
123123
}
124124

125-
#[tokio::main(flavor = "current_thread")] // make this a sync function
126125
pub async fn create_container(image_tag: &str, name: &str) -> Result<ContainerInfo> {
127126
debug!("creating container {name:?} from image {image_tag:?}");
128127
let client = docker().await?;
@@ -143,7 +142,6 @@ pub async fn create_container(image_tag: &str, name: &str) -> Result<ContainerIn
143142
})
144143
}
145144

146-
#[tokio::main(flavor = "current_thread")] // make this a sync function
147145
pub async fn remove_container(container: ContainerInfo) -> Result<()> {
148146
debug!("removing container {}", &container.name);
149147
let client = docker().await?;
@@ -182,7 +180,10 @@ pub async fn copy_file(container: &ContainerInfo, from: &Path, to: &Path) -> Res
182180
});
183181

184182
// collect byte stream chunks into full file
185-
let mut temptar = Builder::new().suffix(".tar").tempfile_in(".")?;
183+
let mut temptar = Builder::new()
184+
.prefix(".beavercds-")
185+
.suffix(".tar")
186+
.tempfile_in(".")?;
186187
while let Some(chunk) = dl_stream.next().await {
187188
temptar.as_file_mut().write_all(&chunk?)?;
188189
}

src/builder/mod.rs

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
use anyhow::{anyhow, Context, Error, Result};
55
use bollard::image::BuildImageOptions;
6-
use futures::stream::Iter;
6+
use futures::future::try_join_all;
7+
use futures::stream::{FuturesOrdered, Iter};
78
use itertools::Itertools;
89
use simplelog::*;
910
use std::default;
@@ -123,55 +124,21 @@ fn build_challenge(
123124
if extract_artifacts {
124125
info!("extracting build artifacts for chal {:?}", chal.directory);
125126

126-
// let (provide_container, provide_static): (Vec<_>, Vec<_>) =
127-
// chal.provide.iter().partition(|p| p.from.is_some());
128-
129-
// let extracted_files = provide_container
130-
// .iter()
131-
// // associate container `Provide` entries with their corresponding container image
132-
// .map(|provide| {
133-
// (
134-
// provide,
135-
// chal.container_tag_for_pod(profile_name, provide.from.as_ref().unwrap()),
136-
// )
137-
// })
138-
// // extract each container provide entry
139-
// .map(|(p, tag)| {
140-
// let tag = tag?;
141-
142-
// let name = format!(
143-
// "asset-container-{}-{}",
144-
// chal.directory.to_string_lossy().replace("/", "-"),
145-
// p.from.clone().unwrap()
146-
// );
147-
// let container = docker::create_container(&tag, &name)?;
148-
149-
// let asset_result =
150-
// artifacts::extract_asset(chal, p, &container).with_context(|| {
151-
// format!(
152-
// "failed to extract build artifacts for chal {:?} container {:?}",
153-
// chal.directory,
154-
// p.from.clone().unwrap()
155-
// )
156-
// });
157-
158-
// // clean up container even if it failed
159-
// docker::remove_container(container)?;
160-
161-
// asset_result
162-
// })
163-
// .flatten_ok()
164-
// .collect::<Result<Vec<_>>>()?;
165-
166-
// // handle potentially zipping up local files as well
167-
// let local_files = provide_static.iter().map(|provide| {
168-
// match provide.as_file.as_ref() {
169-
// // no archiving needed, pass files as-is
170-
// None => Ok(provide.include.clone()),
171-
// // need to archive multiple files into zip
172-
// Some(as_) => artifacts::zip_files(as_, provide.include.as_ref()).map(|z| vec![z]),
173-
// }
174-
// });
127+
// extract each challenge provide entry
128+
// this handles both local files and from build containers
129+
let extracted_files = chal
130+
.provide
131+
.iter()
132+
.map(|p| {
133+
artifacts::extract_asset(chal, p, profile_name).with_context(|| {
134+
format!(
135+
"failed to extract build artifacts for chal {:?}",
136+
chal.directory,
137+
)
138+
})
139+
})
140+
.flatten_ok()
141+
.collect::<Result<Vec<_>>>()?;
175142

176143
info!("extracted artifacts: {:?}", built.assets);
177144
}

src/configparser/challenge.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ enum FlagType {
180180
}
181181

182182
// Parse each distinct kind of Provide action as a separate enum variant
183+
// TODO: enforce relative/absolute paths for repo/container Provide's (`validator` crate?)
183184
#[derive(Debug, PartialEq, Serialize, Deserialize)]
184185
#[serde(untagged, deny_unknown_fields)]
185186
#[fully_pub]

src/tests/parsing/challenges.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,7 @@ fn challenge_provide() {
231231
232232
- from: container
233233
as: pears
234-
include:
235-
- /usr/lib/peaches
234+
include: /usr/lib/peaches
236235
237236
- from: container
238237
as: shells.zip

tests/repo/rcds.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ points:
2222
deploy:
2323
# control challenge deployment status explicitly per environment/profile
2424
testing:
25-
misc/garf: true
25+
# misc/garf: true
2626
pwn/notsh: true
27-
web/bar: true
27+
# web/bar: true
2828

2929
profiles:
3030
# configure per-environment credentials etc

0 commit comments

Comments
 (0)