use super::common::*;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct Service {
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_opt_string_or_struct",
deserialize_with = "deserialize_opt_string_or_struct"
)]
pub build: Option<Build>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cap_add: Vec<RawOr<String>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cap_drop: Vec<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<CommandLine>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cgroup_parent: Option<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub container_name: Option<RawOr<String>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub devices: Vec<RawOr<AliasedName>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub depends_on: Vec<RawOr<String>>,
#[serde(
default,
skip_serializing_if = "Vec::is_empty",
deserialize_with = "deserialize_item_or_list"
)]
pub dns: Vec<RawOr<String>>,
#[serde(
default,
skip_serializing_if = "Vec::is_empty",
deserialize_with = "deserialize_item_or_list"
)]
pub dns_search: Vec<RawOr<String>>,
#[serde(
default,
skip_serializing_if = "Vec::is_empty",
deserialize_with = "deserialize_item_or_list"
)]
pub tmpfs: Vec<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub entrypoint: Option<CommandLine>,
#[serde(
rename = "env_file",
default,
skip_serializing_if = "Vec::is_empty",
deserialize_with = "deserialize_item_or_list"
)]
pub env_files: Vec<RawOr<PathBuf>>,
#[serde(
default,
skip_serializing_if = "BTreeMap::is_empty",
deserialize_with = "deserialize_map_or_key_value_list"
)]
pub environment: BTreeMap<String, RawOr<String>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub expose: Vec<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extends: Option<Extends>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub external_links: Vec<RawOr<AliasedName>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub extra_hosts: Vec<RawOr<HostMapping>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<RawOr<Image>>,
#[serde(
default,
skip_serializing_if = "BTreeMap::is_empty",
deserialize_with = "deserialize_map_or_key_value_list"
)]
pub labels: BTreeMap<String, RawOr<String>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub links: Vec<RawOr<AliasedName>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logging: Option<Logging>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub network_mode: Option<RawOr<NetworkMode>>,
#[serde(
default,
skip_serializing_if = "BTreeMap::is_empty",
deserialize_with = "deserialize_map_or_default_list"
)]
pub networks: BTreeMap<String, NetworkInterface>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pid: Option<RawOr<PidMode>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub ports: Vec<RawOr<PortMapping>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub security_opt: Vec<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_signal: Option<RawOr<String>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub ulimits: BTreeMap<String, Ulimit>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub volumes: Vec<RawOr<VolumeMount>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub volumes_from: Vec<RawOr<VolumesFrom>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub volume_driver: Option<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_shares: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_quota: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domainname: Option<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hostname: Option<RawOr<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ipc: Option<RawOr<IpcMode>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mac_address: Option<RawOr<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mem_limit: Option<RawOr<MemorySize>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memswap_limit: Option<RawOr<MemorySize>>,
#[serde(default, skip_serializing_if = "is_false")]
pub privileged: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub restart: Option<RawOr<RestartMode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shm_size: Option<RawOr<MemorySize>>,
#[serde(default, skip_serializing_if = "is_false")]
pub stdin_open: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub tty: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub working_dir: Option<RawOr<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
oom_score_adj: Option<i16>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
group_add: Vec<String>,
#[doc(hidden)]
#[serde(default, skip_serializing, skip_deserializing)]
pub _hidden: (),
}
derive_standard_impls_for!(Service, {
build,
cap_add,
cap_drop,
command,
cgroup_parent,
container_name,
devices,
depends_on,
dns,
dns_search,
tmpfs,
entrypoint,
env_files,
environment,
expose,
extends,
external_links,
extra_hosts,
image,
labels,
links,
logging,
network_mode,
networks,
pid,
ports,
security_opt,
stop_signal,
ulimits,
volumes,
volumes_from,
volume_driver,
cpu_shares,
cpu_quota,
domainname,
hostname,
ipc,
mac_address,
mem_limit,
memswap_limit,
privileged,
restart,
shm_size,
stdin_open,
tty,
user,
working_dir,
oom_score_adj,
group_add,
_hidden
});
impl Service {
pub fn inline_all(&mut self, base: &Path) -> Result<()> {
let mut new_env = BTreeMap::new();
for rel_path in &self.env_files {
let env_file = EnvFile::load(&base.join(&rel_path.value()?))?;
new_env.append(&mut env_file.to_environment()?);
}
new_env.append(&mut self.environment.clone());
self.environment = new_env;
self.env_files.clear();
Ok(())
}
}
#[test]
fn service_handles_sample_fields_correctly() {
let yaml = r#"---
dns:
- 8.8.8.8
dns_search:
- example.com
- example.net
image: hello
"#;
assert_roundtrip!(Service, yaml);
}
#[test]
fn service_env_file_is_renamed() {
let yaml = r#"---
env_file:
- foo/bar.env
"#;
let service: Service = serde_yaml::from_str(&yaml).unwrap();
assert_eq!(service.env_files.len(), 1);
assert_eq!(service.env_files[0], escape("foo/bar.env").unwrap());
}
#[test]
fn service_networks_supports_map() {
let yaml = r#"---
networks:
backend:
aliases:
- hostname2
frontend: {}
"#;
assert_roundtrip!(Service, yaml);
}
#[test]
fn service_networks_supports_list() {
let yaml = r#"---
"networks":
- "backend"
"#;
let service: Service = serde_yaml::from_str(&yaml).unwrap();
assert_eq!(service.networks.len(), 1);
assert_eq!(
service.networks.get("backend").unwrap(),
&NetworkInterface::default()
);
}
#[test]
fn service_ulimits() {
let yaml = r#"---
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
"#;
assert_roundtrip!(Service, yaml);
}