Skip to content

Commit 13dfb4b

Browse files
tofayrlupton20
authored andcommitted
Source image name from local yaml file (#33)
* Source image name from local yaml file Signed-off-by: Tom Fay <[email protected]> * Remove stray comment Signed-off-by: Tom Fay <[email protected]> * Correct key in doc Signed-off-by: Tom Fay <[email protected]> * Fix case where floki is run from a subdirectory Signed-off-by: Tom Fay <[email protected]> * Change error message Signed-off-by: Tom Fay <[email protected]>
1 parent 677c77a commit 13dfb4b

File tree

8 files changed

+88
-20
lines changed

8 files changed

+88
-20
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ serde_derive = "1.0"
1414
serde_yaml = "0.7"
1515
structopt = "0.2"
1616
uuid = { version = "0.6", features = ["v4"] }
17+
yaml-rust = "0.4.3"
1718

1819
[dev-dependencies]
1920
tempdir = "0.3.7"

docs/content/intro/feature-overview.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The ideal workflow is
1616

1717
# Container images
1818

19-
`floki` offers a couple of ways t configure the container image to use.
19+
`floki` offers a couple of ways to configure the container image to use.
2020

2121
## Prebuilt images
2222

@@ -40,6 +40,16 @@ image:
4040
context: . # Defaults to .
4141
```
4242

43+
## Referencing a key in another yaml file
44+
`floki` can use an image referenced in another yaml file. This can help keep local development environments sync'd with a CI environment.
45+
46+
```
47+
image:
48+
yaml:
49+
file: .gitlab-ci.yaml
50+
key: variables.RUST-IMAGE
51+
```
52+
4353
## Updating an image
4454

4555
`floki pull` pulls the container under the `image` key again. While it is better to version images, this can be used when working against e.g. a `latest` tag.

src/config.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,32 @@ impl FlokiConfig {
5757
error: e,
5858
})?;
5959

60-
let config = serde_yaml::from_reader(f).map_err(|e| {
60+
let mut config: FlokiConfig = serde_yaml::from_reader(f).map_err(|e| {
6161
errors::FlokiError::ProblemParsingConfigYaml {
6262
name: file.display().to_string(),
6363
error: e,
6464
}
6565
})?;
6666

67+
// Ensure the path to an external yaml file is correct.
68+
// If the image.yaml.path file is relative, then it should
69+
// be relative to the floki config file. At this point we
70+
// already have the path to the floki config file, so we
71+
// just prepend that to image.yaml.path.
72+
if let image::Image::Yaml { ref mut yaml } = config.image {
73+
if yaml.file.is_relative() {
74+
yaml.file = file
75+
.parent()
76+
.ok_or_else(|| errors::FlokiInternalError::InternalAssertionFailed {
77+
description: format!(
78+
"could not constuct path to external yaml file '{:?}'",
79+
&yaml.file
80+
),
81+
})?
82+
.join(yaml.file.clone());
83+
}
84+
}
85+
6786
Ok(config)
6887
}
6988
}

src/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ pub enum FlokiError {
5353
#[fail(display = "Failed to check existance of image '{}': {}", image, error)]
5454
FailedToCheckForImage { image: String, error: io::Error },
5555

56+
#[fail(display = "Failed to find the key '{}' in file '{}'", key, file)]
57+
FailedToFindYamlKey { key: String, file: String },
58+
5659
#[fail(display = "Running container failed: {}", exit_status)]
5760
RunContainerFailed {
5861
exit_status: FlokiSubprocessExitStatus,

src/image.rs

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use failure::Error;
22
use quicli::prelude::*;
3+
use std::fs;
4+
use std::path::PathBuf;
35
use std::process::{Command, Stdio};
6+
use yaml_rust::YamlLoader;
47

58
use crate::errors::{FlokiError, FlokiSubprocessExitStatus};
69

@@ -13,6 +16,12 @@ pub struct BuildSpec {
1316
context: String,
1417
}
1518

19+
#[derive(Debug, PartialEq, Serialize, Deserialize)]
20+
pub struct YamlSpec {
21+
pub file: PathBuf,
22+
key: String,
23+
}
24+
1625
fn default_dockerfile() -> String {
1726
"Dockerfile".into()
1827
}
@@ -26,14 +35,39 @@ fn default_context() -> String {
2635
pub enum Image {
2736
Name(String),
2837
Build { build: BuildSpec },
38+
Yaml { yaml: YamlSpec },
2939
}
3040

3141
impl Image {
3242
/// Name of the image
33-
pub fn name(&self) -> String {
43+
pub fn name(&self) -> Result<String, Error> {
3444
match *self {
35-
Image::Name(ref s) => s.clone(),
36-
Image::Build { ref build } => build.name.clone() + ":floki",
45+
Image::Name(ref s) => Ok(s.clone()),
46+
Image::Build { ref build } => Ok(build.name.clone() + ":floki"),
47+
Image::Yaml { ref yaml } => {
48+
let contents = fs::read_to_string(&yaml.file)?;
49+
let raw = YamlLoader::load_from_str(&contents)?;
50+
let path = yaml.key.split('.').collect::<Vec<_>>();
51+
let mut val = &raw[0];
52+
53+
for key in &path {
54+
// Yaml arrays and maps with scalar keys can both be indexed by
55+
// usize, so heuristically prefer a usize index to a &str index.
56+
val = match key.parse::<usize>() {
57+
Ok(x) => &val[x],
58+
Err(_) => &val[*key],
59+
};
60+
}
61+
val.as_str()
62+
.map(std::string::ToString::to_string)
63+
.ok_or_else(|| {
64+
FlokiError::FailedToFindYamlKey {
65+
key: yaml.key.to_string(),
66+
file: yaml.file.display().to_string(),
67+
}
68+
.into()
69+
})
70+
}
3771
}
3872
}
3973

@@ -46,17 +80,17 @@ impl Image {
4680
let exit_status = Command::new("docker")
4781
.arg("build")
4882
.arg("-t")
49-
.arg(self.name())
83+
.arg(self.name()?)
5084
.arg("-f")
5185
.arg(&build.dockerfile)
5286
.arg(&build.context)
5387
.spawn()?
5488
.wait()?;
5589
if exit_status.success() {
56-
Ok(self.name())
90+
Ok(self.name()?)
5791
} else {
5892
Err(FlokiError::FailedToBuildImage {
59-
image: self.name(),
93+
image: self.name()?,
6094
exit_status: FlokiSubprocessExitStatus {
6195
process_description: "docker build".into(),
6296
exit_status: exit_status,
@@ -65,7 +99,7 @@ impl Image {
6599
}
66100
}
67101
// All other cases we just return the name
68-
_ => Ok(self.name()),
102+
_ => Ok(self.name()?),
69103
}
70104
}
71105
}
@@ -94,15 +128,15 @@ pub fn pull_image(name: String) -> Result<(), Error> {
94128
}
95129

96130
/// Determine whether an image exists locally
97-
pub fn image_exists_locally(name: String) -> Result<bool, Error> {
131+
pub fn image_exists_locally(name: &str) -> Result<bool, Error> {
98132
let ret = Command::new("docker")
99133
.args(&["history", "docker:stable-dind"])
100134
.stdin(Stdio::null())
101135
.stdout(Stdio::null())
102136
.stderr(Stdio::null())
103137
.status()
104138
.map_err(|e| FlokiError::FailedToCheckForImage {
105-
image: name.clone(),
139+
image: name.to_string(),
106140
error: e,
107141
})?;
108142

src/interpret.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub(crate) fn run_container(
1515
config: &FlokiConfig,
1616
command: &str,
1717
) -> Result<(), Error> {
18-
let (mut cmd, mut dind) = build_basic_command(&floki_root, &config);
18+
let (mut cmd, mut dind) = build_basic_command(&floki_root, &config)?;
1919

2020
cmd = configure_dind(cmd, &config, &mut dind)?;
2121
cmd = configure_floki_user_env(cmd, &environ);
@@ -144,17 +144,17 @@ fn get_mount_specification<'a, 'b>(
144144
fn build_basic_command(
145145
floki_root: &path::Path,
146146
config: &FlokiConfig,
147-
) -> (DockerCommandBuilder, Dind) {
147+
) -> Result<(DockerCommandBuilder, Dind), Error> {
148148
let mount = get_mount_specification(&floki_root, &config);
149149

150150
// Assign a container for docker-in-docker - we don't spawn it yet
151151
let dind = Dind::new(mount);
152152

153-
let image = &config.image.name();
153+
let image = &config.image.name()?;
154154
let outer_shell = config.shell.outer_shell();
155155
let cmd = command::DockerCommandBuilder::new(image, outer_shell).add_volume(mount);
156156

157-
(cmd, dind)
157+
Ok((cmd, dind))
158158
}
159159

160160
#[cfg(test)]

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ fn run_floki_from_args(args: &Cli) -> Result<(), Error> {
5858
// Pull the image in the configuration file
5959
Some(Subcommand::Pull {}) => {
6060
debug!("Trying to pull image {:?}", &config.image);
61-
debug!("Pulling image: {}", config.image.name());
62-
image::pull_image(config.image.name())
61+
debug!("Pulling image: {}", config.image.name()?);
62+
image::pull_image(config.image.name()?)
6363
}
6464

6565
// Run a command in the floki container

0 commit comments

Comments
 (0)