Skip to main content

sal_virt/buildah/
images.rs

1use super::BuildahError;
2use crate::buildah::execute_buildah_command;
3use sal_process::CommandResult;
4use serde::{Deserialize, Serialize};
5use serde_json::{self, Value};
6use std::collections::HashMap;
7
8/// Represents a container image
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct Image {
11    /// Image ID
12    pub id: String,
13    /// Image names/tags
14    pub names: Vec<String>,
15    /// Image size
16    pub size: String,
17    /// Creation timestamp
18    pub created: String,
19}
20
21/// List images in local storage
22///
23/// # Returns
24/// * Result with array of Image objects on success or error details
25pub fn images() -> Result<Vec<Image>, BuildahError> {
26    let result = execute_buildah_command(&["images", "--json"])?;
27
28    // Try to parse the JSON output
29    match serde_json::from_str::<serde_json::Value>(&result.stdout) {
30        Ok(json) => {
31            if let Value::Array(images_json) = json {
32                let mut images = Vec::new();
33
34                for image_json in images_json {
35                    // Extract image ID
36                    let id = match image_json.get("id").and_then(|v| v.as_str()) {
37                        Some(id) => id.to_string(),
38                        None => {
39                            return Err(BuildahError::ConversionError(
40                                "Missing image ID".to_string(),
41                            ))
42                        }
43                    };
44
45                    // Extract image names
46                    let names = match image_json.get("names").and_then(|v| v.as_array()) {
47                        Some(names_array) => {
48                            let mut names_vec = Vec::new();
49                            for name_value in names_array {
50                                if let Some(name_str) = name_value.as_str() {
51                                    names_vec.push(name_str.to_string());
52                                }
53                            }
54                            names_vec
55                        }
56                        None => Vec::new(), // Empty vector if no names found
57                    };
58
59                    // Extract image size
60                    let size = match image_json.get("size").and_then(|v| v.as_str()) {
61                        Some(size) => size.to_string(),
62                        None => "Unknown".to_string(), // Default value if size not found
63                    };
64
65                    // Extract creation timestamp
66                    let created = match image_json.get("created").and_then(|v| v.as_str()) {
67                        Some(created) => created.to_string(),
68                        None => "Unknown".to_string(), // Default value if created not found
69                    };
70
71                    // Create Image struct and add to vector
72                    images.push(Image {
73                        id,
74                        names,
75                        size,
76                        created,
77                    });
78                }
79
80                Ok(images)
81            } else {
82                Err(BuildahError::JsonParseError(
83                    "Expected JSON array".to_string(),
84                ))
85            }
86        }
87        Err(e) => Err(BuildahError::JsonParseError(format!(
88            "Failed to parse image list JSON: {}",
89            e
90        ))),
91    }
92}
93
94/// Remove one or more images
95///
96/// # Arguments
97/// * `image` - Image ID or name
98///
99/// # Returns
100/// * Result with command output or error
101pub fn image_remove(image: &str) -> Result<CommandResult, BuildahError> {
102    execute_buildah_command(&["rmi", image])
103}
104
105/// Push an image to a registry
106///
107/// # Arguments
108/// * `image` - Image name
109/// * `destination` - Destination (e.g., "docker://registry.example.com/myimage:latest")
110/// * `tls_verify` - Whether to verify TLS (default: true)
111///
112/// # Returns
113/// * Result with command output or error
114pub fn image_push(
115    image: &str,
116    destination: &str,
117    tls_verify: bool,
118) -> Result<CommandResult, BuildahError> {
119    let mut args = vec!["push"];
120
121    if !tls_verify {
122        args.push("--tls-verify=false");
123    }
124
125    args.push(image);
126    args.push(destination);
127
128    execute_buildah_command(&args)
129}
130
131/// Add an additional name to a local image
132///
133/// # Arguments
134/// * `image` - Image ID or name
135/// * `new_name` - New name for the image
136///
137/// # Returns
138/// * Result with command output or error
139pub fn image_tag(image: &str, new_name: &str) -> Result<CommandResult, BuildahError> {
140    execute_buildah_command(&["tag", image, new_name])
141}
142
143/// Pull an image from a registry
144///
145/// # Arguments
146/// * `image` - Image name
147/// * `tls_verify` - Whether to verify TLS (default: true)
148///
149/// # Returns
150/// * Result with command output or error
151pub fn image_pull(image: &str, tls_verify: bool) -> Result<CommandResult, BuildahError> {
152    let mut args = vec!["pull"];
153
154    if !tls_verify {
155        args.push("--tls-verify=false");
156    }
157
158    args.push(image);
159
160    execute_buildah_command(&args)
161}
162
163/// Commit a container to an image
164///
165/// # Arguments
166/// * `container` - Container ID or name
167/// * `image_name` - New name for the image
168/// * `format` - Optional, format to use for the image (oci or docker)
169/// * `squash` - Whether to squash layers
170/// * `rm` - Whether to remove the container after commit
171///
172/// # Returns
173/// * Result with command output or error
174pub fn image_commit(
175    container: &str,
176    image_name: &str,
177    format: Option<&str>,
178    squash: bool,
179    rm: bool,
180) -> Result<CommandResult, BuildahError> {
181    let mut args = vec!["commit"];
182
183    if let Some(format_str) = format {
184        args.push("--format");
185        args.push(format_str);
186    }
187
188    if squash {
189        args.push("--squash");
190    }
191
192    if rm {
193        args.push("--rm");
194    }
195
196    args.push(container);
197    args.push(image_name);
198
199    execute_buildah_command(&args)
200}
201
202/// Container configuration options
203///
204/// # Arguments
205/// * `container` - Container ID or name
206/// * `options` - Map of configuration options
207///
208/// # Returns
209/// * Result with command output or error
210pub fn bah_config(
211    container: &str,
212    options: HashMap<String, String>,
213) -> Result<CommandResult, BuildahError> {
214    let mut args_owned: Vec<String> = Vec::new();
215    args_owned.push("config".to_string());
216
217    // Process options map
218    for (key, value) in options.iter() {
219        let option_name = format!("--{}", key);
220        args_owned.push(option_name);
221        args_owned.push(value.clone());
222    }
223
224    args_owned.push(container.to_string());
225
226    // Convert Vec<String> to Vec<&str> for execute_buildah_command
227    let args: Vec<&str> = args_owned.iter().map(|s| s.as_str()).collect();
228
229    execute_buildah_command(&args)
230}