Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,8 @@ impl TryFrom<service::Build> for Build {
let mut tags = tags.into_iter();
let tag = tags
.next()
.ok_or_eyre("an image tag is required")?
.into_inner();
.map(|t| t.into_inner())
.unwrap_or_else(|| "latest".to_string());
ensure!(
tags.next().is_none(),
"Quadlet only supports setting a single tag"
Expand Down
20 changes: 18 additions & 2 deletions src/cli/compose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use compose_spec::{
};
use indexmap::IndexMap;

use cli::Service as ServiceUnit;
use crate::cli;
use crate::quadlet::{self, container::volume::Source, Globals};

use super::{k8s, Build, Container, File, GlobalArgs, Unit};
Expand Down Expand Up @@ -95,6 +97,10 @@ impl Compose {
compose_file,
} = self;

// get the parent directory of the provided compose_file (if exists)
let compose_parent: Option<PathBuf> = compose_file.clone().and_then(|file| {
Some(PathBuf::from(file.parent().unwrap()))
});
let mut options = compose_spec::Compose::options();
options.apply_merge(true);
let compose = read_from_file_or_stdin(compose_file.as_deref(), &options)
Expand All @@ -103,18 +109,28 @@ impl Compose {
.validate_all()
.wrap_err("error validating compose file")?;

let build_required = compose.services.iter().find(|(identifier, service)| {
service.build.is_some()
}).is_some();

if kube {
let mut k8s_file = k8s::File::try_from(compose)
.wrap_err("error converting compose file into Kubernetes YAML")?;

let kube =
let mut kube =
quadlet::Kube::new(PathBuf::from(format!("{}-kube.yaml", k8s_file.name)).into());

// if one of the compose services has a build section let's add --build=true to the podman args.
if build_required {
kube.push_arg("build", "true");
}

let quadlet_file = quadlet::File {
name: k8s_file.name.clone(),
unit,
resource: kube.into(),
globals: Globals::default(),
service: None,
service: if build_required { Some(ServiceUnit::from(compose_parent.unwrap())) } else { None },
install,
};

Expand Down
19 changes: 14 additions & 5 deletions src/cli/k8s/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ use self::mount::tmpfs_and_volumes_try_into_volume_mounts;
#[allow(clippy::struct_excessive_bools)]
pub(super) struct Service {
unsupported: Unsupported,
build: Option<ShortOrLong<Context, Build>>,
name: Identifier,
resources: ContainerResources,
security_context: ContainerSecurityContext,
Expand Down Expand Up @@ -154,7 +155,6 @@ impl Service {
Self {
unsupported: Unsupported {
attach,
build,
blkio_config,
cpu_count,
cpu_percent,
Expand Down Expand Up @@ -230,6 +230,7 @@ impl Service {
security_opt,
user,
},
build,
command,
entrypoint,
environment,
Expand Down Expand Up @@ -268,6 +269,7 @@ impl Service {
tty,
volumes,
working_dir,
build
} = self;

unsupported.ensure_empty()?;
Expand All @@ -277,6 +279,16 @@ impl Service {
// converting `tmpfs` always succeeds
.wrap_err("error converting `volumes`")?;

let container_image: String;
if build.is_some() {
container_image = match build.unwrap() {
ShortOrLong::Short(build) => Some(build),
ShortOrLong::Long(build) => build.context,
}.unwrap().into_string().unwrap();
} else {
container_image = image.ok_or_eyre("`image` is required")?.into_inner();
}

spec.containers.push(Container {
name: name.into(),
resources: resources.into_resource_requirements(),
Expand Down Expand Up @@ -316,7 +328,7 @@ impl Service {
})
.transpose()
.wrap_err("error converting `healthcheck`")?,
image: Some(image.ok_or_eyre("`image` is required")?.into_inner()),
image: Some(container_image),
ports: (!ports.is_empty())
.then(|| {
ports::into_long_iter(ports)
Expand Down Expand Up @@ -679,7 +691,6 @@ fn security_opt_try_into_selinux_options(
/// [`Container`]s.
struct Unsupported {
attach: bool,
build: Option<ShortOrLong<Context, Build>>,
blkio_config: Option<BlkioConfig>,
cpu_count: Option<u64>,
cpu_percent: Option<Percent>,
Expand Down Expand Up @@ -752,7 +763,6 @@ impl Unsupported {
fn ensure_empty(&self) -> color_eyre::Result<()> {
let Self {
attach,
build,
blkio_config,
cpu_count,
cpu_percent,
Expand Down Expand Up @@ -817,7 +827,6 @@ impl Unsupported {

let unsupported_options = [
("attach", *attach),
("build", build.is_none()),
("blkio_config", blkio_config.is_none()),
("cpu_count", cpu_count.is_none()),
("cpu_percent", cpu_percent.is_none()),
Expand Down
16 changes: 15 additions & 1 deletion src/cli/service.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt::{self, Display, Formatter};

use std::path::{ PathBuf};
use clap::{Args, ValueEnum};
use compose_spec::service::Restart;

Expand All @@ -8,6 +8,7 @@ pub struct Service {
/// Configure if and when the service should be restarted
#[arg(long, value_name = "POLICY")]
restart: Option<RestartConfig>,
working_directory: Option<PathBuf>,
}

impl Service {
Expand All @@ -22,6 +23,9 @@ impl Display for Service {
if let Some(restart) = self.restart.and_then(|restart| restart.to_possible_value()) {
writeln!(f, "Restart={}", restart.get_name())?;
}
if let Some(working_directory) = self.working_directory.as_ref().and_then(|working_directory| working_directory.into()) {
writeln!(f, "WorkingDirectory={}", working_directory.display())?;
}
Ok(())
}
}
Expand All @@ -30,6 +34,16 @@ impl From<RestartConfig> for Service {
fn from(restart: RestartConfig) -> Self {
Self {
restart: Some(restart),
working_directory: None,
}
}
}

impl From<PathBuf> for Service {
fn from(working_directory: PathBuf) -> Self {
Self {
restart: None,
working_directory: Some(working_directory),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/quadlet/kube.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ impl Kube {
}

/// Add `--{flag} {arg}` to `PodmanArgs=`.
fn push_arg(&mut self, flag: &str, arg: &str) {
pub(crate) fn push_arg(&mut self, flag: &str, arg: &str) {
let podman_args = self.podman_args.get_or_insert_with(String::new);
if !podman_args.is_empty() {
podman_args.push(' ');
}
podman_args.push_str("--");
podman_args.push_str(flag);
podman_args.push(' ');
podman_args.push('=');
podman_args.push_str(arg);
}
}
Expand Down
Loading