zlayer_builder/backend/buildah_sidecar/
mod.rs1use std::path::Path;
13use std::sync::Arc;
14
15use async_trait::async_trait;
16
17use crate::backend::BuildBackend;
18use crate::builder::{BuildOptions, BuiltImage, RegistryAuth};
19use crate::dockerfile::Dockerfile;
20use crate::error::Result;
21use crate::tui::BuildEvent;
22
23pub mod proto {
25 #![allow(clippy::all, missing_docs, clippy::pedantic, clippy::nursery)]
26 tonic::include_proto!("zlayer.buildah_sidecar.v1");
27}
28
29pub mod build;
30pub mod discover;
31pub mod lifecycle;
32pub mod ops;
33pub mod tls;
34
35pub use lifecycle::{LiveSidecar, SidecarLifecycle};
36pub use tls::{ensure_tls_material, TlsMaterial};
37
38#[derive(Debug, Clone)]
51pub struct BuildahSidecarBackend {
52 config: Arc<zlayer_types::builder::SidecarConfig>,
53 lifecycle: Arc<SidecarLifecycle>,
54}
55
56impl BuildahSidecarBackend {
57 pub const NAME: &'static str = "buildah-sidecar";
59
60 #[must_use]
62 pub fn new(config: zlayer_types::builder::SidecarConfig) -> Self {
63 let config = Arc::new(config);
64 let lifecycle = Arc::new(SidecarLifecycle::new(Arc::clone(&config)));
65 Self { config, lifecycle }
66 }
67
68 #[must_use]
70 pub fn config(&self) -> &zlayer_types::builder::SidecarConfig {
71 &self.config
72 }
73
74 #[must_use]
77 pub fn lifecycle(&self) -> &Arc<SidecarLifecycle> {
78 &self.lifecycle
79 }
80}
81
82impl Default for BuildahSidecarBackend {
83 fn default() -> Self {
84 Self::new(zlayer_types::builder::SidecarConfig::default())
85 }
86}
87
88#[async_trait]
89impl BuildBackend for BuildahSidecarBackend {
90 async fn build_image(
91 &self,
92 context: &Path,
93 dockerfile: &Dockerfile,
94 options: &BuildOptions,
95 event_tx: Option<std::sync::mpsc::Sender<BuildEvent>>,
96 ) -> Result<BuiltImage> {
97 self.build_image_impl(context, dockerfile, options, event_tx)
98 .await
99 }
100
101 async fn push_image(&self, tag: &str, auth: Option<&RegistryAuth>) -> Result<()> {
102 self.push_image_impl(tag, auth).await
103 }
104
105 async fn tag_image(&self, image: &str, new_tag: &str) -> Result<()> {
106 self.tag_image_impl(image, new_tag).await
107 }
108
109 async fn manifest_create(&self, name: &str) -> Result<()> {
110 self.manifest_create_impl(name).await
111 }
112
113 async fn manifest_add(&self, manifest: &str, image: &str) -> Result<()> {
114 self.manifest_add_impl(manifest, image).await
115 }
116
117 async fn manifest_push(
118 &self,
119 name: &str,
120 destination: &str,
121 auth: Option<&RegistryAuth>,
122 ) -> Result<()> {
123 self.manifest_push_impl(name, destination, auth).await
124 }
125
126 async fn is_available(&self) -> bool {
127 self.lifecycle.ensure().await.is_ok()
132 }
133
134 fn name(&self) -> &'static str {
135 Self::NAME
136 }
137}
138
139#[cfg(test)]
140#[allow(unsafe_code)]
141mod tests {
142 use super::*;
143 use crate::TEST_ENV_LOCK;
144
145 #[test]
146 fn new_holds_config() {
147 let cfg = zlayer_types::builder::SidecarConfig {
148 addr: Some("127.0.0.1:1234".into()),
149 tls_dir: None,
150 idle_secs: 99,
151 ..Default::default()
152 };
153 let backend = BuildahSidecarBackend::new(cfg.clone());
154 assert_eq!(backend.config(), &cfg);
155 assert_eq!(backend.name(), "buildah-sidecar");
156 }
157
158 #[tokio::test]
162 #[allow(clippy::await_holding_lock)]
163 async fn default_is_unavailable_when_binary_missing() {
164 let _g = TEST_ENV_LOCK
165 .lock()
166 .unwrap_or_else(std::sync::PoisonError::into_inner);
167
168 let prev_path = std::env::var_os("PATH");
171 let prev_buildd_bin = std::env::var_os("ZLAYER_BUILDD_BIN");
172 let prev_data_dir = std::env::var_os("ZLAYER_DATA_DIR");
173
174 let tmp = tempfile::tempdir().unwrap();
175 unsafe {
177 std::env::remove_var("ZLAYER_BUILDD_BIN");
178 std::env::set_var("PATH", "/nonexistent-zlayer-test-dir");
179 std::env::set_var("ZLAYER_DATA_DIR", tmp.path());
182 }
183
184 let cfg = zlayer_types::builder::SidecarConfig {
185 addr: None,
186 tls_dir: Some(tmp.path().to_path_buf()),
189 idle_secs: 30,
190 ..Default::default()
191 };
192 let backend = BuildahSidecarBackend::new(cfg);
193 let available = backend.is_available().await;
194
195 unsafe {
197 match prev_path {
198 Some(v) => std::env::set_var("PATH", v),
199 None => std::env::remove_var("PATH"),
200 }
201 match prev_buildd_bin {
202 Some(v) => std::env::set_var("ZLAYER_BUILDD_BIN", v),
203 None => std::env::remove_var("ZLAYER_BUILDD_BIN"),
204 }
205 match prev_data_dir {
206 Some(v) => std::env::set_var("ZLAYER_DATA_DIR", v),
207 None => std::env::remove_var("ZLAYER_DATA_DIR"),
208 }
209 }
210
211 assert!(
212 !available,
213 "is_available should be false when zlayer-buildd cannot be discovered"
214 );
215 }
216
217 #[test]
218 fn proto_types_compile() {
219 let req = proto::BuildRequest::default();
221 assert!(req.context_dir.is_empty());
222 let _client_module_exists: Option<
223 proto::build_service_client::BuildServiceClient<tonic::transport::Channel>,
224 > = None;
225 }
226}