opencontainers 0.0.0-alpha.2

OpenContainers Implementation in Rust
Documentation
pub use super::go::{GoArch, GoOs};
use std::collections::HashMap;

#[derive(Debug, Fail)]
#[allow(clippy::large_enum_variant)]
pub enum ImageSpecError {
    #[fail(display = "JSON Error: {:?}", _0)]
    JsonError(serde_json::Error),
}

/// Image structure.
///
/// # Spec
///
/// > # Image JSON
/// >
/// > * Each image has an associated JSON structure which describes some basic
/// >   information about the image such as date created, author, as well as
/// >   execution/runtime configuration like its entrypoint, default arguments,
/// >   networking, and volumes.
/// > * The JSON structure also references a cryptographic hash of each layer
/// >   used by the image, and provides history information for those layers.
/// > * The JSON structure also references a cryptographic hash of each layer
/// >   used by the image, and provides history information for those layers.
/// > * Changing it means creating a new derived image, instead of changing the
/// >   existing image.
#[derive(Debug, Deserialize, Serialize)]
pub struct ImageV1 {
    /// A combinedChanging it means creating a new derived image, instead of changing the existing image.
    /// defined by RFC 3339, section 5.6.
    created: Option<String>,

    /// Gives the name and/or email address of the person or entity which
    /// created and is responsible for maintaining the image.
    author: Option<String>,

    /// The CPU architecture which the binaries in this image are built to run
    /// on. Configurations SHOULD use, and implementations SHOULD understand,
    /// values listed in the Go Language document for GOARCH.
    pub architecture: GoArch,

    /// The name of the operating system which the image is built to run on.
    /// Configurations SHOULD use, and implementations SHOULD understand, values
    /// listed in the Go Language document for GOOS.
    pub os: GoOs,

    /// The execution parameters which SHOULD be used as a base when running a
    /// container using the image. This field can be null, in which case any
    /// execution parameters should be specified at creation of the container.
    config: Option<ConfigV1>,

    /// The rootfs key references the layer content addresses used by the image.
    /// This makes the image config hash depend on the filesystem hash.
    rootfs: RootFSV1,

    /// Describes the history of each layer. The array is ordered from first to
    /// last.
    history: Option<Vec<HistoryV1>>,
}

impl std::str::FromStr for ImageV1 {
    type Err = ImageSpecError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        serde_json::from_str(s).map_err(ImageSpecError::JsonError)
    }
}

#[derive(Debug, Serialize, Deserialize)]
struct Empty {}

#[derive(Debug, Serialize, Deserialize)]
pub struct ConfigV1 {
    /// The username or UID which is a platform-specific structure that allows
    /// specific control over which user the process run as. This acts as a
    /// default value to use when the value is not specified when creating a
    /// container. For Linux based systems, all of the following are valid:
    /// `user`, `uid`, `user:group`, `uid:gid`, `uid:group`, `user:gid`.
    /// If `group`/`gid` is not specified, the default group and supplementary
    /// groups of the given `user`/`uid` in `/etc/passwd` from the container are
    /// applied.
    #[serde(rename = "User")]
    user: Option<String>,

    /// A set of ports to expose from a container running this image. Its keys
    /// can be in the format of: `port/tcp`, `port/udp`, `port` with the default
    /// protocol being tcp if not specified. These values act as defaults and
    /// are merged with any specified when creating a container.
    ///
    /// NOTE: This JSON structure value is unusual because it is a direct JSON
    /// serialization of the Go type `map[string]struct{}` and is represented in
    /// JSON as an object mapping its keys to an empty object.
    #[serde(rename = "ExposedPorts")]
    exposed_ports: Option<HashMap<String, Empty>>,

    /// Entries are in the format of VARNAME=VARVALUE. These values act as
    /// defaults and are merged with any specified when creating a container.
    #[serde(rename = "Env")]
    env: Option<Vec<String>>,

    /// A list of arguments to use as the command to execute when the container
    /// starts. These values act as defaults and may be replaced by an
    /// entrypoint specified when creating a container.
    #[serde(rename = "Entrypoint")]
    entrypoint: Option<Vec<String>>,

    /// Default arguments to the entrypoint of the container. These values act
    /// as defaults and may be replaced by any specified when creating a
    /// container. If an Entrypoint value is not specified, then the first entry
    /// of the `Cmd` array SHOULD be interpreted as the executable to run.
    #[serde(rename = "Cmd")]
    cmd: Option<Vec<String>>,

    /// A set of directories describing where the process is likely write data
    /// pecific to a container instance. NOTE: This JSON structure value is
    /// unusual because it is a direct JSON serialization of the Go type
    /// `map[string]struct{}` and is represented in JSON as an object mapping
    /// its keys to an empty object.
    #[serde(rename = "Volumes")]
    volumes: Option<HashMap<String, Empty>>,

    /// Sets the current working directory of the entrypoint process in the
    /// container. This value acts as a default and may be replaced by a working
    /// directory specified when creating a container.
    #[serde(rename = "WorkingDir")]
    working_dir: Option<String>,

    /// The field contains arbitrary metadata for the container. This property
    /// MUST use the [annotation rules]
    ///
    /// [annotation rules]: https://github.com/opencontainers/image-spec/blob/master/annotations.md#rules.
    #[serde(rename = "Labels")]
    labels: Option<HashMap<String, String>>,

    /// The field contains the system call signal that will be sent to the
    /// container to exit. The signal can be a signal name in the format
    /// SIGNAME, for instance SIGKILL rSIGTMIN+3.
    #[serde(rename = "StopSignal")]
    stop_signal: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RootFSV1 {
    /// MUST be set to `layers`. Implementations MUST generate an error if they
    /// encounter a unknown value while verifying or unpacking an image.
    r#type: String,

    /// An array of layer content hashes (DiffIDs), in order from first to last.
    diff_ids: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct HistoryV1 {
    //// A combined date and time at which the layer was created, formatted as
    /// defined by RFC 3339, section 5.6.
    created: Option<String>,

    /// The author of the build point.
    author: Option<String>,

    /// The command which created the layer.
    created_by: Option<String>,

    /// A custom message set when creating the layer.
    comment: Option<String>,

    /// This field is used to mark if the history item created a filesystem
    /// diff. It is set to true if this history item doesn't correspond to an
    /// actual layer in the rootfs section (for example, Dockerfile's ENV
    /// command results in no change to the filesystem).
    empty_layer: Option<bool>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json;

    #[test]
    fn test_config_v1() {
        let test_data = include_str!("test/config-v1.test.json");

        let image: ImageV1 =
            serde_json::from_str(test_data).expect("Could not deserialize configs");

        assert_eq!(image.architecture, GoArch::AMD64);
        assert_eq!(image.os, GoOs::Linux);
    }
}