metropolis/vm: add kube/{generated,apis}

This is a first pass at the vm.metropolis.monogon.dev Kubnernetes API,
containing the VirtualMachine resource.

This also adds all the required BUILDfile boilerplate for the output
(generated) directory.

Test Plan: Builds should pass, eg. bazel build //metropolis/vm/kube/...

X-Origin-Diff: phab/D752
GitOrigin-RevId: d05c1a38e26b14c11b7a5d5885b15bbca8662174
diff --git a/metropolis/vm/kube/apis/vm/BUILD.bazel b/metropolis/vm/kube/apis/vm/BUILD.bazel
new file mode 100644
index 0000000..3b758f3
--- /dev/null
+++ b/metropolis/vm/kube/apis/vm/BUILD.bazel
@@ -0,0 +1,8 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["register.go"],
+    importpath = "source.monogon.dev/metropolis/vm/kube/apis/vm",
+    visibility = ["//visibility:public"],
+)
diff --git a/metropolis/vm/kube/apis/vm/register.go b/metropolis/vm/kube/apis/vm/register.go
new file mode 100644
index 0000000..b2fe5f6
--- /dev/null
+++ b/metropolis/vm/kube/apis/vm/register.go
@@ -0,0 +1,21 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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
+//
+//     http://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.
+
+package vm
+
+const (
+	GroupName = "vm.metropolis.monogon.dev"
+)
diff --git a/metropolis/vm/kube/apis/vm/v1alpha1/BUILD.bazel b/metropolis/vm/kube/apis/vm/v1alpha1/BUILD.bazel
new file mode 100644
index 0000000..4b4bfd4
--- /dev/null
+++ b/metropolis/vm/kube/apis/vm/v1alpha1/BUILD.bazel
@@ -0,0 +1,30 @@
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = [
+        "doc.go",
+        "register.go",
+        "types.go",
+    ],
+    embed = select({
+        "//metropolis/build/kube-code-generator:embed_deepcopy": [":go_kubernetes_library"],
+        "//conditions:default": [],
+    }),
+    importpath = "source.monogon.dev/metropolis/vm/kube/apis/vm/v1alpha1",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//metropolis/vm/kube/apis/vm:go_default_library",
+        "@io_k8s_api//core/v1:go_default_library",
+        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
+    ],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/apis/vm/v1alpha1",
+)
diff --git a/metropolis/vm/kube/apis/vm/v1alpha1/doc.go b/metropolis/vm/kube/apis/vm/v1alpha1/doc.go
new file mode 100644
index 0000000..f0d6d3d
--- /dev/null
+++ b/metropolis/vm/kube/apis/vm/v1alpha1/doc.go
@@ -0,0 +1,20 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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
+//
+//     http://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.
+
+// +k8s:deepcopy-gen=package
+// +groupName=vm.metropolis.monogon.dev
+
+package v1alpha1
diff --git a/metropolis/vm/kube/apis/vm/v1alpha1/register.go b/metropolis/vm/kube/apis/vm/v1alpha1/register.go
new file mode 100644
index 0000000..3111d54
--- /dev/null
+++ b/metropolis/vm/kube/apis/vm/v1alpha1/register.go
@@ -0,0 +1,55 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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
+//
+//     http://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.
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
+	"source.monogon.dev/metropolis/vm/kube/apis/vm"
+)
+
+// SchemeGroupVersion is group version used to register these objects
+var SchemeGroupVersion = schema.GroupVersion{Group: vm.GroupName, Version: "v1alpha1"}
+
+// Kind takes an unqualified kind and returns back a Group qualified GroupKind
+func Kind(kind string) schema.GroupKind {
+	return SchemeGroupVersion.WithKind(kind).GroupKind()
+}
+
+// Resource takes an unqualified resource and returns a Group qualified GroupResource
+func Resource(resource string) schema.GroupResource {
+	return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+
+var (
+	// SchemeBuilder initializes a scheme builder
+	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
+	// AddToScheme is a global function that registers this API group & version to a scheme
+	AddToScheme = SchemeBuilder.AddToScheme
+)
+
+// Adds the list of known types to Scheme.
+func addKnownTypes(scheme *runtime.Scheme) error {
+	scheme.AddKnownTypes(SchemeGroupVersion,
+		&VirtualMachine{},
+		&VirtualMachineList{},
+	)
+	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
+	return nil
+}
diff --git a/metropolis/vm/kube/apis/vm/v1alpha1/types.go b/metropolis/vm/kube/apis/vm/v1alpha1/types.go
new file mode 100644
index 0000000..365dda2
--- /dev/null
+++ b/metropolis/vm/kube/apis/vm/v1alpha1/types.go
@@ -0,0 +1,119 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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
+//
+//     http://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.
+
+package v1alpha1
+
+import (
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+type VirtualMachine struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   VirtualMachineSpec   `json:"spec"`
+	Status VirtualMachineStatus `json:"status"`
+}
+
+type VirtualMachineSpec struct {
+	// TODO(lorenz): document
+	InitialImage VirtualMachineImage `json:"initialImage"`
+
+	// HypervsisorImage determines what OCI image will run within the pod. This
+	// image must communicate using the Metropolis VM hypervisor API and run
+	// the actual VM monitor (eg. qemu).
+	// Defaults to "" (default image for cluster).
+	// +optional
+	HypervisorImage string
+
+	// Resources are the resources assigned to the pod backing this virtual
+	// machine when running. Non-integer CPU requests and overcommit will
+	// result in reduced side-channel attack resistance as CPUs will not be
+	// statically assigned
+	// +optional
+	Resources corev1.ResourceRequirements `json:"resources,omitempty"`
+
+	// VolumeClaimTemplate is used to produce a PersistentVolumeClaim that will
+	// be attached to the pod backing this virtual machine when running.
+	VolumeClaimTemplate corev1.PersistentVolumeClaim `json:"volumeClaimTemplate,omitempty"`
+
+	// VirtualMachineIPs is a list of IP addresses (in CIDR prefix notation)
+	// that the virtual machine will receive traffic for when running. These
+	// can be either IPv4 or IPv6 addresses.
+	VirtualMachineIPs []string `json:"vmIPs"`
+
+	// MigrateStrategy determines what migration strategy is used when the
+	// virtual machine needs to be migrated across hosts.
+	// Defaults to "Live".
+	// +optional
+	// +default="Live"
+	MigrateStrategy VirtualMachineMigrateStrategy `json:"migrateStrategy"`
+
+	// ExecutionMode determines whether the machine is running or paused.
+	// Defaults to "Run".
+	// +optional
+	// +default="Run"
+	ExecutionMode VirtualMachineExecutionMode `json:"mode"`
+}
+
+type VirtualMachineImage struct {
+	// TODO(lorenz): document
+	// +optional
+	URL string `json:"url"`
+}
+
+type VirtualMachineMigrateStrategy string
+
+const (
+	VirtualMachineColdMigrate VirtualMachineMigrateStrategy = "Cold"
+	VirtualMachineLiveMigrate VirtualMachineMigrateStrategy = "Live"
+)
+
+type VirtualMachineExecutionMode string
+
+const (
+	VirtualMachineRun   VirtualMachineExecutionMode = "Run"
+	VirtualMachinePause VirtualMachineExecutionMode = "Pause"
+)
+
+type VirtualMachineStatus struct {
+	Phase           VirtualMachinePhase `json:"phase"`
+	ActivePodName   string              `json:"activePodName"`
+	VolumeClaimName string              `json:"volumeClaimName"`
+}
+
+type VirtualMachinePhase string
+
+const (
+	VirtualMachinePhaseCreating    VirtualMachinePhase = "Creating"
+	VirtualMachinePhaseRunning     VirtualMachinePhase = "Running"
+	VirtualMachinePhaseTerminating VirtualMachinePhase = "Terminating"
+	VirtualMachinePhaseStopped     VirtualMachinePhase = "Stopped"
+	VirtualMachinePhaseLost        VirtualMachinePhase = "Lost"
+)
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+type VirtualMachineList struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Items []VirtualMachine `json:"items"`
+}
diff --git a/metropolis/vm/kube/generated/BUILD.bazel b/metropolis/vm/kube/generated/BUILD.bazel
new file mode 100644
index 0000000..5e30cdc
--- /dev/null
+++ b/metropolis/vm/kube/generated/BUILD.bazel
@@ -0,0 +1,20 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_path")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_resource_bundle")
+
+go_kubernetes_resource_bundle(
+    name = "bundle",
+    apipath = "source.monogon.dev/metropolis/vm/kube/apis",
+    apis = {
+        "vm/v1alpha1": ["virtualmachine"],
+    },
+    gopath = ":go_path",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated",
+    visibility = ["//metropolis/vm/kube:__subpackages__"],
+)
+
+go_path(
+    name = "go_path",
+    deps = [
+        "//metropolis/vm/kube/apis/vm/v1alpha1:go_default_library",
+    ],
+)
diff --git a/metropolis/vm/kube/generated/clientset/versioned/BUILD.bazel b/metropolis/vm/kube/generated/clientset/versioned/BUILD.bazel
new file mode 100644
index 0000000..6e21803
--- /dev/null
+++ b/metropolis/vm/kube/generated/clientset/versioned/BUILD.bazel
@@ -0,0 +1,21 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+
+# keep
+go_library(
+    name = "go_default_library",
+    embed = [":go_kubernetes_library"],
+    visibility = ["//metropolis/vm:__subpackages__"],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated/clientset/versioned",
+    deps = [
+        "//metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1:go_default_library",
+        "@io_k8s_client_go//discovery:go_default_library",
+        "@io_k8s_client_go//rest:go_default_library",
+        "@io_k8s_client_go//util/flowcontrol:go_default_library",
+    ],
+)
diff --git a/metropolis/vm/kube/generated/clientset/versioned/scheme/BUILD.bazel b/metropolis/vm/kube/generated/clientset/versioned/scheme/BUILD.bazel
new file mode 100644
index 0000000..49f7a99
--- /dev/null
+++ b/metropolis/vm/kube/generated/clientset/versioned/scheme/BUILD.bazel
@@ -0,0 +1,23 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+
+# keep
+go_library(
+    name = "go_default_library",
+    embed = [":go_kubernetes_library"],
+    visibility = ["//metropolis/vm:__subpackages__"],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated/clientset/versioned/scheme",
+    deps = [
+        "//metropolis/vm/kube/apis/vm/v1alpha1:go_default_library",
+        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime/serializer:go_default_library",
+        "@io_k8s_apimachinery//pkg/util/runtime:go_default_library",
+    ],
+)
diff --git a/metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1/BUILD.bazel b/metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1/BUILD.bazel
new file mode 100644
index 0000000..312009d
--- /dev/null
+++ b/metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1/BUILD.bazel
@@ -0,0 +1,23 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+
+# keep
+go_library(
+    name = "go_default_library",
+    embed = [":go_kubernetes_library"],
+    visibility = ["//metropolis/vm:__subpackages__"],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1",
+    deps = [
+        "//metropolis/vm/kube/apis/vm/v1alpha1:go_default_library",
+        "//metropolis/vm/kube/generated/clientset/versioned/scheme:go_default_library",
+        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
+        "@io_k8s_apimachinery//pkg/types:go_default_library",
+        "@io_k8s_apimachinery//pkg/watch:go_default_library",
+        "@io_k8s_client_go//rest:go_default_library",
+    ],
+)
diff --git a/metropolis/vm/kube/generated/informers/externalversions/BUILD.bazel b/metropolis/vm/kube/generated/informers/externalversions/BUILD.bazel
new file mode 100644
index 0000000..290b40a
--- /dev/null
+++ b/metropolis/vm/kube/generated/informers/externalversions/BUILD.bazel
@@ -0,0 +1,25 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+
+# keep
+go_library(
+    name = "go_default_library",
+    embed = [":go_kubernetes_library"],
+    visibility = ["//metropolis/vm:__subpackages__"],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated/informers/externalversions",
+    deps = [
+        "//metropolis/vm/kube/apis/vm/v1alpha1:go_default_library",
+        "//metropolis/vm/kube/generated/clientset/versioned:go_default_library",
+        "//metropolis/vm/kube/generated/informers/externalversions/internalinterfaces:go_default_library",
+        "//metropolis/vm/kube/generated/informers/externalversions/vm:go_default_library",
+        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
+        "@io_k8s_client_go//tools/cache:go_default_library",
+    ],
+)
diff --git a/metropolis/vm/kube/generated/informers/externalversions/internalinterfaces/BUILD.bazel b/metropolis/vm/kube/generated/informers/externalversions/internalinterfaces/BUILD.bazel
new file mode 100644
index 0000000..da30152
--- /dev/null
+++ b/metropolis/vm/kube/generated/informers/externalversions/internalinterfaces/BUILD.bazel
@@ -0,0 +1,21 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+
+# keep
+go_library(
+    name = "go_default_library",
+    embed = [":go_kubernetes_library"],
+    visibility = ["//metropolis/vm:__subpackages__"],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated/informers/externalversions/internalinterfaces",
+    deps = [
+        "//metropolis/vm/kube/generated/clientset/versioned:go_default_library",
+        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime:go_default_library",
+        "@io_k8s_client_go//tools/cache:go_default_library",
+    ],
+)
diff --git a/metropolis/vm/kube/generated/informers/externalversions/vm/BUILD.bazel b/metropolis/vm/kube/generated/informers/externalversions/vm/BUILD.bazel
new file mode 100644
index 0000000..c336d96
--- /dev/null
+++ b/metropolis/vm/kube/generated/informers/externalversions/vm/BUILD.bazel
@@ -0,0 +1,19 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+
+# keep
+go_library(
+    name = "go_default_library",
+    embed = [":go_kubernetes_library"],
+    visibility = ["//metropolis/vm:__subpackages__"],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated/informers/externalversions/vm",
+    deps = [
+        "//metropolis/vm/kube/generated/informers/externalversions/internalinterfaces:go_default_library",
+        "//metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1:go_default_library",
+    ],
+)
diff --git a/metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1/BUILD.bazel b/metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1/BUILD.bazel
new file mode 100644
index 0000000..a4c5f12
--- /dev/null
+++ b/metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1/BUILD.bazel
@@ -0,0 +1,25 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+
+# keep
+go_library(
+    name = "go_default_library",
+    embed = [":go_kubernetes_library"],
+    visibility = ["//metropolis/vm:__subpackages__"],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1",
+    deps = [
+        "//metropolis/vm/kube/apis/vm/v1alpha1:go_default_library",
+        "//metropolis/vm/kube/generated/clientset/versioned:go_default_library",
+        "//metropolis/vm/kube/generated/informers/externalversions/internalinterfaces:go_default_library",
+        "//metropolis/vm/kube/generated/listers/vm/v1alpha1:go_default_library",
+        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime:go_default_library",
+        "@io_k8s_apimachinery//pkg/watch:go_default_library",
+        "@io_k8s_client_go//tools/cache:go_default_library",
+    ],
+)
diff --git a/metropolis/vm/kube/generated/listers/vm/v1alpha1/BUILD.bazel b/metropolis/vm/kube/generated/listers/vm/v1alpha1/BUILD.bazel
new file mode 100644
index 0000000..8b60875
--- /dev/null
+++ b/metropolis/vm/kube/generated/listers/vm/v1alpha1/BUILD.bazel
@@ -0,0 +1,21 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
+
+# keep
+go_library(
+    name = "go_default_library",
+    embed = [":go_kubernetes_library"],
+    visibility = ["//metropolis/vm:__subpackages__"],
+)
+
+go_kubernetes_library(
+    name = "go_kubernetes_library",
+    bundle = "//metropolis/vm/kube/generated:bundle",
+    importpath = "source.monogon.dev/metropolis/vm/kube/generated/listers/vm/v1alpha1",
+    deps = [
+        "//metropolis/vm/kube/apis/vm/v1alpha1:go_default_library",
+        "@io_k8s_apimachinery//pkg/api/errors:go_default_library",
+        "@io_k8s_apimachinery//pkg/labels:go_default_library",
+        "@io_k8s_client_go//tools/cache:go_default_library",
+    ],
+)