use std::path::Path;
use std::sync::Arc;
use async_trait::async_trait;
use crate::backend::BuildBackend;
use crate::builder::{BuildOptions, BuiltImage, RegistryAuth};
use crate::dockerfile::Dockerfile;
use crate::error::Result;
use crate::tui::BuildEvent;
pub mod proto {
#![allow(clippy::all, missing_docs, clippy::pedantic, clippy::nursery)]
tonic::include_proto!("zlayer.buildah_sidecar.v1");
}
pub mod build;
pub mod discover;
pub mod lifecycle;
pub mod ops;
pub mod tls;
pub use lifecycle::{LiveSidecar, SidecarLifecycle};
pub use tls::{ensure_tls_material, TlsMaterial};
#[derive(Debug, Clone)]
pub struct BuildahSidecarBackend {
config: Arc<zlayer_types::builder::SidecarConfig>,
lifecycle: Arc<SidecarLifecycle>,
}
impl BuildahSidecarBackend {
pub const NAME: &'static str = "buildah-sidecar";
#[must_use]
pub fn new(config: zlayer_types::builder::SidecarConfig) -> Self {
let config = Arc::new(config);
let lifecycle = Arc::new(SidecarLifecycle::new(Arc::clone(&config)));
Self { config, lifecycle }
}
#[must_use]
pub fn config(&self) -> &zlayer_types::builder::SidecarConfig {
&self.config
}
#[must_use]
pub fn lifecycle(&self) -> &Arc<SidecarLifecycle> {
&self.lifecycle
}
}
impl Default for BuildahSidecarBackend {
fn default() -> Self {
Self::new(zlayer_types::builder::SidecarConfig::default())
}
}
#[async_trait]
impl BuildBackend for BuildahSidecarBackend {
async fn build_image(
&self,
context: &Path,
dockerfile: &Dockerfile,
options: &BuildOptions,
event_tx: Option<std::sync::mpsc::Sender<BuildEvent>>,
) -> Result<BuiltImage> {
self.build_image_impl(context, dockerfile, options, event_tx)
.await
}
async fn push_image(&self, tag: &str, auth: Option<&RegistryAuth>) -> Result<()> {
self.push_image_impl(tag, auth).await
}
async fn tag_image(&self, image: &str, new_tag: &str) -> Result<()> {
self.tag_image_impl(image, new_tag).await
}
async fn manifest_create(&self, name: &str) -> Result<()> {
self.manifest_create_impl(name).await
}
async fn manifest_add(&self, manifest: &str, image: &str) -> Result<()> {
self.manifest_add_impl(manifest, image).await
}
async fn manifest_push(&self, name: &str, destination: &str) -> Result<()> {
self.manifest_push_impl(name, destination).await
}
async fn is_available(&self) -> bool {
self.lifecycle.ensure().await.is_ok()
}
fn name(&self) -> &'static str {
Self::NAME
}
}
#[cfg(test)]
#[allow(unsafe_code)]
mod tests {
use super::*;
use crate::TEST_ENV_LOCK;
#[test]
fn new_holds_config() {
let cfg = zlayer_types::builder::SidecarConfig {
addr: Some("127.0.0.1:1234".into()),
tls_dir: None,
idle_secs: 99,
..Default::default()
};
let backend = BuildahSidecarBackend::new(cfg.clone());
assert_eq!(backend.config(), &cfg);
assert_eq!(backend.name(), "buildah-sidecar");
}
#[tokio::test]
#[allow(clippy::await_holding_lock)]
async fn default_is_unavailable_when_binary_missing() {
let _g = TEST_ENV_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let prev_path = std::env::var_os("PATH");
let prev_buildd_bin = std::env::var_os("ZLAYER_BUILDD_BIN");
let prev_data_dir = std::env::var_os("ZLAYER_DATA_DIR");
let tmp = tempfile::tempdir().unwrap();
unsafe {
std::env::remove_var("ZLAYER_BUILDD_BIN");
std::env::set_var("PATH", "/nonexistent-zlayer-test-dir");
std::env::set_var("ZLAYER_DATA_DIR", tmp.path());
}
let cfg = zlayer_types::builder::SidecarConfig {
addr: None,
tls_dir: Some(tmp.path().to_path_buf()),
idle_secs: 30,
..Default::default()
};
let backend = BuildahSidecarBackend::new(cfg);
let available = backend.is_available().await;
unsafe {
match prev_path {
Some(v) => std::env::set_var("PATH", v),
None => std::env::remove_var("PATH"),
}
match prev_buildd_bin {
Some(v) => std::env::set_var("ZLAYER_BUILDD_BIN", v),
None => std::env::remove_var("ZLAYER_BUILDD_BIN"),
}
match prev_data_dir {
Some(v) => std::env::set_var("ZLAYER_DATA_DIR", v),
None => std::env::remove_var("ZLAYER_DATA_DIR"),
}
}
assert!(
!available,
"is_available should be false when zlayer-buildd cannot be discovered"
);
}
#[test]
fn proto_types_compile() {
let req = proto::BuildRequest::default();
assert!(req.context_dir.is_empty());
let _client_module_exists: Option<
proto::build_service_client::BuildServiceClient<tonic::transport::Channel>,
> = None;
}
}