use crate::deserialize::*;
#[cfg(feature = "json_schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf};
use struct_patch::Patch;
use url::Url;
#[derive(Serialize, Debug, Clone, PartialEq, Default, Patch)]
#[serde(rename_all = "camelCase")]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Dofigen", rename = "Dofigen"))
)
)]
pub struct Dofigen {
#[patch(name = "VecPatch<String>")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub context: Vec<String>,
#[patch(name = "VecPatch<String>")]
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "ignores"))))]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub ignore: Vec<String>,
#[patch(name = "HashMapDeepPatch<String, StagePatch>")]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub builders: HashMap<String, Stage>,
#[patch(name = "StagePatch", attribute(serde(flatten)))]
#[serde(flatten)]
pub stage: Stage,
#[patch(name = "VecPatch<String>")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub entrypoint: Vec<String>,
#[patch(name = "VecPatch<String>")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub cmd: Vec<String>,
#[cfg_attr(
feature = "permissive",
patch(name = "VecDeepPatch<Port, ParsableStruct<PortPatch>>")
)]
#[cfg_attr(
not(feature = "permissive"),
patch(name = "VecDeepPatch<Port, PortPatch>")
)]
#[cfg_attr(
not(feature = "strict"),
patch(attribute(serde(alias = "port", alias = "ports")))
)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub expose: Vec<Port>,
#[patch(name = "Option<HealthcheckPatch>")]
#[serde(skip_serializing_if = "Option::is_none")]
pub healthcheck: Option<Healthcheck>,
}
#[derive(Serialize, Debug, Clone, PartialEq, Default, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(default)),
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Stage", rename = "Stage"))
)
)]
pub struct Stage {
#[serde(flatten, skip_serializing_if = "FromContext::is_empty")]
#[patch(name = "FromContextPatch", attribute(serde(flatten, default)))]
pub from: FromContext,
#[cfg_attr(
feature = "permissive",
patch(name = "Option<ParsableStruct<UserPatch>>")
)]
#[cfg_attr(not(feature = "permissive"), patch(name = "Option<UserPatch>"))]
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<User>,
#[serde(skip_serializing_if = "Option::is_none")]
pub workdir: Option<String>,
#[patch(name = "HashMapPatch<String, String>")]
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "args"))))]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub arg: HashMap<String, String>,
#[patch(name = "HashMapPatch<String, String>")]
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "envs"))))]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub env: HashMap<String, String>,
#[cfg_attr(
not(feature = "strict"),
patch(attribute(serde(
alias = "add",
alias = "adds",
alias = "artifact",
alias = "artifacts"
)))
)]
#[cfg_attr(
feature = "permissive",
patch(name = "VecDeepPatch<CopyResource, ParsableStruct<CopyResourcePatch>>")
)]
#[cfg_attr(
not(feature = "permissive"),
patch(name = "VecDeepPatch<CopyResource, CopyResourcePatch>")
)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub copy: Vec<CopyResource>,
#[patch(name = "Option<RunPatch>")]
#[serde(skip_serializing_if = "Option::is_none")]
pub root: Option<Run>,
#[patch(name = "RunPatch", attribute(serde(flatten)))]
#[serde(flatten)]
pub run: Run,
}
#[derive(Serialize, Debug, Clone, PartialEq, Default, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(default)),
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Run", rename = "Run"))
)
)]
pub struct Run {
#[patch(name = "VecPatch<String>")]
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "script"))))]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub run: Vec<String>,
#[cfg_attr(
feature = "permissive",
patch(name = "VecDeepPatch<Cache, ParsableStruct<CachePatch>>")
)]
#[cfg_attr(
not(feature = "permissive"),
patch(name = "VecDeepPatch<Cache, CachePatch>")
)]
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "caches"))))]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub cache: Vec<Cache>,
#[cfg_attr(
feature = "permissive",
patch(name = "VecDeepPatch<Bind, ParsableStruct<BindPatch>>")
)]
#[cfg_attr(
not(feature = "permissive"),
patch(name = "VecDeepPatch<Bind, BindPatch>")
)]
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "binds"))))]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub bind: Vec<Bind>,
}
#[derive(Serialize, Debug, Clone, PartialEq, Default, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Cache", rename = "Cache"))
)
)]
pub struct Cache {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub target: String,
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "ro"))))]
#[serde(skip_serializing_if = "Option::is_none")]
pub readonly: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sharing: Option<CacheSharing>,
#[serde(flatten, skip_serializing_if = "FromContext::is_empty")]
#[patch(name = "FromContextPatch", attribute(serde(flatten)))]
pub from: FromContext,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[cfg_attr(
feature = "permissive",
patch(attribute(serde(
deserialize_with = "deserialize_from_optional_string_or_number",
default
)))
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub chmod: Option<String>,
#[patch(name = "Option<UserPatch>")]
#[serde(skip_serializing_if = "Option::is_none")]
pub chown: Option<User>,
}
#[derive(Serialize, Debug, Clone, PartialEq, Default, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Bind", rename = "Bind"))
)
)]
pub struct Bind {
pub target: String,
#[serde(flatten, skip_serializing_if = "FromContext::is_empty")]
#[patch(name = "FromContextPatch", attribute(serde(flatten)))]
pub from: FromContext,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "rw"))))]
#[serde(skip_serializing_if = "Option::is_none")]
pub readwrite: Option<bool>,
}
#[derive(Serialize, Debug, Clone, PartialEq, Default, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(deny_unknown_fields, default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Healthcheck", rename = "Healthcheck"))
)
)]
pub struct Healthcheck {
pub cmd: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub interval: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub retries: Option<u16>,
}
#[derive(Serialize, Debug, Clone, PartialEq, Default, Patch, Hash, Eq, PartialOrd)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(deny_unknown_fields, default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "ImageName", rename = "ImageName"))
)
)]
pub struct ImageName {
#[serde(skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
pub path: String,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
#[patch(attribute(serde(flatten)))]
pub version: Option<ImageVersion>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(deny_unknown_fields, default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Copy", rename = "Copy"))
)
)]
pub struct Copy {
#[serde(flatten, skip_serializing_if = "FromContext::is_empty")]
#[patch(name = "FromContextPatch", attribute(serde(flatten)))]
pub from: FromContext,
#[patch(name = "VecPatch<String>")]
#[cfg_attr(
not(feature = "strict"),
patch(attribute(serde(alias = "path", alias = "source")))
)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub paths: Vec<String>,
#[serde(flatten)]
#[patch(name = "CopyOptionsPatch", attribute(serde(flatten)))]
pub options: CopyOptions,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(deny_unknown_fields, default, rename_all = "camelCase"))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "AddGitRepo", rename = "AddGitRepo"))
)
)]
pub struct AddGitRepo {
pub repo: String,
#[serde(flatten)]
#[patch(name = "CopyOptionsPatch", attribute(serde(flatten)))]
pub options: CopyOptions,
#[serde(skip_serializing_if = "Option::is_none")]
pub keep_git_dir: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(deny_unknown_fields, default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Add", rename = "Add"))
)
)]
pub struct Add {
#[patch(name = "VecPatch<Resource>")]
#[cfg_attr(not(feature = "strict"), patch(attribute(serde(alias = "file"))))]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub files: Vec<Resource>,
#[serde(flatten)]
#[patch(name = "CopyOptionsPatch", attribute(serde(flatten)))]
pub options: CopyOptions,
#[serde(skip_serializing_if = "Option::is_none")]
pub checksum: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(deny_unknown_fields, default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "CopyOptions", rename = "CopyOptions"))
)
)]
pub struct CopyOptions {
#[cfg_attr(
not(feature = "strict"),
patch(attribute(serde(alias = "destination")))
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub target: Option<String>,
#[patch(name = "Option<UserPatch>")]
#[serde(skip_serializing_if = "Option::is_none")]
pub chown: Option<User>,
#[cfg_attr(
feature = "permissive",
patch(attribute(serde(
deserialize_with = "deserialize_from_optional_string_or_number",
default
)))
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub chmod: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub link: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(deny_unknown_fields, default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "User", rename = "User"))
)
)]
pub struct User {
pub user: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Patch)]
#[patch(
attribute(derive(Deserialize, Debug, Clone, PartialEq, Default)),
attribute(serde(deny_unknown_fields, default))
)]
#[cfg_attr(
feature = "json_schema",
patch(
attribute(derive(JsonSchema)),
attribute(schemars(title = "Port", rename = "Port"))
)
)]
pub struct Port {
pub port: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub protocol: Option<PortProtocol>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Hash, Eq, PartialOrd)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub enum ImageVersion {
Tag(String),
Digest(String),
}
#[derive(Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum FromContext {
FromImage(ImageName),
FromBuilder(String),
FromContext(Option<String>),
}
#[derive(Serialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum CopyResource {
Copy(Copy),
AddGitRepo(AddGitRepo),
Add(Add),
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub enum CacheSharing {
Shared,
Private,
Locked,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub enum PortProtocol {
Tcp,
Udp,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Hash, Eq, PartialOrd)]
#[serde(untagged)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub enum Resource {
Url(Url),
File(PathBuf),
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub enum FromContextPatch {
#[cfg(not(feature = "permissive"))]
#[cfg_attr(not(feature = "strict"), serde(alias = "image"))]
FromImage(ImageNamePatch),
#[cfg(feature = "permissive")]
#[cfg_attr(not(feature = "strict"), serde(alias = "image"))]
FromImage(ParsableStruct<ImageNamePatch>),
#[cfg_attr(not(feature = "strict"), serde(alias = "builder"))]
FromBuilder(String),
#[cfg_attr(not(feature = "strict"), serde(alias = "from"))]
FromContext(Option<String>),
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(untagged)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub enum CopyResourcePatch {
Copy(CopyPatch),
AddGitRepo(AddGitRepoPatch),
Add(AddPatch),
Unknown(UnknownPatch),
}
#[derive(Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub struct UnknownPatch {
#[serde(flatten)]
pub options: Option<CopyOptionsPatch>,
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions_sorted::assert_eq_sorted;
mod deserialize {
use super::*;
mod dofigen {
use super::*;
#[test]
fn empty() {
let data = r#""#;
let dofigen: DofigenPatch = serde_yaml::from_str(data).unwrap();
let dofigen: Dofigen = dofigen.into();
assert_eq_sorted!(dofigen, Dofigen::default());
}
#[test]
fn from() {
let data = r#"
fromImage:
path: ubuntu
"#;
let dofigen: DofigenPatch = serde_yaml::from_str(data).unwrap();
let dofigen: Dofigen = dofigen.into();
assert_eq_sorted!(
dofigen,
Dofigen {
stage: Stage {
from: FromContext::FromImage(ImageName {
path: "ubuntu".into(),
..Default::default()
}),
..Default::default()
},
..Default::default()
}
);
}
#[ignore = "Not managed yet by serde because of multilevel flatten: https://serde.rs/field-attrs.html#flatten"]
#[test]
fn duplicate_from() {
let data = r#"
from:
path: ubuntu
from:
path: alpine
"#;
let dofigen: serde_yaml::Result<DofigenPatch> = serde_yaml::from_str(data);
println!("{:?}", dofigen);
assert!(dofigen.is_err());
}
#[ignore = "Not managed yet by serde because of multilevel flatten: https://serde.rs/field-attrs.html#flatten"]
#[test]
fn duplicate_from_and_alias() {
let data = r#"
from:
path: ubuntu
image:
path: alpine
"#;
let dofigen: serde_yaml::Result<DofigenPatch> = serde_yaml::from_str(data);
println!("{:?}", dofigen);
assert!(dofigen.is_err());
}
}
mod stage {
use super::*;
#[test]
fn empty() {
let data = r#""#;
let stage: StagePatch = serde_yaml::from_str(data).unwrap();
let stage: Stage = stage.into();
assert_eq_sorted!(stage, Stage::default());
}
#[test]
fn from() {
let data = r#"
fromImage:
path: ubuntu
"#;
let stage: StagePatch = serde_yaml::from_str(data).unwrap();
let stage: Stage = stage.into();
assert_eq_sorted!(
stage,
Stage {
from: FromContext::FromImage(ImageName {
path: "ubuntu".into(),
..Default::default()
})
.into(),
..Default::default()
}
);
}
#[ignore = "Not managed yet by serde because of multilevel flatten: https://serde.rs/field-attrs.html#flatten"]
#[test]
fn duplicate_from() {
let data = r#"
fromImage:
path: ubuntu
fromImage:
path: alpine
"#;
let stage: serde_yaml::Result<StagePatch> = serde_yaml::from_str(data);
assert!(stage.is_err());
}
#[ignore = "Not managed yet by serde because of multilevel flatten: https://serde.rs/field-attrs.html#flatten"]
#[test]
fn duplicate_from_and_alias() {
let data = r#"
fromImage:
path: ubuntu
image:
path: alpine
"#;
let stage: serde_yaml::Result<StagePatch> = serde_yaml::from_str(data);
assert!(stage.is_err());
}
}
mod user {
use super::*;
#[test]
fn name_and_group() {
let json_data = r#"{
"user": "test",
"group": "test"
}"#;
let user: UserPatch = serde_yaml::from_str(json_data).unwrap();
let user: User = user.into();
assert_eq_sorted!(
user,
User {
user: "test".into(),
group: Some("test".into())
}
);
}
}
mod copy_resource {
use super::*;
#[test]
fn copy() {
let json_data = r#"{
"paths": ["file1.txt", "file2.txt"],
"target": "destination/",
"chown": {
"user": "root",
"group": "root"
},
"chmod": "755",
"link": true,
"fromImage": {"path": "my-image"}
}"#;
let copy_resource: CopyResourcePatch = serde_yaml::from_str(json_data).unwrap();
let copy_resource: CopyResource = copy_resource.into();
assert_eq_sorted!(
copy_resource,
CopyResource::Copy(Copy {
paths: vec!["file1.txt".into(), "file2.txt".into()].into(),
options: CopyOptions {
target: Some("destination/".into()),
chown: Some(User {
user: "root".into(),
group: Some("root".into())
}),
chmod: Some("755".into()),
link: Some(true),
},
from: FromContext::FromImage(ImageName {
path: "my-image".into(),
..Default::default()
})
})
);
}
#[test]
fn copy_simple() {
let json_data = r#"{
"paths": ["file1.txt"]
}"#;
let copy_resource: CopyResourcePatch = serde_yaml::from_str(json_data).unwrap();
assert_eq_sorted!(
copy_resource,
CopyResourcePatch::Copy(CopyPatch {
paths: Some(vec!["file1.txt".into()].into_patch()),
options: Some(CopyOptionsPatch::default()),
..Default::default()
})
);
let copy_resource: CopyResource = copy_resource.into();
assert_eq_sorted!(
copy_resource,
CopyResource::Copy(Copy {
paths: vec!["file1.txt".into()].into(),
options: CopyOptions::default(),
..Default::default()
})
);
}
#[cfg(feature = "permissive")]
#[test]
fn copy_chmod_int() {
let json_data = r#"{
"paths": ["file1.txt"],
"chmod": 755
}"#;
let copy_resource: CopyPatch = serde_yaml::from_str(json_data).unwrap();
assert_eq_sorted!(
copy_resource,
CopyPatch {
paths: Some(vec!["file1.txt".into()].into_patch()),
options: Some(CopyOptionsPatch {
chmod: Some(Some("755".into())),
..Default::default()
}),
..Default::default()
}
);
}
#[cfg(feature = "permissive")]
#[test]
fn deserialize_copy_from_str() {
let json_data = "file1.txt destination/";
let copy_resource: ParsableStruct<CopyResourcePatch> =
serde_yaml::from_str(json_data).unwrap();
let copy_resource: CopyResource = copy_resource.into();
assert_eq_sorted!(
copy_resource,
CopyResource::Copy(Copy {
paths: vec!["file1.txt".into()].into(),
options: CopyOptions {
target: Some("destination/".into()),
..Default::default()
},
..Default::default()
})
);
}
#[test]
fn add_git_repo() {
let json_data = r#"{
"repo": "https://github.com/example/repo.git",
"target": "destination/",
"chown": {
"user": "root",
"group": "root"
},
"chmod": "755",
"link": true,
"keepGitDir": true
}"#;
let copy_resource: CopyResourcePatch = serde_yaml::from_str(json_data).unwrap();
let copy_resource: CopyResource = copy_resource.into();
assert_eq_sorted!(
copy_resource,
CopyResource::AddGitRepo(AddGitRepo {
repo: "https://github.com/example/repo.git".into(),
options: CopyOptions {
target: Some("destination/".into()),
chown: Some(User {
user: "root".into(),
group: Some("root".into())
}),
chmod: Some("755".into()),
link: Some(true),
},
keep_git_dir: Some(true)
})
);
}
#[test]
fn add() {
let json_data = r#"{
"files": ["file1.txt", "file2.txt"],
"target": "destination/",
"checksum": "sha256:abcdef123456",
"chown": {
"user": "root",
"group": "root"
},
"chmod": "755",
"link": true
}"#;
let copy_resource: CopyResourcePatch = serde_yaml::from_str(json_data).unwrap();
let copy_resource: CopyResource = copy_resource.into();
assert_eq_sorted!(
copy_resource,
CopyResource::Add(Add {
files: vec![
Resource::File("file1.txt".into()),
Resource::File("file2.txt".into())
]
.into(),
options: CopyOptions {
target: Some("destination/".into()),
chown: Some(User {
user: "root".into(),
group: Some("root".into())
}),
chmod: Some("755".into()),
link: Some(true),
},
checksum: Some("sha256:abcdef123456".into()),
})
);
}
}
mod builder {
use super::*;
#[test]
fn with_bind() {
let json_data = r#"
fromImage:
path: clux/muslrust:stable
workdir: /app
bind:
- target: /app
run:
- cargo build --release
- mv target/x86_64-unknown-linux-musl/release/dofigen /app/
"#;
let builder: Stage = serde_yaml::from_str::<StagePatch>(json_data)
.unwrap()
.into();
assert_eq_sorted!(
builder,
Stage {
from: FromContext::FromImage(ImageName {
path: "clux/muslrust:stable".into(),
..Default::default()
}),
workdir: Some("/app".into()),
run: Run {
bind: vec![Bind {
target: "/app".into(),
..Default::default()
}],
run: vec![
"cargo build --release".into(),
"mv target/x86_64-unknown-linux-musl/release/dofigen /app/".into()
],
..Default::default()
},
..Default::default()
}
);
}
}
}
}