imgapi/
manifest.rs

1use chrono::{DateTime, Utc};
2use derive_builder::{Builder, UninitializedFieldError};
3use indexmap::IndexMap;
4use miette::Diagnostic;
5use serde::{Deserialize, Serialize};
6use serde_json::{Map, Value};
7use std::fmt::Display;
8use strum::{Display as StrumDisplay, EnumString};
9use thiserror::Error;
10use url::Url;
11use uuid::Uuid;
12
13#[doc = "Error type for All zfs related builders"]
14#[derive(Debug, Error, Diagnostic)]
15#[non_exhaustive]
16pub enum ManifestBuilderError {
17    // where `LoremBuilder` is the name of the builder struct
18    /// Uninitialized field
19    UninitializedField(&'static str),
20    /// Custom validation error
21    ValidationError(String),
22}
23
24impl From<String> for ManifestBuilderError {
25    fn from(s: String) -> Self {
26        Self::ValidationError(s)
27    }
28}
29impl From<UninitializedFieldError> for ManifestBuilderError {
30    fn from(value: UninitializedFieldError) -> Self {
31        Self::UninitializedField(value.field_name())
32    }
33}
34impl Display for ManifestBuilderError {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            ManifestBuilderError::UninitializedField(value) => {
38                write!(f, "field {} must be initialized", value)
39            }
40            ManifestBuilderError::ValidationError(s) => write!(f, "validation error: {}", s),
41        }
42    }
43}
44
45#[derive(Deserialize, Serialize, Debug, Clone, Builder)]
46#[builder(build_fn(error = "ManifestBuilderError"))]
47pub struct Manifest {
48    //Version of the manifest format/spec. The current value is 2.
49    #[builder(setter(skip), default = "2")]
50    pub v: i32,
51
52    //The unique identifier for a UUID. This is set by the IMGAPI server. See details below.
53    #[builder(setter(skip), default = "uuid::Builder::nil().into_uuid()")]
54    pub uuid: Uuid,
55
56    //The UUID of the owner of this image (the account that created it).
57    #[builder(setter(skip), default = "uuid::Builder::nil().into_uuid()")]
58    pub owner: Uuid,
59
60    //A short name for this image. Max 512 characters (though practical usage should be much shorter). No uniqueness guarantee.
61    #[builder(setter(into))]
62    pub name: String,
63
64    //A version string for this image. Max 128 characters. No uniqueness guarantee.
65    #[builder(setter(into))]
66    pub version: String,
67
68    //A short description of the image.
69    #[builder(setter(into, strip_option), default)]
70    pub description: Option<String>,
71
72    //Homepage URL where users can find more information about the image.
73    #[builder(setter(into, strip_option), default)]
74    pub homepage: Option<Url>,
75
76    //URL of the End User License Agreement (EULA) for the image.
77    #[builder(setter(into, strip_option), default)]
78    pub eula: Option<Url>,
79
80    //Indicates if the image has an icon file. If not present, then no icon is present.
81    #[builder(setter(into, strip_option), default)]
82    pub icon: Option<bool>,
83
84    //The current state of the image. One of 'active', 'unactivated', 'disabled', 'creating', 'failed'.
85    #[builder(default)]
86    pub state: ImageState,
87
88    //An object with details on image creation failure. It only exists when state=='failed'.
89    #[builder(setter(into, strip_option), default)]
90    pub error: Option<Map<String, Value>>,
91
92    //Indicates if this image is available for provisioning.
93    #[builder(default = "false")]
94    pub disabled: bool,
95
96    //Indicates if this image is publicly available.
97    #[builder(default = "false")]
98    pub public: bool,
99
100    //The date at which the image is activated. Set by the IMGAPI server.
101    #[builder(setter(into, strip_option), default)]
102    pub published_at: Option<DateTime<Utc>>,
103
104    //The image type. One of "zone-dataset" for a ZFS dataset used to create a new SmartOS zone, "lx-dataset" for a Lx-brand image, "lxd" for a LXD image, "zvol" for a virtual machine image or "other" for image types that serve any other specific purpose.
105    #[serde(rename = "type")]
106    #[builder(setter(into), default)]
107    pub image_type: ImageType,
108
109    //The OS family this image provides. One of "smartos", "windows", "linux", "bsd", "illumos" or "other".
110    #[builder(setter(into), default)]
111    pub os: ImageOs,
112
113    //The origin image UUID if this is an incremental image.
114    #[builder(setter(into, strip_option), default)]
115    pub origin: Option<Uuid>,
116
117    //An array of objects describing the image files.
118    #[builder(default)]
119    pub files: Vec<Map<String, Value>>,
120
121    //Access Control List. An array of account UUIDs given access to a private image. The field is only relevant to private images.
122    #[builder(setter(into, strip_option), default)]
123    pub acl: Option<Vec<Uuid>>,
124
125    //A set of named requirements for provisioning a VM with this image
126    #[builder(setter(into, strip_option), default)]
127    pub requirements: Option<ImageRequirements>,
128
129    //A list of users for which passwords should be generated for provisioning. This may only make sense for some images. Example: [{"name": "root"}, {"name": "admin"}]
130    #[builder(setter(into, strip_option), default)]
131    pub users: Option<Vec<ImageUsers>>,
132
133    //A list of tags that can be used by operators for additional billing processing.
134    #[builder(setter(into, strip_option), default)]
135    pub billing_tags: Option<Vec<String>>,
136
137    //An object that defines a collection of properties that is used by other APIs to evaluate where should customer VMs be placed.
138    #[builder(setter(into, strip_option), default)]
139    pub traits: Option<Vec<String>>,
140
141    //An object of key/value pairs that allows clients to categorize images by any given criteria.
142    #[builder(setter(into, strip_option), default)]
143    pub tags: Option<IndexMap<String, String>>,
144
145    //A boolean indicating whether to generate passwords for the users in the "users" field. If not present, the default value is true.
146    #[builder(setter(into, strip_option), default)]
147    pub generate_password: Option<bool>,
148
149    //A list of inherited directories (other than the defaults for the brand).
150    #[builder(setter(into, strip_option), default)]
151    pub inherited_directories: Option<Vec<String>>,
152
153    //Array of channel names to which this image belongs.
154    #[builder(setter(into, strip_option), default)]
155    pub channels: Option<Vec<String>>,
156
157    #[serde(flatten)]
158    #[builder(setter(into, strip_option), default)]
159    pub vm_image_properties: Option<ImageVMProperties>,
160}
161
162#[derive(Default, Deserialize, Serialize, Debug, Clone, StrumDisplay, PartialEq, Eq)]
163#[serde(rename_all = "snake_case")]
164pub enum ImageState {
165    Active,
166    Unactivated,
167    Disabled,
168    #[default]
169    Creating,
170    Failed,
171}
172
173#[derive(Default, Deserialize, Serialize, Debug, Clone, StrumDisplay, PartialEq, Eq)]
174#[serde(rename_all = "kebab-case")]
175pub enum ImageType {
176    #[strum(serialize = "zone-dataset")]
177    #[default]
178    ZoneDataset,
179    #[strum(serialize = "lx-dataset")]
180    LxDataset,
181    #[strum(serialize = "lxd")]
182    Lxd,
183    #[strum(serialize = "zvol")]
184    Zvol,
185    #[strum(serialize = "other")]
186    Other,
187}
188
189#[derive(Default, Deserialize, Serialize, Debug, Clone, StrumDisplay, PartialEq, Eq)]
190#[serde(rename_all = "kebab-case")]
191pub enum ImageOs {
192    #[default]
193    Smartos,
194    Windows,
195    Linux,
196    Bsd,
197    Illumos,
198    Other,
199}
200
201#[derive(Deserialize, Serialize, Debug, Clone, Builder)]
202pub struct ImageRequirements {
203    //Defines the minimum number of network interfaces required by this image.
204    #[builder(setter(into, strip_option), default)]
205    pub networks: Option<Vec<RequirementNetworks>>,
206
207    //Defines the brand that is required to provision with this image.
208    #[builder(setter(into, strip_option), default)]
209    pub brand: Option<String>,
210
211    //Indicates that provisioning with this image requires that an SSH public key be provided.
212    #[builder(setter(into, strip_option), default)]
213    pub ssh_key: Option<bool>,
214
215    //Minimum RAM (in MiB) required to provision this image.
216    #[builder(setter(into, strip_option), default)]
217    pub min_ram: Option<i64>,
218
219    //Maximum RAM (in MiB) this image may be provisioned with.
220    #[builder(setter(into, strip_option), default)]
221    pub max_ram: Option<i64>,
222
223    //Minimum platform requirement for provisioning with this image.
224    #[builder(setter(into, strip_option), default)]
225    pub min_platform: Option<IndexMap<String, String>>,
226
227    //Maximum platform requirement for provisioning with this image.
228    #[builder(setter(into, strip_option), default)]
229    pub max_platform: Option<IndexMap<String, String>>,
230
231    //Bootrom image to use with this image.
232    #[builder(setter(into, strip_option), default)]
233    pub bootrom: Option<ImageRequirementBootRom>,
234}
235
236#[derive(Deserialize, Serialize, Debug, Clone, Builder)]
237pub struct RequirementNetworks {
238    name: String,
239    description: String,
240}
241
242#[derive(Deserialize, Serialize, Debug, Clone, StrumDisplay)]
243#[serde(rename_all = "kebab-case")]
244pub enum ImageRequirementBootRom {
245    Bios,
246    Uefi,
247}
248
249#[derive(Deserialize, Serialize, Debug, Clone)]
250pub struct ImageUsers {
251    name: String,
252}
253
254#[derive(Deserialize, Serialize, Debug, Clone, Builder)]
255#[builder(build_fn(error = "ManifestBuilderError"))]
256pub struct ImageVMProperties {
257    //NIC driver used by this VM image.
258    #[builder(setter(into))]
259    pub nic_driver: NetDrivers,
260
261    //Disk driver used by this VM image.
262    #[builder(setter(into))]
263    pub disk_driver: DiskDrivers,
264
265    //The QEMU CPU model to use for this VM image.
266    #[builder(setter(into))]
267    pub cpu_type: String,
268
269    //The size (in MiB) of this VM image's disk.
270    pub image_size: u64,
271}
272
273#[derive(Deserialize, Serialize, Debug, Clone, EnumString, StrumDisplay)]
274#[serde(rename_all = "kebab-case")]
275#[strum(serialize_all = "kebab-case")]
276pub enum NetDrivers {
277    Virtio,
278    E1000g0,
279}
280
281#[derive(Deserialize, Serialize, Debug, Clone, EnumString, StrumDisplay)]
282#[serde(rename_all = "kebab-case")]
283#[strum(serialize_all = "kebab-case")]
284pub enum DiskDrivers {
285    Virtio,
286    Sata,
287}
288
289#[derive(Deserialize, Serialize, Debug, Clone, Builder)]
290pub struct ImageFile {
291    //SHA-1 hex digest of the file content. Used for upload/download corruption checking.
292    pub sha1: String,
293
294    //Number of bytes. Maximum 20GiB. This maximum is meant to be a "you'll never hit it" cap, the purpose is to inform cache handling in IMGAPI servers.
295    pub size: i64,
296
297    //The type of file compression used by the file. One of 'bzip2', 'gzip', 'none'.
298    pub compression: ImageFileCompression,
299
300    //Optional. The ZFS internal unique identifier for this dataset's snapshot (available via zfs get guid SNAPSHOT, e.g. zfs get guid zones/f669428c-a939-11e2-a485-b790efc0f0c1@final). If available, this is used to ensure a common base snapshot for incremental images (via imgadm create -i) and VM migrations (via vmadm send/receive).
301    #[builder(setter(into, strip_option), default)]
302    pub dataset_guid: Option<String>,
303
304    //Only included if ?inclAdminFields=true is passed to GetImage/ListImages. The IMGAPI storage type used to store this file.
305    #[builder(setter(into, strip_option), default)]
306    pub stor: Option<String>,
307
308    //Optional. Docker digest of the file contents. Only used when manifest.type is 'docker'. This field gets set automatically by the AdminImportDockerImage call.
309    #[builder(setter(into, strip_option), default)]
310    pub digest: Option<String>,
311
312    //Optional. Docker digest of the uncompressed file contents. Only used when manifest.type is 'docker'. This field gets set automatically by the AdminImportDockerImage call. Note that this field will be removed in a future version of IMGAPI.
313    #[serde(rename = "uncompressedDigest")]
314    #[builder(setter(into, strip_option), default)]
315    pub uncompressed_digest: Option<String>,
316}
317
318#[derive(Deserialize, Serialize, Debug, Clone, StrumDisplay)]
319#[serde(rename_all = "kebab-case")]
320pub enum ImageFileCompression {
321    Bzip2,
322    Gzip,
323    None,
324}