oci-spec-rs

repository·main·Indexed 43 days ago

https://github.com/youki-dev/oci-spec-rs

A Rust library providing programmatic access to Open Container Initiative (OCI) specifications, including Image Format, Runtime, and Distribution specs. Version 0.10.0 provides tools for managing OCI image manifests, configurations, artifact manifests, and descriptors using builder patterns and serialization/deserialization methods.

Tokens
12.2K
Snippets
39
Records
50
Agent score
53%

What's inside oci-spec-rs

  1. How to become a Maintainer of oci-spec-rs

    main

    To become a Maintainer, you must demonstrate commitment, technical ability, and collaboration. The process involves meeting specific contribution thresholds and receiving approval via a simple majority vote from existing Maintainers.

    Requirements

    • Commitment:
      • Participate in discussions, contributions, and reviews (code and documentation) for at least 3 months.
      • Perform reviews for at least 10 non-trivial pull requests.
      • Contribute at least 5 non-trivial pull requests that are successfully merged.
    • Technical Skills: Ability to write quality code and/or documentation.
    • Collaboration: Ability to work effectively with the team.
    • Project Knowledge: Understanding of the team's processes (testing, code review), the codebase, and the project's coding/documentation style.

    Application Process

    1. An existing Maintainer must propose you by opening an issue within the repository.
    2. The existing Maintainer Council will evaluate the nomination.
    3. A simple majority vote of existing Maintainers is required for approval.
  2. Access OCI specifications via modules

    main

    The oci-spec crate provides structured Rust representations of the Open Container Initiative (OCI) specifications. Depending on the enabled features, you can access the following modules:

    • image: Types and structures for the OCI Image Format specification (requires image feature).
    • runtime: Types and structures for the OCI Runtime specification (requires runtime feature).
    • distribution: Types and structures for the OCI Distribution specification (requires distribution feature).
  3. Configure OCI runtime lifecycle hooks

    main

    The Hooks struct allows you to specify commands that run at specific events during a container's lifecycle. These hooks are used for setup and teardown operations, such as configuring network namespaces or cleaning up resources.

    Hook Lifecycle and Namespaces

    Hook TypeTimingNamespace
    create_runtimeAfter container creation, before pivot_rootRuntime Namespace
    create_containerAfter container creation, before pivot_rootContainer Namespace
    start_containerAfter the start operation, before the container process startsContainer Namespace
    poststartAfter the container process has startedRuntime Namespace
    poststopAfter the container process exitsRuntime Namespace
    prestart (Deprecated)After start is called, before the user program executesRuntime Namespace

    Note: prestart is deprecated in favor of create_runtime, create_container, and start_container hooks.

    // Example of building a Hooks configuration
    let hooks = Hooks::builder()
        .create_runtime(vec![Hook::builder().path("/usr/bin/setup-net").build().unwrap()])
        .poststop(vec![Hook::builder().path("/usr/bin/cleanup").build().unwrap()])
        .build()
        .unwrap();
  4. Represent OCI runtime capabilities with the Capability enum

    main

    The Capability enum represents Linux kernel capabilities used to divide superuser privileges into distinct units. This allows for fine-grained permission control in OCI runtimes.

    When deserializing Capability values (e.g., from JSON or configuration files), the implementation is highly flexible. It is case-insensitive and automatically strips any number of CAP_ prefixes. This makes it compatible with various input formats, such as those used in Kubernetes or standard Linux tooling.

    Supported Formats:

    • CAP_SYS_ADMIN (Standard)
    • SYS_ADMIN (Stripped prefix)
    • cap_sys_admin (Lowercase)
    • CAP_CAP_SYS_ADMIN (Multiple prefixes)
    // Example of flexible deserialization
    let cap: Capability = serde_json::from_str("\"syslog\"").unwrap();
    let cap: Capability = serde_json::from_str("\"CAP_SYSLOG\"").unwrap();
    let cap: Capability = serde_json::from_str("\"cap_SYSLOG\"").unwrap();
  5. Use the ImageIndex type for multi-platform OCI images

    main

    The ImageIndex struct is a high-level OCI manifest that points to specific image manifests, typically used to support multiple platforms (e.g., different architectures or operating systems) within a single index.

    Key Properties

    • schema_version: Required. Must be 2 for compatibility with older Docker versions. The constant SCHEMA_VERSION is provided for this purpose.
    • manifests: Required. A list of Descriptor objects pointing to specific platform manifests.
    • media_type: Optional. The media type of the document.
    • artifact_type: Optional. Used when the manifest is used for an artifact (must comply with RFC 6838).
    • subject: Optional. A descriptor of another manifest, used by the referrers API to indicate a relationship.
    • annotations: Optional. Arbitrary metadata following annotation rules.

    Construction

    ImageIndex supports the builder pattern via ImageIndexBuilder.

    use oci_spec::image::{ImageIndex, ImageIndexBuilder, MediaType, DescriptorBuilder};
    
    // Example of building an ImageIndex
    let index = ImageIndexBuilder::default()
        .schema_version(2)
        .manifests(vec![/* list of Descriptors */])
        .build()
        .expect("failed to build index");
  6. Manage OCI Image Layouts with OciLayout

    main

    The OciLayout struct represents the JSON object that serves as a marker for the base of an Open Container Image Layout. It provides the version of the image layout in use, which aligns with the OCI Image Specification version.

    Key properties:

    • image_layout_version: A required string specifying the layout version.

    You can use the OciLayoutBuilder to construct new instances and the provided methods to serialize/deserialize the layout to files, readers, writers, or strings.

    use oci_spec::image::OciLayout;
    
    let oci_layout = OciLayoutBuilder::default()
        .image_layout_version("1.0.0")
        .build()
        .unwrap();
  7. Represent an OCI Image Manifest with `ImageManifest`

    main

    An ImageManifest provides a configuration and a set of layers for a single container image for a specific architecture and operating system. It is distinct from an ImageIndex, which describes a set of images spanning multiple architectures.

    Key Properties

    • schema_version: REQUIRED. Must be 2 for backward compatibility.
    • config: REQUIRED. A Descriptor referencing the image configuration object by digest.
    • layers: REQUIRED. An array of Descriptor objects. The base layer must be at index 0, with subsequent layers following in stack order.
    • media_type: OPTIONAL. The media type of the document.
    • artifact_type: OPTIONAL. Used when the manifest is used for an artifact; must comply with RFC 6838.
    • subject: OPTIONAL. A Descriptor of another manifest, used by the referrers API to indicate a relationship.
    • annotations: OPTIONAL. A HashMap<String, String> containing arbitrary metadata.
    use oci_spec::image::ImageManifest;
    use oci_spec::image::DescriptorBuilder;
    
    // Example of building a manifest (conceptual structure)
    let manifest = ImageManifestBuilder::default()
        .schema_version(2)
        .config(config_descriptor)
        .layers(vec![layer_descriptor_1, layer_descriptor_2])
        .build()
        .expect("build image manifest");
  8. The `Spec` struct for OCI runtime configuration

    main

    The Spec struct is the root object representing the OCI Runtime Specification (typically found in a config.json file). It defines the entire configuration for a container, including its version, root filesystem, mounts, process details, hostname, hooks, and platform-specific configurations (Linux, Windows, Solaris, etc.).

    Key fields include:

    • version: A SemVer v2.0.0 string specifying the OCI Runtime Spec version.
    • root: Specifies the container's root filesystem.
    • mounts: Additional mounts beyond the root.
    • process: The container process configuration (required for start).
    • hostname / domainname: Network identity within the container.
    • hooks: Programs to run during container lifecycle events.
    • annotations: A key-value map for arbitrary metadata.
    • linux / windows / solaris / vm / zos: Platform-specific configuration objects.
    use oci_spec::runtime::Spec;
    
    let spec = Spec::default();
    // Access fields via getters provided by getset macro
    let version = spec.version();
    let hostname = spec.hostname();
    // Use setters to modify
    spec.set_hostname("new-host".to_string());
    // Use mut getters for mutable access
    spec.get_mut_annotations();
  9. Load an Image Manifest from a file

    main

    Use ImageManifest::from_file to load an existing OCI image manifest from the filesystem. This returns an ImageManifest instance which you can then inspect, for example, by checking the number of layers.

    use oci_spec::image::ImageManifest;
    
    let image_manifest = ImageManifest::from_file("manifest.json").unwrap();
    assert_eq!(image_manifest.layers().len(), 5);
  10. Create a list of repositories using RepositoryListBuilder

    main

    Use RepositoryListBuilder from the oci_spec::distribution module to construct a list of repository names as defined by the OCI Distribution Specification.

    use oci_spec::distribution::RepositoryListBuilder;
    
    let list = RepositoryListBuilder::default()
                .repositories(vec!["busybox".to_owned()])
                .build().unwrap();
  11. Create a new Image Manifest using builders

    main

    You can construct complex OCI Image Manifests using the ImageManifestBuilder and DescriptorBuilder. This involves defining a configuration descriptor and a list of layer descriptors, then assembling them into the manifest.

    Key components:

    • DescriptorBuilder: Used to create descriptors for the image config and individual layers (specifying media_type, size, and digest).
    • ImageManifestBuilder: Used to assemble the schema_version, the config descriptor, and the collection of layer descriptors.
    • MediaType: Provides standard OCI media types like ImageConfig and ImageLayerGzip.
    • Sha256Digest: Used to parse and hold SHA256 digests.
    • to_file_pretty: Saves the constructed manifest to a file in a human-readable JSON format.
    use std::str::FromStr;
    use oci_spec::image::{
        Descriptor, 
        DescriptorBuilder, 
        ImageManifest, 
        ImageManifestBuilder, 
        MediaType, 
        Sha256Digest,
        SCHEMA_VERSION
    };
    
    let config = DescriptorBuilder::default()
                .media_type(MediaType::ImageConfig)
                .size(7023u64)
                .digest(Sha256Digest::from_str("b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7").unwrap())
                .build()
                .expect("build config descriptor");
    
    let layers: Vec<Descriptor> = [
        (
            32654u64,
            "9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0",
        ),
        (16724, "3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"),
        (73109, "ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",),
    ]
        .iter()
        .map(|l| {
        DescriptorBuilder::default()
            .media_type(MediaType::ImageLayerGzip)
            .size(l.0)
            .digest(Sha256Digest::from_str(l.1).unwrap())
            .build()
            .expect("build layer")
        })
        .collect();
    
    let image_manifest = ImageManifestBuilder::default()
        .schema_version(SCHEMA_VERSION)
        .config(config)
        .layers(layers)
        .build()
        .expect("build image manifest");
    
    image_manifest.to_file_pretty("my-manifest.json").unwrap();