containerization-framework 0.4.0

Rust bindings for Apple's Containerization framework: Linux containers in lightweight VMs, in-process and without a daemon.
//===----------------------------------------------------------------------===//
// ContainerizationOCI's runtime spec: the seccomp profiles, `Bundle`, and the
// conversions Containerization's types make to it.
//
// A `Bundle` crosses as its path. Its only stored property is that URL, and
// its initializer from one is private.
//===----------------------------------------------------------------------===//

import Containerization
import ContainerizationOCI
import Foundation

func processFromImageConfig(config: RustImageConfig) -> CzOutcome {
  let config = ImageConfig(config)

  return CzOutcome { ContainerizationOCI.Process(from: config) }
}

func processDescription(process: RustProcess) -> CzOutcome {
  let process = ContainerizationOCI.Process(process)

  return CzOutcome { process.description }
}

func hookDescription(hook: RustHook) -> CzOutcome {
  let hook = Hook(hook)

  return CzOutcome { hook.description }
}

func decodeLinuxSeccomp(data: RustVec<UInt8>) -> CzOutcome {
  let data = Data(data.map { $0 })

  return CzOutcome { try LinuxSeccomp.decode(from: data) }
}

func defaultSeccompProfile(
  hasCapabilities: Bool,
  capabilities: RustOciLinuxCapabilities,
  arch: RustStr
) -> CzOutcome {
  let capabilities = hasCapabilities ? ContainerizationOCI.LinuxCapabilities(capabilities) : nil
  let arch: Arch = swiftCase(arch)

  return CzOutcome { LinuxSeccomp.defaultProfile(capabilities: capabilities, arch: arch) }
}

func currentArch() -> CzOutcome {
  CzOutcome { absent(Arch.current?.rawValue) }
}

func currentVerifiedArch() -> CzOutcome {
  CzOutcome { try Arch.currentVerified().rawValue }
}

func currentRuntimeSpecVersion() -> CzOutcome {
  CzOutcome { RuntimeSpecVersion.current }
}

func linuxRLimitToOCI(rlimit: RustLinuxRLimit) -> CzOutcome {
  let kind = rlimit.kind()
  let rlimit = LinuxRLimit(kind: rlimitKinds.first { $0.1 == kind }!.0, hard: rlimit.hard(), soft: rlimit.soft())

  return CzOutcome { rlimit.toOCI() }
}

func linuxCapabilitiesToOCI(capabilities: RustLinuxCapabilities) -> CzOutcome {
  CzOutcome { try Containerization.LinuxCapabilities(capabilities).toOCI() }
}

func systemPlatformOCIPlatform(platform: RustSystemPlatform) -> CzOutcome {
  CzOutcome { try SystemPlatform(platform).ociPlatform() }
}

// MARK: Bundle

extension ContainerizationOCI.Bundle {
  /// The bundle at `path`, unchecked, as Swift's private `init(path:)` would
  /// make it.
  init(bridged path: RustStr) {
    self = unsafeBitCast(URL(filePath: path.toString()), to: ContainerizationOCI.Bundle.self)
  }

  var bridgedPath: String {
    path.path(percentEncoded: false)
  }
}

func createBundle(path: RustStr, spec: RustSpec) -> CzOutcome {
  let path = URL(filePath: path.toString())
  let spec = Spec(spec)

  return CzOutcome { try ContainerizationOCI.Bundle.create(path: path, spec: spec).bridgedPath }
}

func createBundleFromData(path: RustStr, spec: RustVec<UInt8>) -> CzOutcome {
  let path = URL(filePath: path.toString())
  let spec = Data(spec.map { $0 })

  return CzOutcome { try ContainerizationOCI.Bundle.create(path: path, spec: spec).bridgedPath }
}

func loadBundle(path: RustStr) -> CzOutcome {
  let path = URL(filePath: path.toString())

  return CzOutcome { try ContainerizationOCI.Bundle.load(path: path).bridgedPath }
}

func bundleConfigPath(path: RustStr) -> CzOutcome {
  let bundle = ContainerizationOCI.Bundle(bridged: path)

  return CzOutcome { bundle.configPath.path(percentEncoded: false) }
}

func bundleRootfsPath(path: RustStr) -> CzOutcome {
  let bundle = ContainerizationOCI.Bundle(bridged: path)

  return CzOutcome { bundle.rootfsPath.path(percentEncoded: false) }
}

func deleteBundle(path: RustStr) -> CzOutcome {
  let bundle = ContainerizationOCI.Bundle(bridged: path)

  return CzOutcome { try bundle.delete() }
}

func bundleLoadConfig(path: RustStr) -> CzOutcome {
  let bundle = ContainerizationOCI.Bundle(bridged: path)

  return CzOutcome { try bundle.loadConfig() }
}

// MARK: For unit tests

/// The raw values of the Swift enum `name`, in the order of Rust's `ALL`.
func rawValues(name: RustStr) -> RustVec<RustString> {
  let name = name.toString()
  let rawValues: [String] =
    switch name {
    case "LinuxNamespaceType":
      [LinuxNamespaceType.pid, .network, .uts, .mount, .ipc, .user, .cgroup].map(\.rawValue)
    case "LinuxPersonalityDomain":
      [LinuxPersonalityDomain.perLinux, .perLinux32].map(\.rawValue)
    case "LinuxSeccompFlag":
      [LinuxSeccompFlag.flagLog, .flagSpecAllow, .flagWaitKillableRecv].map(\.rawValue)
    case "Arch":
      [
        Arch.archX86, .archX86_64, .archX32, .archARM, .archAARCH64, .archMIPS, .archMIPS64, .archMIPS64N32,
        .archMIPSEL, .archMIPSEL64, .archMIPSEL64N32, .archPPC, .archPPC64, .archPPC64LE, .archS390, .archS390X,
        .archPARISC, .archPARISC64, .archRISCV64,
      ].map(\.rawValue)
    case "LinuxSeccompAction":
      [
        LinuxSeccompAction.actKill, .actKillProcess, .actKillThread, .actTrap, .actErrno, .actTrace, .actAllow,
        .actLog, .actNotify,
      ].map(\.rawValue)
    case "LinuxSeccompOperator":
      [
        LinuxSeccompOperator.opNotEqual, .opLessThan, .opLessEqual, .opEqualTo, .opGreaterEqual, .opGreaterThan,
        .opMaskedEqual,
      ].map(\.rawValue)
    case "ContainerState":
      [ContainerState.creating, .created, .running, .stopped].map(\.rawValue)
    default:
      preconditionFailure("no enum named \(name)")
    }

  return rustStrings(rawValues)
}

func seccompFdName() -> String {
  ContainerizationOCI.seccompFdName
}

/// What the initializer of the Swift type `name` makes with no arguments.
func defaultValue(name: RustStr) -> CzOutcome {
  let name = name.toString()
  let value: Any =
    switch name {
    case "Spec": Spec()
    case "Process": ContainerizationOCI.Process()
    case "LinuxCapabilities": ContainerizationOCI.LinuxCapabilities()
    case "Linux": Linux()
    case "LinuxResources": LinuxResources()
    case "LinuxMemory": LinuxMemory()
    case "LinuxCPU": LinuxCPU()
    default: preconditionFailure("no type named \(name)")
    }

  return CzOutcome { value }
}