From 51ef9f81fef574bbd815d4f5560157297b0a4067 Mon Sep 17 00:00:00 2001 From: Eric Ernst Date: Fri, 19 Sep 2025 02:13:54 -0700 Subject: [PATCH] examples: add ctr-example (#295) Add very basic example of running a Linux container using Containerization Signed-off-by: Eric Ernst --- examples/ctr-example/Makefile | 41 ++++ examples/ctr-example/Package.resolved | 213 ++++++++++++++++++ examples/ctr-example/Package.swift | 45 ++++ examples/ctr-example/README.md | 3 + .../Sources/ctr-example/main.swift | 74 ++++++ examples/ctr-example/ctr-example.entitlements | 8 + 6 files changed, 384 insertions(+) create mode 100644 examples/ctr-example/Makefile create mode 100644 examples/ctr-example/Package.resolved create mode 100644 examples/ctr-example/Package.swift create mode 100644 examples/ctr-example/README.md create mode 100644 examples/ctr-example/Sources/ctr-example/main.swift create mode 100644 examples/ctr-example/ctr-example.entitlements diff --git a/examples/ctr-example/Makefile b/examples/ctr-example/Makefile new file mode 100644 index 00000000..2d6ad405 --- /dev/null +++ b/examples/ctr-example/Makefile @@ -0,0 +1,41 @@ +# Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ctr-example Makefile + +SWIFT = /usr/bin/swift + +.PHONY: all build clean run + +all: build + +build: + $(SWIFT) build --configuration release + codesign --force --sign - --entitlements ctr-example.entitlements ./.build/release/ctr-example + cp ./.build/release/ctr-example ./ctr-example + +clean: + $(SWIFT) package clean + rm -f ./ctr-example + +run: build + ./ctr-example + +# Development targets +debug: + $(SWIFT) build + codesign --force --sign - --entitlements ctr-example.entitlements ./.build/debug/ctr-example + +fmt: + $(SWIFT) format --in-place --recursive Sources/ diff --git a/examples/ctr-example/Package.resolved b/examples/ctr-example/Package.resolved new file mode 100644 index 00000000..9eb48df4 --- /dev/null +++ b/examples/ctr-example/Package.resolved @@ -0,0 +1,213 @@ +{ + "originHash" : "596c757bf0a22a54817d60de250dc349ae2da22ce1dfca2785b16a723baa0e6b", + "pins" : [ + { + "identity" : "async-http-client", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swift-server/async-http-client.git", + "state" : { + "revision" : "254d34096123cd5da4f82e69f52f622ac7526d68", + "version" : "1.27.0" + } + }, + { + "identity" : "containerization", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/containerization.git", + "state" : { + "revision" : "4099bdccd615bca49a2ea9cdb5b20dc5c5bd0fea", + "version" : "0.6.2" + } + }, + { + "identity" : "grpc-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/grpc/grpc-swift.git", + "state" : { + "revision" : "a56a157218877ef3e9625f7e1f7b2cb7e46ead1b", + "version" : "1.26.1" + } + }, + { + "identity" : "swift-algorithms", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-algorithms.git", + "state" : { + "revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023", + "version" : "1.2.1" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "309a47b2b1d9b5e991f36961c983ecec72275be3", + "version" : "1.6.1" + } + }, + { + "identity" : "swift-asn1", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-asn1.git", + "state" : { + "revision" : "f70225981241859eb4aa1a18a75531d26637c8cc", + "version" : "1.4.0" + } + }, + { + "identity" : "swift-async-algorithms", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-async-algorithms.git", + "state" : { + "revision" : "042e1c4d9d19748c9c228f8d4ebc97bb1e339b0b", + "version" : "1.0.4" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-certificates", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-certificates.git", + "state" : { + "revision" : "20c451f1ad8e344e61ddbb34ef196653d4b73ea6", + "version" : "1.13.0" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "8c0c0a8b49e080e54e5e328cc552821ff07cd341", + "version" : "1.2.1" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "d1c6b70f7c5f19fb0b8750cb8dcdf2ea6e2d8c34", + "version" : "3.15.0" + } + }, + { + "identity" : "swift-http-structured-headers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-http-structured-headers.git", + "state" : { + "revision" : "1625f271afb04375bf48737a5572613248d0e7a0", + "version" : "1.4.0" + } + }, + { + "identity" : "swift-http-types", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-http-types.git", + "state" : { + "revision" : "a0a57e949a8903563aba4615869310c0ebf14c03", + "version" : "1.4.0" + } + }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "ce592ae52f982c847a4efc0dd881cc9eb32d29f2", + "version" : "1.6.4" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio.git", + "state" : { + "revision" : "1c30f0f2053b654e3d1302492124aa6d242cdba7", + "version" : "2.86.0" + } + }, + { + "identity" : "swift-nio-extras", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-extras.git", + "state" : { + "revision" : "a55c3dd3a81d035af8a20ce5718889c0dcab073d", + "version" : "1.29.0" + } + }, + { + "identity" : "swift-nio-http2", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-http2.git", + "state" : { + "revision" : "5e9e99ec96c53bc2c18ddd10c1e25a3cd97c55e5", + "version" : "1.38.0" + } + }, + { + "identity" : "swift-nio-ssl", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-ssl.git", + "state" : { + "revision" : "737e550e607d82bf15bdfddf158ec61652ce836f", + "version" : "2.34.0" + } + }, + { + "identity" : "swift-nio-transport-services", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-transport-services.git", + "state" : { + "revision" : "e645014baea2ec1c2db564410c51a656cf47c923", + "version" : "1.25.1" + } + }, + { + "identity" : "swift-numerics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-numerics.git", + "state" : { + "revision" : "bbadd4b853a33fd78c4ae977d17bb2af15eb3f2a", + "version" : "1.1.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "e3f69fd321d0c9fcdc16fb576a0cdd956675face", + "version" : "1.31.0" + } + }, + { + "identity" : "swift-service-lifecycle", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swift-server/swift-service-lifecycle.git", + "state" : { + "revision" : "e7187309187695115033536e8fc9b2eb87fd956d", + "version" : "2.8.0" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "890830fff1a577dc83134890c7984020c5f6b43b", + "version" : "1.6.2" + } + } + ], + "version" : 3 +} diff --git a/examples/ctr-example/Package.swift b/examples/ctr-example/Package.swift new file mode 100644 index 00000000..a87828d4 --- /dev/null +++ b/examples/ctr-example/Package.swift @@ -0,0 +1,45 @@ +// swift-tools-version: 6.2 +//===----------------------------------------------------------------------===// +// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//===----------------------------------------------------------------------===// + +import PackageDescription + +let scVersion = "0.6.2" + +let package = Package( + name: "ctr-example", + platforms: [ + .macOS("26.0") + ], + products: [ + .executable( + name: "ctr-example", + targets: ["ctr-example"] + ) + ], + dependencies: [ + .package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion)) + ], + targets: [ + .executableTarget( + name: "ctr-example", + dependencies: [ + .product(name: "Containerization", package: "containerization"), + .product(name: "ContainerizationOS", package: "containerization"), + ] + ) + ] +) diff --git a/examples/ctr-example/README.md b/examples/ctr-example/README.md new file mode 100644 index 00000000..a142eeb7 --- /dev/null +++ b/examples/ctr-example/README.md @@ -0,0 +1,3 @@ +# + +Very basic example of launching a Linux container using Containerization. diff --git a/examples/ctr-example/Sources/ctr-example/main.swift b/examples/ctr-example/Sources/ctr-example/main.swift new file mode 100644 index 00000000..03c35ecd --- /dev/null +++ b/examples/ctr-example/Sources/ctr-example/main.swift @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//===----------------------------------------------------------------------===// + +import Containerization +import ContainerizationOS +import Foundation + +@main +struct CtrExample { + static func main() async throws { + print("Starting container example...") + + // Set up terminal in raw mode (like cctl) + let current = try Terminal.current + try current.setraw() + defer { current.tryReset() } + + // Create container manager with file-based initfs + var manager = try await ContainerManager( + kernel: Kernel(path: URL(fileURLWithPath: "./vmlinux"), platform: .linuxArm), + initfsReference: "vminit:latest", + network: try ContainerManager.VmnetNetwork() + ) + + let containerId = "ctr-example" + let imageReference = "docker.io/library/alpine:3.16" + + print("Creating container from \(imageReference)...") + + // Create container with simple configuration + let container = try await manager.create( + containerId, + reference: imageReference, + rootfsSizeInBytes: 1.gib() + ) { @Sendable config in + config.cpus = 2 + config.memoryInBytes = 512.mib() + config.process.setTerminalIO(terminal: current) + config.process.arguments = ["/bin/sh"] + config.process.workingDirectory = "/" + } + + // Clean up on exit + defer { + try? manager.delete(containerId) + } + + print("Starting container...") + try await container.create() + try await container.start() + + // Resize terminal to match current window + try? await container.resize(to: try current.size) + + // Wait for container to finish + let exitCode = try await container.wait() + + print("Container exited with code \(exitCode)") + try await container.stop() + } +} diff --git a/examples/ctr-example/ctr-example.entitlements b/examples/ctr-example/ctr-example.entitlements new file mode 100644 index 00000000..f7f5d7ce --- /dev/null +++ b/examples/ctr-example/ctr-example.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.virtualization + + + \ No newline at end of file