Kubernetes 1.32.0 Swift Package Manager CI Status

目录

概述

SwiftkubeModel 是一个零依赖的 Swift 包,用于 Kubernetes API 对象。

兼容性矩阵

1.25.9 1.26.4 1.28.0 1.28.3 1.29.6 1.32.0 1.32.0
0.9.x - - - - - -
0.10.x - - - - - -
0.11.x - - - - - -
0.12.x - - - - - -
0.13.x - - - - -
0.14.x - - - - - -
0.15.x - - - - -
0.16.x - - - - -

用法

要使用 Kubernetes 对象,只需导入 SwiftkubeModel

import SwiftkubeModel

let metadata = meta.v1.ObjectMatadata(name: "swiftkube")
let pod = core.v1.Pod(metadata: metadata)

所有对象都根据其 API 组和版本进行命名空间划分,例如 apps.v1.Deploymentnetworking.v1beta1.Ingress。这意味着,例如 rbac.v1.Rolerbac.v1beta1.Role 是完全不同的对象。

示例

任何 Kubernetes 对象都可以使用模型结构体直接构建。这是一个 Deployment 清单的示例

let deployment = apps.v1.Deployment(
    metadata: meta.v1.ObjectMeta(
        name: "nginx"
    ),
    spec: apps.v1.DeploymentSpec(
        replicas: 1,
        selector: meta.v1.LabelSelector(
            matchLabels: ["app": "nginx"]
        ),
        template: core.v1.PodTemplateSpec (
            spec: core.v1.PodSpec(
                containers: [
                    core.v1.Container(
                        image: "nginx",
                        name: "nginx",
                    )
                ]
            )
        )
    )
)

这是一个 ConfigMap

let configMap = core.v1.ConfigMap(
    metadata: meta.v1.ObjectMeta(
        name: "config"
    ),
    data: [
        "env": "dev",
        "log_leve": "debug"
    ]
)

一个更完整的 Deployment 示例,它定义了 ProbesResourceRequirementsVolumesVolumeMounts,看起来像这样

let deployment = apps.v1.Deployment(
    metadata: meta.v1.ObjectMeta(
        name: "opa",
        namespace: "default"
    ),
    spec: apps.v1.DeploymentSpec(
        replicas: 2,
        selector: meta.v1.LabelSelector(
            matchLabels: ["app": "opa"]
        ),
        template: core.v1.PodTemplateSpec (
            spec: core.v1.PodSpec(
                containers: [
                    core.v1.Container(
                        image: "openpolicyagent/opa",
                        name: "opa",
                        readinessProbe: core.v1.Probe(
                            failureThreshold: 1,
                            httpGet: core.v1.HTTPGetAction(
                                path: "/health",
                                port: 8080
                            ),
                            initialDelaySeconds: 10,
                            periodSeconds: 20,
                            successThreshold: 2,
                            timeoutSeconds: 5
                        ),
                        resources: core.v1.ResourceRequirements(
                            limits: [
                                "ram": "512MB"
                            ],
                            requests: [
                                "ram": "128MB",
                                "cpu": "200m",
                            ]
                        ),
                        volumeMounts: [
                            core.v1.VolumeMount(
                                mountPath: "/etc/test",
                                name: "data"
                            )
                        ]
                    )
                ],
                imagePullSecrets: [
                    core.v1.LocalObjectReference(name: "secret-name")
                ],
                volumes: [
                    core.v1.Volume(
                        name: "data",
                        persistentVolumeClaim: core.v1.PersistentVolumeClaimVolumeSource(
                            claimName: "pvc",
                            readOnly: true
                        )
                    )
                ]
            )
        )
    )
)

Sendable 类型

所有资源都是 Sendable 结构体。

但是,当处理具有 JSONObject 字段的资源时,有一个需要注意的地方

或者当使用 UnstructuredResource 时。

它们将其属性存储为 Dictionary<String, any Sendable>。因此,字典字面量必须显式转换为 [String: any Sendable]

例如

UnstructuredResource(properties: [
    "apiVersion": "v1",
    "kind": "ConfigMap",
    "metadata": meta.v1.ObjectMeta(name: "configs", namespace: "default"),
    "data": [
        "foo": 42,
	    "bar": "baz"
    ] as [String: any Sendable]
])

构建器

从上面的例子可以清楚地看出,为了编写完整的清单,需要对所有子类型及其 API 组有一定的了解。此外,Swift 不允许任意的参数顺序。

为此,SwiftkubeModel 提供了简单的基于闭包的构建器函数,以方便使用。所有这些函数都位于 sk 命名空间下。

⚠️语法尚未最终确定,并且在 v1.0.0 发布之前可能会多次更改。这也可以用 Function/Result Builders 替换,目前正在开发中 (WIP)。

⚠️ SwiftkubeModel 目前仅为最常见的 Kubernetes 对象提供便捷的构建器。

上面的例子看起来像这样

let deployment = sk.deployment(name: "opa") {
    $0.metadata = sk.metadata {
        $0.namespace = "default"
    }
    $0.spec = sk.deploymentSpec {
        $0.replicas = 1
        $0.selector = sk.match(labels: ["app": "nginx"])
        $0.template = sk.podTemplate {
            $0.spec = sk.podSpec {
                $0.containers = [
                    sk.container(name: "opa") {
                        $0.image = "openpolicyagent/opa"
                        $0.readinessProbe = sk.probe(action: .httpGet(path: "/health", port: 8080)) {
                            $0.failureThreshold = 1
                            $0.initialDelaySeconds = 10
                            $0.periodSeconds = 20
                            $0.successThreshold = 2
                            $0.failureThreshold = 5
                        }
                        $0.resources = sk.requirements {
                            $0.requests = [
                                "ram": "512MB"
                            ]
                            $0.limits = [
                                "ram": "128MB",
                                "cpu": "200m",
                            ]
                        }
                        $0.volumeMounts = [
                            sk.volumeMount(name: "data", mountPath: "/etc/test")
                        ]
                    }
                ]
                $0.imagePullSecrets = [
                    sk.localObjectReference(name: "secret-name")
                ]
                $0.volumes = [
                    sk.volume(name: "data", from: .persistentVolumeClaim(claimName: "pvc", readOnly: true))
                ]
            }
        }
    }
}

扩展

除了基于闭包的构建器之外,SwiftkubeModel 还使用一些便捷函数扩展了 Model 对象,灵感来自 cdk8s

core.v1.ConfigMap

let configMap: core.v1.ConfigMap = sk.configMap(name: "test")

// populate the config map
configMap.add(data: "stuff", forKey: "foo")
configMap.add(binaryData: <binary>, forKey: "foo")
configMap.add(file: URL(fileURLWithPath: "/some/path"), forKey: "foo")
configMap.add(binaryFile: URL(fileURLWithPath: "/some/path"), forKey: "foo")

core.v1.Container

let container: core.v1.Container = ...
let volume: core.v1.Volume = ...

// mount a volume in a container
container.mount(volume: volume, on: "/data")
container.mount(volume: "dataVolume", on: "/data")

core.v1.Namespace

let namespace: core.v1.Namespace = ...

// add/remove finalizers
namespace.add(finalizer: "foo")
namespace.remove(finalizer: "foo")

core.v1.Secret

let secret: core.v1.Secret = sk.secret(name: "test")

// populate the secret
configMap.add(data: "stuff", forKey: "foo")
configMap.add(file: URL(fileURLWithPath: "/some/path"), forKey: "foo")

core.v1.Service

let service: core.v1.Service = ...

// add a service port entry
service.serve(port: 8080, targetPort: 80)

core.v1.ServiceAccount

let serviceAccount: core.v1.ServiceAccount = ...

// add an object reference for a secret
serviceAccount.use(imagePullSecret: "pullSecret")
serviceAccount.use(secret: "secret", namespace: "ns")

apps.v1.Deployment

let deployment: apps.v1.Deployment = ...

// expose a deployment instance to create a service
let service = deployment.expose(on: 8080, type: .clusterIP)

类型擦除

通常,在使用 Kubernetes 时,资源的具体类型是未知的或不相关的,例如,当从 YAML 清单文件创建资源时。在其他情况下,必须在运行时根据其字符串表示形式推导出资源的类型或种类。

SwiftkubeModel 提供了类型擦除的资源实现 UnstructuredResource 及其对应的 List-Type UnstructuredResourceList,以解决这些用例。

UnstruturedResource 允许通用地操作未注册 KubernetesAPIResource 的对象。这可以用于处理来自插件或 CRD 的 API 对象。

以下是一些示例,以阐明其用途

// Given a JSON string, e.g. at runtime, containing some Kubernetes resource
let json = """
  {
    "apiVersion": "stable.example.com/v1",
    "kind": "CronTab",
    "metadata": {
      "name": "my-new-cron-object",
      "namespace": "default"
    },
     "spec": {
       "cronSpec": "* * * * */5",
       "image": "my-awesome-cron-image"
     }
  }
"""

// We can still decode it without knowing the concrete type
let data = str.data(using: .utf8)!
let resource = try? JSONDecoder().decode(UnstructuredResource.self, from: data)

// When encoding the previous instance, it serializes the underlying resource
let encoded = try? JSONEncoder().encode(resource)

UnstruturedResource 公开其内部字典表示形式,并提供动态下标支持

let json = """
  {
    "apiVersion": "stable.example.com/v1",
    "kind": "CronTab",
    "metadata": {
      "name": "my-new-cron-object",
      "namespace": "default"
    },
     "spec": {
       "cronSpec": "* * * * */5",
       "image": "my-awesome-cron-image"
     }
  }
"""

let data = str.data(using: .utf8)!
let cron = try? JSONDecoder().decode(UnstructuredResource.self, from: data)

// Shortcut vars
print(cron.apiVersion)
print(cron.kind)
print(cron.metadata)

// The internal Dictionary<String: Any> representation
print(cron.properties)

// Dynamic member lookup
let spec: [String: Any]? = cron.spec
print(spec?["cronSpec"])

安装

要在 SwiftPM 项目中使用 SwiftkubeModel,请将以下行添加到 Package.swift 文件中的依赖项中

.package(name: "SwiftkubeModel", url: "https://github.com/swiftkube/model.git", from: "0.16.0")

然后将其作为依赖项包含在您的目标中

import PackageDescription

let package = Package(
    // ...
    dependencies: [
        .package(name: "SwiftkubeModel", url: "https://github.com/swiftkube/model.git", from: "0.16.0")
    ],
    targets: [
        .target(name: "<your-target>", dependencies: [
            .product(name: "SwiftkubeModel", package: "SwiftkubeModel"),
        ])
    ]
)

然后运行 swift build

许可证

Swiftkube 项目根据 Apache License 2.0 版本获得许可。有关更多详细信息,请参阅 LICENSE