From ec1c3295787e881027f260eea25637a3af520bf5 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Sun, 9 Jul 2023 23:45:53 +0800 Subject: [PATCH 1/7] add a new crd iot.device/deviceservice/deviceprofile and incorporate it Signed-off-by: wangxye <1031989637@qq.com> --- ...nition_deviceprofiles.iot.openyurt.io.yaml | 172 + ...rcedefinition_devices.iot.openyurt.io.yaml | 195 + ...nition_deviceservices.iot.openyurt.io.yaml | 141 + ...nition_platformadmins.iot.openyurt.io.yaml | 8493 +++++++++++++++++ .../yurt-manager-auto-generated.yaml | 78 + cmd/yurt-iot-carrier/app/core.go | 190 + cmd/yurt-iot-carrier/app/options/options.go | 82 + cmd/yurt-iot-carrier/yurt-iot-carrier.go | 43 + go.mod | 14 +- go.sum | 31 + .../build/Dockerfile.yurt-iot-carrier | 8 + hack/make-rules/image_build.sh | 1 + pkg/apis/iot/v1alpha1/device_types.go | 185 + pkg/apis/iot/v1alpha1/deviceprofile_types.go | 120 + pkg/apis/iot/v1alpha1/deviceservice_types.go | 129 + .../iot/v1alpha1/zz_generated.deepcopy.go | 506 + .../platformadmin/platformadmin_controller.go | 116 + pkg/controller/platformadmin/utils/version.go | 29 + .../clients/edgex-foundry/device_client.go | 371 + .../edgex-foundry/device_client_test.go | 203 + .../edgex-foundry/deviceprofile_client.go | 142 + .../deviceprofile_client_test.go | 107 + .../edgex-foundry/deviceservice_client.go | 167 + .../deviceservice_client_test.go | 126 + .../clients/edgex-foundry/edgexobject.go | 21 + .../clients/edgex-foundry/util.go | 456 + pkg/yurtiotcarrier/clients/errors.go | 29 + pkg/yurtiotcarrier/clients/interface.go | 86 + .../controllers/device_controller.go | 296 + .../controllers/device_controller_test.go | 78 + .../controllers/device_syncer.go | 239 + .../controllers/deviceprofile_controller.go | 165 + .../deviceprofile_controller_test.go | 103 + .../controllers/deviceprofile_syncer.go | 214 + .../controllers/deviceservice_controller.go | 220 + .../deviceservice_controller_test.go | 75 + .../controllers/deviceservice_syncer.go | 237 + pkg/yurtiotcarrier/controllers/predicate.go | 47 + .../controllers/util/conditions.go | 120 + .../controllers/util/fieldindexer.go | 62 + pkg/yurtiotcarrier/controllers/util/string.go | 30 + .../controllers/util/string_test.go | 70 + pkg/yurtiotcarrier/controllers/util/tools.go | 99 + .../controllers/util/tools_test.go | 54 + .../controllers/well_known_labels.go | 21 + 45 files changed, 14370 insertions(+), 1 deletion(-) create mode 100644 charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceprofiles.iot.openyurt.io.yaml create mode 100644 charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_devices.iot.openyurt.io.yaml create mode 100644 charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceservices.iot.openyurt.io.yaml create mode 100644 charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml create mode 100644 cmd/yurt-iot-carrier/app/core.go create mode 100644 cmd/yurt-iot-carrier/app/options/options.go create mode 100644 cmd/yurt-iot-carrier/yurt-iot-carrier.go create mode 100644 hack/dockerfiles/build/Dockerfile.yurt-iot-carrier create mode 100644 pkg/apis/iot/v1alpha1/device_types.go create mode 100644 pkg/apis/iot/v1alpha1/deviceprofile_types.go create mode 100644 pkg/apis/iot/v1alpha1/deviceservice_types.go create mode 100644 pkg/controller/platformadmin/utils/version.go create mode 100644 pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go create mode 100644 pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go create mode 100644 pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go create mode 100644 pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go create mode 100644 pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go create mode 100644 pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go create mode 100644 pkg/yurtiotcarrier/clients/edgex-foundry/edgexobject.go create mode 100644 pkg/yurtiotcarrier/clients/edgex-foundry/util.go create mode 100644 pkg/yurtiotcarrier/clients/errors.go create mode 100644 pkg/yurtiotcarrier/clients/interface.go create mode 100644 pkg/yurtiotcarrier/controllers/device_controller.go create mode 100644 pkg/yurtiotcarrier/controllers/device_controller_test.go create mode 100644 pkg/yurtiotcarrier/controllers/device_syncer.go create mode 100644 pkg/yurtiotcarrier/controllers/deviceprofile_controller.go create mode 100644 pkg/yurtiotcarrier/controllers/deviceprofile_controller_test.go create mode 100644 pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go create mode 100644 pkg/yurtiotcarrier/controllers/deviceservice_controller.go create mode 100644 pkg/yurtiotcarrier/controllers/deviceservice_controller_test.go create mode 100644 pkg/yurtiotcarrier/controllers/deviceservice_syncer.go create mode 100644 pkg/yurtiotcarrier/controllers/predicate.go create mode 100644 pkg/yurtiotcarrier/controllers/util/conditions.go create mode 100644 pkg/yurtiotcarrier/controllers/util/fieldindexer.go create mode 100644 pkg/yurtiotcarrier/controllers/util/string.go create mode 100644 pkg/yurtiotcarrier/controllers/util/string_test.go create mode 100644 pkg/yurtiotcarrier/controllers/util/tools.go create mode 100644 pkg/yurtiotcarrier/controllers/util/tools_test.go create mode 100644 pkg/yurtiotcarrier/controllers/well_known_labels.go diff --git a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceprofiles.iot.openyurt.io.yaml b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceprofiles.iot.openyurt.io.yaml new file mode 100644 index 00000000000..c1d92ed7a8e --- /dev/null +++ b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceprofiles.iot.openyurt.io.yaml @@ -0,0 +1,172 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: deviceprofiles.iot.openyurt.io +spec: + group: iot.openyurt.io + names: + kind: DeviceProfile + listKind: DeviceProfileList + plural: deviceprofiles + shortNames: + - dp + singular: deviceprofile + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The nodepool of deviceProfile + jsonPath: .spec.nodePool + name: NODEPOOL + type: string + - description: The synced status of deviceProfile + jsonPath: .status.synced + name: SYNCED + type: boolean + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: DeviceProfile represents the attributes and operational capabilities + of a device. It is a template for which there can be multiple matching devices + within a given system. NOTE This struct is derived from edgex/go-mod-core-contracts/models/deviceprofile.go + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DeviceProfileSpec defines the desired state of DeviceProfile + properties: + description: + type: string + deviceCommands: + items: + properties: + isHidden: + type: boolean + name: + type: string + readWrite: + type: string + resourceOperations: + items: + properties: + defaultValue: + type: string + deviceResource: + type: string + mappings: + additionalProperties: + type: string + type: object + required: + - defaultValue + type: object + type: array + required: + - isHidden + - name + - readWrite + - resourceOperations + type: object + type: array + deviceResources: + items: + properties: + attributes: + additionalProperties: + type: string + type: object + description: + type: string + isHidden: + type: boolean + name: + type: string + properties: + properties: + assertion: + type: string + base: + type: string + defaultValue: + type: string + mask: + type: string + maximum: + type: string + mediaType: + type: string + minimum: + type: string + offset: + type: string + readWrite: + type: string + scale: + type: string + shift: + type: string + units: + type: string + valueType: + type: string + type: object + tag: + type: string + required: + - description + - isHidden + - name + - properties + type: object + type: array + labels: + description: Labels used to search for groups of profiles on EdgeX + Foundry + items: + type: string + type: array + manufacturer: + description: Manufacturer of the device + type: string + model: + description: Model of the device + type: string + nodePool: + description: NodePool specifies which nodePool the deviceProfile belongs + to + type: string + type: object + status: + description: DeviceProfileStatus defines the observed state of DeviceProfile + properties: + id: + type: string + synced: + type: boolean + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_devices.iot.openyurt.io.yaml b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_devices.iot.openyurt.io.yaml new file mode 100644 index 00000000000..a284a0f5b8f --- /dev/null +++ b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_devices.iot.openyurt.io.yaml @@ -0,0 +1,195 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: devices.iot.openyurt.io +spec: + group: iot.openyurt.io + names: + kind: Device + listKind: DeviceList + plural: devices + shortNames: + - dev + singular: device + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The nodepool of device + jsonPath: .spec.nodePool + name: NODEPOOL + type: string + - description: The synced status of device + jsonPath: .status.synced + name: SYNCED + type: boolean + - description: The managed status of device + jsonPath: .spec.managed + name: MANAGED + priority: 1 + type: boolean + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Device is the Schema for the devices API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DeviceSpec defines the desired state of Device + properties: + adminState: + description: Admin state (locked/unlocked) + type: string + description: + description: Information describing the device + type: string + deviceProperties: + additionalProperties: + properties: + desiredValue: + type: string + name: + type: string + putURL: + type: string + required: + - desiredValue + - name + type: object + description: TODO support the following field A list of auto-generated + events coming from the device AutoEvents []AutoEvent `json:"autoEvents"` + DeviceProperties represents the expected state of the device's properties + type: object + labels: + description: Other labels applied to the device to help with searching + items: + type: string + type: array + location: + description: 'Device service specific location (interface{} is an + empty interface so it can be anything) TODO: location type in edgex + is interface{}' + type: string + managed: + description: True means device is managed by cloud, cloud can update + the related fields False means cloud can't update the fields + type: boolean + nodePool: + description: NodePool indicates which nodePool the device comes from + type: string + notify: + type: boolean + operatingState: + description: Operating state (enabled/disabled) + type: string + profileName: + description: Associated Device Profile - Describes the device + type: string + protocols: + additionalProperties: + additionalProperties: + type: string + type: object + description: A map of supported protocols for the given device + type: object + serviceName: + description: Associated Device Service - One per device + type: string + required: + - notify + - profileName + - serviceName + type: object + status: + description: DeviceStatus defines the observed state of Device + properties: + adminState: + description: Admin state (locked/unlocked) + type: string + conditions: + description: current device state + items: + description: DeviceCondition describes current state of a Device. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of in place set condition. + type: string + type: object + type: array + deviceProperties: + additionalProperties: + properties: + actualValue: + type: string + getURL: + type: string + name: + type: string + required: + - actualValue + - name + type: object + description: it represents the actual state of the device's properties + type: object + edgeId: + type: string + lastConnected: + description: Time (milliseconds) that the device last provided any + feedback or responded to any request + format: int64 + type: integer + lastReported: + description: Time (milliseconds) that the device reported data to + the core microservice + format: int64 + type: integer + operatingState: + description: Operating state (up/down/unknown) + type: string + synced: + description: Synced indicates whether the device already exists on + both OpenYurt and edge platform + type: boolean + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceservices.iot.openyurt.io.yaml b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceservices.iot.openyurt.io.yaml new file mode 100644 index 00000000000..d4722a55546 --- /dev/null +++ b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceservices.iot.openyurt.io.yaml @@ -0,0 +1,141 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: deviceservices.iot.openyurt.io +spec: + group: iot.openyurt.io + names: + kind: DeviceService + listKind: DeviceServiceList + plural: deviceservices + shortNames: + - dsvc + singular: deviceservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The nodepool of deviceService + jsonPath: .spec.nodePool + name: NODEPOOL + type: string + - description: The synced status of deviceService + jsonPath: .status.synced + name: SYNCED + type: boolean + - description: The managed status of deviceService + jsonPath: .spec.managed + name: MANAGED + priority: 1 + type: boolean + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: DeviceService is the Schema for the deviceservices API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DeviceServiceSpec defines the desired state of DeviceService + properties: + adminState: + description: Device Service Admin State + type: string + baseAddress: + type: string + description: + description: Information describing the device + type: string + labels: + description: tags or other labels applied to the device service for + search or other identification needs on the EdgeX Foundry + items: + type: string + type: array + managed: + description: True means deviceService is managed by cloud, cloud can + update the related fields False means cloud can't update the fields + type: boolean + nodePool: + description: NodePool indicates which nodePool the deviceService comes + from + type: string + required: + - baseAddress + type: object + status: + description: DeviceServiceStatus defines the observed state of DeviceService + properties: + adminState: + description: Device Service Admin State + type: string + conditions: + description: current deviceService state + items: + description: DeviceServiceCondition describes current state of a + Device. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of in place set condition. + type: string + type: object + type: array + edgeId: + description: the Id assigned by the edge platform + type: string + lastConnected: + description: time in milliseconds that the device last reported data + to the core + format: int64 + type: integer + lastReported: + description: time in milliseconds that the device last reported data + to the core + format: int64 + type: integer + synced: + description: Synced indicates whether the device already exists on + both OpenYurt and edge platform + type: boolean + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml new file mode 100644 index 00000000000..9b71d04091b --- /dev/null +++ b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml @@ -0,0 +1,8493 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: platformadmins.iot.openyurt.io +spec: + group: iot.openyurt.io + names: + categories: + - all + kind: PlatformAdmin + listKind: PlatformAdminList + plural: platformadmins + shortNames: + - pa + singular: platformadmin + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The platform ready status + jsonPath: .status.ready + name: READY + type: boolean + - description: The Service Replica. + jsonPath: .status.serviceReplicas + name: Service + type: integer + - description: The Ready Service Replica. + jsonPath: .status.serviceReadyReplicas + name: ReadyService + type: integer + - description: The Deployment Replica. + jsonPath: .status.deploymentReplicas + name: Deployment + type: integer + - description: The Ready Deployment Replica. + jsonPath: .status.deploymentReadyReplicas + name: ReadyDeployment + type: integer + deprecated: true + deprecationWarning: iot.openyurt.io/v1alpha1 PlatformAdmin will be deprecated + in future; use iot.openyurt.io/v1alpha2 PlatformAdmin; v1alpha1 PlatformAdmin.Spec.ServiceType + only support ClusterIP + name: v1alpha1 + schema: + openAPIV3Schema: + description: PlatformAdmin is the Schema for the samples API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PlatformAdminSpec defines the desired state of PlatformAdmin + properties: + additionalDeployments: + items: + description: DeploymentTemplateSpec defines the pool template of + Deployment. + properties: + metadata: + type: object + spec: + description: DeploymentSpec is the specification of the desired + behavior of the Deployment. + properties: + minReadySeconds: + description: Minimum number of seconds for which a newly + created pod should be ready without any of its container + crashing, for it to be considered available. Defaults + to 0 (pod will be considered available as soon as it is + ready) + format: int32 + type: integer + paused: + description: Indicates that the deployment is paused. + type: boolean + progressDeadlineSeconds: + description: The maximum time in seconds for a deployment + to make progress before it is considered to be failed. + The deployment controller will continue to process failed + deployments and a condition with a ProgressDeadlineExceeded + reason will be surfaced in the deployment status. Note + that progress will not be estimated during the time a + deployment is paused. Defaults to 600s. + format: int32 + type: integer + replicas: + description: Number of desired pods. This is a pointer to + distinguish between explicit zero and not specified. Defaults + to 1. + format: int32 + type: integer + revisionHistoryLimit: + description: The number of old ReplicaSets to retain to + allow rollback. This is a pointer to distinguish between + explicit zero and not specified. Defaults to 10. + format: int32 + type: integer + selector: + description: Label selector for pods. Existing ReplicaSets + whose pods are selected by this will be the ones affected + by this deployment. It must match the pod template's labels. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + strategy: + description: The deployment strategy to use to replace existing + pods with new ones. + properties: + rollingUpdate: + description: 'Rolling update config params. Present + only if DeploymentStrategyType = RollingUpdate. --- + TODO: Update this to follow our convention for oneOf, + whatever we decide it to be.' + properties: + maxSurge: + anyOf: + - type: integer + - type: string + description: 'The maximum number of pods that can + be scheduled above the desired number of pods. + Value can be an absolute number (ex: 5) or a percentage + of desired pods (ex: 10%). This can not be 0 if + MaxUnavailable is 0. Absolute number is calculated + from percentage by rounding up. Defaults to 25%. + Example: when this is set to 30%, the new ReplicaSet + can be scaled up immediately when the rolling + update starts, such that the total number of old + and new pods do not exceed 130% of desired pods. + Once old pods have been killed, new ReplicaSet + can be scaled up further, ensuring that total + number of pods running at any time during the + update is at most 130% of desired pods.' + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: 'The maximum number of pods that can + be unavailable during the update. Value can be + an absolute number (ex: 5) or a percentage of + desired pods (ex: 10%). Absolute number is calculated + from percentage by rounding down. This can not + be 0 if MaxSurge is 0. Defaults to 25%. Example: + when this is set to 30%, the old ReplicaSet can + be scaled down to 70% of desired pods immediately + when the rolling update starts. Once new pods + are ready, old ReplicaSet can be scaled down further, + followed by scaling up the new ReplicaSet, ensuring + that the total number of pods available at all + times during the update is at least 70% of desired + pods.' + x-kubernetes-int-or-string: true + type: object + type: + description: Type of deployment. Can be "Recreate" or + "RollingUpdate". Default is RollingUpdate. + type: string + type: object + template: + description: Template describes the pods that will be created. + properties: + metadata: + description: 'Standard object''s metadata. More info: + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: 'Specification of the desired behavior + of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + activeDeadlineSeconds: + description: Optional duration in seconds the pod + may be active on the node relative to StartTime + before the system will actively try to mark it + failed and kill associated containers. Value must + be a positive integer. + format: int64 + type: integer + affinity: + description: If specified, the pod's scheduling + constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling + rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to + schedule pods to nodes that satisfy the + affinity expressions specified by this + field, but it may choose a node that violates + one or more of the expressions. The node + that is most preferred is the one with + the greatest sum of weights, i.e. for + each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a + sum by iterating through the elements + of this field and adding "weight" to the + sum if the node matches the corresponding + matchExpressions; the node(s) with the + highest sum are the most preferred. + items: + description: An empty preferred scheduling + term matches all objects with implicit + weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no + objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, + associated with the corresponding + weight. + properties: + matchExpressions: + description: A list of node selector + requirements by node's labels. + items: + description: A node selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + properties: + key: + description: The label key + that the selector applies + to. + type: string + operator: + description: Represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of + string values. If the + operator is In or NotIn, + the values array must + be non-empty. If the operator + is Exists or DoesNotExist, + the values array must + be empty. If the operator + is Gt or Lt, the values + array must have a single + element, which will be + interpreted as an integer. + This array is replaced + during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector + requirements by node's fields. + items: + description: A node selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + properties: + key: + description: The label key + that the selector applies + to. + type: string + operator: + description: Represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of + string values. If the + operator is In or NotIn, + the values array must + be non-empty. If the operator + is Exists or DoesNotExist, + the values array must + be empty. If the operator + is Gt or Lt, the values + array must have a single + element, which will be + interpreted as an integer. + This array is replaced + during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with + matching the corresponding nodeSelectorTerm, + in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not met at + scheduling time, the pod will not be scheduled + onto the node. If the affinity requirements + specified by this field cease to be met + at some point during pod execution (e.g. + due to an update), the system may or may + not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node + selector terms. The terms are ORed. + items: + description: A null or empty node + selector term matches no objects. + The requirements of them are ANDed. + The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector + requirements by node's labels. + items: + description: A node selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + properties: + key: + description: The label key + that the selector applies + to. + type: string + operator: + description: Represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of + string values. If the + operator is In or NotIn, + the values array must + be non-empty. If the operator + is Exists or DoesNotExist, + the values array must + be empty. If the operator + is Gt or Lt, the values + array must have a single + element, which will be + interpreted as an integer. + This array is replaced + during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector + requirements by node's fields. + items: + description: A node selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + properties: + key: + description: The label key + that the selector applies + to. + type: string + operator: + description: Represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of + string values. If the + operator is In or NotIn, + the values array must + be non-empty. If the operator + is Exists or DoesNotExist, + the values array must + be empty. If the operator + is Gt or Lt, the values + array must have a single + element, which will be + interpreted as an integer. + This array is replaced + during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling + rules (e.g. co-locate this pod in the same + node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to + schedule pods to nodes that satisfy the + affinity expressions specified by this + field, but it may choose a node that violates + one or more of the expressions. The node + that is most preferred is the one with + the greatest sum of weights, i.e. for + each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a + sum by iterating through the elements + of this field and adding "weight" to the + sum if the node has pods which matches + the corresponding podAffinityTerm; the + node(s) with the highest sum are the most + preferred. + items: + description: The weights of all of the + matched WeightedPodAffinityTerm fields + are added per-node to find the most + preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity + term, associated with the corresponding + weight. + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key and + values. + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to a + set of values. Valid + operators are In, + NotIn, Exists and + DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, the + values array must + be non-empty. If the + operator is Exists + or DoesNotExist, the + values array must + be empty. This array + is replaced during + a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is + a map of {key,value} pairs. + A single {key,value} in + the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", + the operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over + the set of namespaces that the + term applies to. The term is + applied to the union of the + namespaces selected by this + field and the ones listed in + the namespaces field. null selector + and null or empty namespaces + list means "this pod's namespace". + An empty selector ({}) matches + all namespaces. This field is + beta-level and is only honored + when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key and + values. + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to a + set of values. Valid + operators are In, + NotIn, Exists and + DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, the + values array must + be non-empty. If the + operator is Exists + or DoesNotExist, the + values array must + be empty. This array + is replaced during + a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is + a map of {key,value} pairs. + A single {key,value} in + the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", + the operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies + a static list of namespace names + that the term applies to. The + term is applied to the union + of the namespaces listed in + this field and the ones selected + by namespaceSelector. null or + empty namespaces list and null + namespaceSelector means "this + pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be + co-located (affinity) or not + co-located (anti-affinity) with + the pods matching the labelSelector + in the specified namespaces, + where co-located is defined + as running on a node whose value + of the label with key topologyKey + matches that of any node on + which any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with + matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not met at + scheduling time, the pod will not be scheduled + onto the node. If the affinity requirements + specified by this field cease to be met + at some point during pod execution (e.g. + due to a pod label update), the system + may or may not try to eventually evict + the pod from its node. When there are + multiple elements, the lists of nodes + corresponding to each podAffinityTerm + are intersected, i.e. all terms must be + satisfied. + items: + description: Defines a set of pods (namely + those matching the labelSelector relative + to the given namespace(s)) that this + pod should be co-located (affinity) + or not co-located (anti-affinity) with, + where co-located is defined as running + on a node whose value of the label with + key matches that of any + node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a + set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + properties: + key: + description: key is the + label key that the selector + applies to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In + or NotIn, the values array + must be non-empty. If + the operator is Exists + or DoesNotExist, the values + array must be empty. This + array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a + map of {key,value} pairs. A + single {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator + is "In", and the values array + contains only "value". The requirements + are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the + set of namespaces that the term + applies to. The term is applied + to the union of the namespaces selected + by this field and the ones listed + in the namespaces field. null selector + and null or empty namespaces list + means "this pod's namespace". An + empty selector ({}) matches all + namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + properties: + key: + description: key is the + label key that the selector + applies to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In + or NotIn, the values array + must be non-empty. If + the operator is Exists + or DoesNotExist, the values + array must be empty. This + array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a + map of {key,value} pairs. A + single {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator + is "In", and the values array + contains only "value". The requirements + are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies + a static list of namespace names + that the term applies to. The term + is applied to the union of the namespaces + listed in this field and the ones + selected by namespaceSelector. null + or empty namespaces list and null + namespaceSelector means "this pod's + namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where + co-located is defined as running + on a node whose value of the label + with key topologyKey matches that + of any node on which any of the + selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling + rules (e.g. avoid putting this pod in the + same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to + schedule pods to nodes that satisfy the + anti-affinity expressions specified by + this field, but it may choose a node that + violates one or more of the expressions. + The node that is most preferred is the + one with the greatest sum of weights, + i.e. for each node that meets all of the + scheduling requirements (resource request, + requiredDuringScheduling anti-affinity + expressions, etc.), compute a sum by iterating + through the elements of this field and + adding "weight" to the sum if the node + has pods which matches the corresponding + podAffinityTerm; the node(s) with the + highest sum are the most preferred. + items: + description: The weights of all of the + matched WeightedPodAffinityTerm fields + are added per-node to find the most + preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity + term, associated with the corresponding + weight. + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key and + values. + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to a + set of values. Valid + operators are In, + NotIn, Exists and + DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, the + values array must + be non-empty. If the + operator is Exists + or DoesNotExist, the + values array must + be empty. This array + is replaced during + a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is + a map of {key,value} pairs. + A single {key,value} in + the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", + the operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over + the set of namespaces that the + term applies to. The term is + applied to the union of the + namespaces selected by this + field and the ones listed in + the namespaces field. null selector + and null or empty namespaces + list means "this pod's namespace". + An empty selector ({}) matches + all namespaces. This field is + beta-level and is only honored + when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key and + values. + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to a + set of values. Valid + operators are In, + NotIn, Exists and + DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, the + values array must + be non-empty. If the + operator is Exists + or DoesNotExist, the + values array must + be empty. This array + is replaced during + a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is + a map of {key,value} pairs. + A single {key,value} in + the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", + the operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies + a static list of namespace names + that the term applies to. The + term is applied to the union + of the namespaces listed in + this field and the ones selected + by namespaceSelector. null or + empty namespaces list and null + namespaceSelector means "this + pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be + co-located (affinity) or not + co-located (anti-affinity) with + the pods matching the labelSelector + in the specified namespaces, + where co-located is defined + as running on a node whose value + of the label with key topologyKey + matches that of any node on + which any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with + matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements + specified by this field are not met at + scheduling time, the pod will not be scheduled + onto the node. If the anti-affinity requirements + specified by this field cease to be met + at some point during pod execution (e.g. + due to a pod label update), the system + may or may not try to eventually evict + the pod from its node. When there are + multiple elements, the lists of nodes + corresponding to each podAffinityTerm + are intersected, i.e. all terms must be + satisfied. + items: + description: Defines a set of pods (namely + those matching the labelSelector relative + to the given namespace(s)) that this + pod should be co-located (affinity) + or not co-located (anti-affinity) with, + where co-located is defined as running + on a node whose value of the label with + key matches that of any + node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a + set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + properties: + key: + description: key is the + label key that the selector + applies to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In + or NotIn, the values array + must be non-empty. If + the operator is Exists + or DoesNotExist, the values + array must be empty. This + array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a + map of {key,value} pairs. A + single {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator + is "In", and the values array + contains only "value". The requirements + are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the + set of namespaces that the term + applies to. The term is applied + to the union of the namespaces selected + by this field and the ones listed + in the namespaces field. null selector + and null or empty namespaces list + means "this pod's namespace". An + empty selector ({}) matches all + namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + properties: + key: + description: key is the + label key that the selector + applies to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In + or NotIn, the values array + must be non-empty. If + the operator is Exists + or DoesNotExist, the values + array must be empty. This + array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a + map of {key,value} pairs. A + single {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator + is "In", and the values array + contains only "value". The requirements + are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies + a static list of namespace names + that the term applies to. The term + is applied to the union of the namespaces + listed in this field and the ones + selected by namespaceSelector. null + or empty namespaces list and null + namespaceSelector means "this pod's + namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where + co-located is defined as running + on a node whose value of the label + with key topologyKey matches that + of any node on which any of the + selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates + whether a service account token should be automatically + mounted. + type: boolean + containers: + description: List of containers belonging to the + pod. Containers cannot currently be added or removed. + There must be at least one container in a Pod. + Cannot be updated. + items: + description: A single application container that + you want to run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if this + is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. Double + $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal + "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed + within a shell. The docker image''s ENTRYPOINT + is used if this is not provided. Variable + references $(VAR_NAME) are expanded using + the container''s environment. If a variable + cannot be resolved, the reference in the + input string will be unchanged. Double $$ + are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal + "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables + to set in the container. Cannot be updated. + items: + description: EnvVar represents an environment + variable present in a Container. + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previously + defined environment variables in the + container and any service environment + variables. If a variable cannot be + resolved, the reference in the input + string will be unchanged. Double $$ + are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: + i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped + references will never be expanded, + regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be used if + value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a + ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its key must + be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of + the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the + schema the FieldPath is written + in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field + to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: + required for volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a + secret in the pod's namespace + properties: + key: + description: The key of the + secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined + within a source must be a C_IDENTIFIER. + All invalid keys will be reported as an + event when the container is starting. When + a key exists in multiple sources, the value + associated with the last source will take + precedence. Values defined by an Env with + a duplicate key will take precedence. Cannot + be updated. + items: + description: EnvFromSource represents the + source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select + from + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the + ConfigMap must be defined + type: boolean + type: object + prefix: + description: An optional identifier + to prepend to each key in the ConfigMap. + Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the + Secret must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: + https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level + config management to default or override + container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, + Never, IfNotPresent. Defaults to Always + if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: + https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system + should take in response to container lifecycle + events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the + handler fails, the container is terminated + and restarted according to its restart + policy. Other management of the container + blocks until the hook completes. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the + following should be specified. Exec + specifies the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the + command is root ('/') in the + container's filesystem. The + command is simply exec'd, it + is not run inside a shell, so + traditional shell instructions + ('|', etc) won't work. To use + a shell, you need to explicitly + call out to that shell. Exit + status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the + http request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to + set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on + the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the + pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately + before a container is terminated due + to an API request or management event + such as liveness/startup probe failure, + preemption, resource contention, etc. + The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s + termination grace period countdown begins + before the PreStop hooked is executed. + Regardless of the outcome of the handler, + the container will eventually terminate + within the Pod''s termination grace + period. Other management of the container + blocks until the hook completes or until + the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the + following should be specified. Exec + specifies the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the + command is root ('/') in the + container's filesystem. The + command is simply exec'd, it + is not run inside a shell, so + traditional shell instructions + ('|', etc) won't work. To use + a shell, you need to explicitly + call out to that shell. Exit + status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the + http request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to + set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on + the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the + pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container + liveness. Container will be restarted if + the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified + as a DNS_LABEL. Each container in a pod + must have a unique name (DNS_LABEL). Cannot + be updated. + type: string + ports: + description: List of ports to expose from + the container. Exposing a port here gives + the system additional information about + the network connections a container uses, + but is primarily informational. Not specifying + a port here DOES NOT prevent that port from + being exposed. Any port which is listening + on the default "0.0.0.0" address inside + a container will be accessible from the + network. Cannot be updated. + items: + description: ContainerPort represents a + network port in a single container. + properties: + containerPort: + description: Number of port to expose + on the pod's IP address. This must + be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the + external port to. + type: string + hostPort: + description: Number of port to expose + on the host. If specified, this must + be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this + must match ContainerPort. Most containers + do not need this. + format: int32 + type: integer + name: + description: If specified, this must + be an IANA_SVC_NAME and unique within + the pod. Each named port in a pod + must have a unique name. Name for + the port that can be referred to by + services. + type: string + protocol: + default: TCP + description: Protocol for port. Must + be UDP, TCP, or SCTP. Defaults to + "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container + service readiness. Container will be removed + from service endpoints if the probe fails. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by + this container. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. + If Requests is omitted for a container, + it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + securityContext: + description: 'SecurityContext defines the + security options the container should be + run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can gain + more privileges than its parent process. + This bool directly controls if the no_new_privs + flag will be set on the container process. + AllowPrivilegeEscalation is true always + when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop + when running containers. Defaults to + the default set of capabilities granted + by the container runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent + POSIX capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent + POSIX capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged + mode. Processes in privileged containers + are essentially equivalent to root on + the host. Defaults to false. + type: boolean + procMount: + description: procMount denotes the type + of proc mount to use for the containers. + The default is DefaultProcMount which + uses the container runtime defaults + for readonly paths and masked paths. + This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has + a read-only root filesystem. Default + is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint + of the container process. Uses runtime + default if unset. May also be set in + PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container + must run as a non-root user. If true, + the Kubelet will validate the image + at runtime to ensure that it does not + run as UID 0 (root) and fail to start + the container if it does. If unset or + false, no such validation will be performed. + May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint + of the container process. Defaults to + user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be + applied to the container. If unspecified, + the container runtime will allocate + a random SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + properties: + level: + description: Level is SELinux level + label that applies to the container. + type: string + role: + description: Role is a SELinux role + label that applies to the container. + type: string + type: + description: Type is a SELinux type + label that applies to the container. + type: string + user: + description: User is a SELinux user + label that applies to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use + by this container. If seccomp options + are provided at both the pod & container + level, the container options override + the pod options. + properties: + localhostProfile: + description: localhostProfile indicates + a profile defined in a file on the + node should be used. The profile + must be preconfigured on the node + to work. Must be a descending path, + relative to the kubelet's configured + seccomp profile location. Must only + be set if type is "Localhost". + type: string + type: + description: "type indicates which + kind of seccomp profile will be + applied. Valid options are: \n Localhost + - a profile defined in a file on + the node should be used. RuntimeDefault + - the container runtime default + profile should be used. Unconfined + - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings + applied to all containers. If unspecified, + the options from the PodSecurityContext + will be used. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is + where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA + credential spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. + type: string + hostProcess: + description: HostProcess determines + if a container should be run as + a 'Host Process' container. This + field is alpha-level and will only + be honored by components that enable + the WindowsHostProcessContainers + feature flag. Setting this field + without the feature flag will result + in errors when validating the Pod. + All of a Pod's containers must have + the same effective HostProcess value + (it is not allowed to have a mix + of HostProcess containers and non-HostProcess + containers). In addition, if HostProcess + is true then HostNetwork must also + be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows + to run the entrypoint of the container + process. Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext and + PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that + the Pod has successfully initialized. If + specified, no other probes are executed + until this completes successfully. If this + probe fails, the Pod will be restarted, + just as if the livenessProbe failed. This + can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, + when it might take a long time to load data + or warm a cache, than during steady-state + operation. This cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should + allocate a buffer for stdin in the container + runtime. If this is not set, reads from + stdin in the container will always result + in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after it + has been opened by a single attach. When + stdin is true the stdin stream will remain + open across multiple attach sessions. If + stdinOnce is set to true, stdin is opened + on container start, is empty until the first + client attaches to stdin, and then remains + open and accepts data until the client disconnects, + at which time stdin is closed and remains + closed until the container is restarted. + If this flag is false, a container processes + that reads from stdin will never receive + an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the + file to which the container''s termination + message will be written is mounted into + the container''s filesystem. Message written + is intended to be brief final status, such + as an assertion failure message. Will be + truncated by the node if greater than 4096 + bytes. The total message length across all + containers will be limited to 12kb. Defaults + to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File will use + the contents of terminationMessagePath to + populate the container status message on + both success and failure. FallbackToLogsOnError + will use the last chunk of container log + output if the termination message file is + empty and the container exited with an error. + The log output is limited to 2048 bytes + or 80 lines, whichever is smaller. Defaults + to File. Cannot be updated. + type: string + tty: + description: Whether this container should + allocate a TTY for itself, also requires + 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of + block devices to be used by the container. + items: + description: volumeDevice describes a mapping + of a raw block device within a container. + properties: + devicePath: + description: devicePath is the path + inside of the container that the device + will be mapped to. + type: string + name: + description: name must match the name + of a persistentVolumeClaim in the + pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the + container's filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting + of a Volume within a container. + properties: + mountPath: + description: Path within the container + at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines + how mounts are propagated from the + host to container and the other way + around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name + of a Volume. + type: string + readOnly: + description: Mounted read-only if true, + read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults to "" + (volume's root). + type: string + subPathExpr: + description: Expanded path within the + volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) are + expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might be configured + in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + dnsConfig: + description: Specifies the DNS parameters of a pod. + Parameters specified here will be merged to the + generated DNS configuration based on DNSPolicy. + properties: + nameservers: + description: A list of DNS name server IP addresses. + This will be appended to the base nameservers + generated from DNSPolicy. Duplicated nameservers + will be removed. + items: + type: string + type: array + options: + description: A list of DNS resolver options. + This will be merged with the base options + generated from DNSPolicy. Duplicated entries + will be removed. Resolution options given + in Options will override those that appear + in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS + resolver options of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: A list of DNS search domains for + host-name lookup. This will be appended to + the base search paths generated from DNSPolicy. + Duplicated search paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: Set DNS policy for the pod. Defaults + to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet', + 'ClusterFirst', 'Default' or 'None'. DNS parameters + given in DNSConfig will be merged with the policy + selected with DNSPolicy. To have DNS options set + along with hostNetwork, you have to specify DNS + policy explicitly to 'ClusterFirstWithHostNet'. + type: string + enableServiceLinks: + description: 'EnableServiceLinks indicates whether + information about services should be injected + into pod''s environment variables, matching the + syntax of Docker links. Optional: Defaults to + true.' + type: boolean + ephemeralContainers: + description: List of ephemeral containers run in + this pod. Ephemeral containers may be run in an + existing pod to perform user-initiated actions + such as debugging. This list cannot be specified + when creating a pod, and it cannot be modified + by updating the pod spec. In order to add an ephemeral + container to an existing pod, use the pod's ephemeralcontainers + subresource. This field is alpha-level and is + only honored by servers that enable the EphemeralContainers + feature. + items: + description: An EphemeralContainer is a container + that may be added temporarily to an existing + pod for user-initiated activities such as debugging. + Ephemeral containers have no resource or scheduling + guarantees, and they will not be restarted when + they exit or when a pod is removed or restarted. + If an ephemeral container causes a pod to exceed + its resource allocation, the pod may be evicted. + Ephemeral containers may not be added by directly + updating the pod spec. They must be added via + the pod's ephemeralcontainers subresource, and + they will appear in the pod spec once added. + This is an alpha feature enabled by the EphemeralContainers + feature flag. + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if this + is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. Double + $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal + "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed + within a shell. The docker image''s ENTRYPOINT + is used if this is not provided. Variable + references $(VAR_NAME) are expanded using + the container''s environment. If a variable + cannot be resolved, the reference in the + input string will be unchanged. Double $$ + are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal + "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables + to set in the container. Cannot be updated. + items: + description: EnvVar represents an environment + variable present in a Container. + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previously + defined environment variables in the + container and any service environment + variables. If a variable cannot be + resolved, the reference in the input + string will be unchanged. Double $$ + are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: + i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped + references will never be expanded, + regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be used if + value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a + ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its key must + be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of + the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the + schema the FieldPath is written + in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field + to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: + required for volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a + secret in the pod's namespace + properties: + key: + description: The key of the + secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined + within a source must be a C_IDENTIFIER. + All invalid keys will be reported as an + event when the container is starting. When + a key exists in multiple sources, the value + associated with the last source will take + precedence. Values defined by an Env with + a duplicate key will take precedence. Cannot + be updated. + items: + description: EnvFromSource represents the + source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select + from + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the + ConfigMap must be defined + type: boolean + type: object + prefix: + description: An optional identifier + to prepend to each key in the ConfigMap. + Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the + Secret must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: + https://kubernetes.io/docs/concepts/containers/images' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, + Never, IfNotPresent. Defaults to Always + if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: + https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Lifecycle is not allowed for + ephemeral containers. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the + handler fails, the container is terminated + and restarted according to its restart + policy. Other management of the container + blocks until the hook completes. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the + following should be specified. Exec + specifies the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the + command is root ('/') in the + container's filesystem. The + command is simply exec'd, it + is not run inside a shell, so + traditional shell instructions + ('|', etc) won't work. To use + a shell, you need to explicitly + call out to that shell. Exit + status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the + http request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to + set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on + the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the + pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately + before a container is terminated due + to an API request or management event + such as liveness/startup probe failure, + preemption, resource contention, etc. + The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s + termination grace period countdown begins + before the PreStop hooked is executed. + Regardless of the outcome of the handler, + the container will eventually terminate + within the Pod''s termination grace + period. Other management of the container + blocks until the hook completes or until + the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the + following should be specified. Exec + specifies the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the + command is root ('/') in the + container's filesystem. The + command is simply exec'd, it + is not run inside a shell, so + traditional shell instructions + ('|', etc) won't work. To use + a shell, you need to explicitly + call out to that shell. Exit + status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the + http request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to + set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on + the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the + pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the ephemeral container + specified as a DNS_LABEL. This name must + be unique among all containers, init containers + and ephemeral containers. + type: string + ports: + description: Ports are not allowed for ephemeral + containers. + items: + description: ContainerPort represents a + network port in a single container. + properties: + containerPort: + description: Number of port to expose + on the pod's IP address. This must + be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the + external port to. + type: string + hostPort: + description: Number of port to expose + on the host. If specified, this must + be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this + must match ContainerPort. Most containers + do not need this. + format: int32 + type: integer + name: + description: If specified, this must + be an IANA_SVC_NAME and unique within + the pod. Each named port in a pod + must have a unique name. Name for + the port that can be referred to by + services. + type: string + protocol: + default: TCP + description: Protocol for port. Must + be UDP, TCP, or SCTP. Defaults to + "TCP". + type: string + required: + - containerPort + type: object + type: array + readinessProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: Resources are not allowed for + ephemeral containers. Ephemeral containers + use spare resources already allocated to + the pod. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. + If Requests is omitted for a container, + it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + securityContext: + description: 'Optional: SecurityContext defines + the security options the ephemeral container + should be run with. If set, the fields of + SecurityContext override the equivalent + fields of PodSecurityContext.' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can gain + more privileges than its parent process. + This bool directly controls if the no_new_privs + flag will be set on the container process. + AllowPrivilegeEscalation is true always + when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop + when running containers. Defaults to + the default set of capabilities granted + by the container runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent + POSIX capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent + POSIX capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged + mode. Processes in privileged containers + are essentially equivalent to root on + the host. Defaults to false. + type: boolean + procMount: + description: procMount denotes the type + of proc mount to use for the containers. + The default is DefaultProcMount which + uses the container runtime defaults + for readonly paths and masked paths. + This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has + a read-only root filesystem. Default + is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint + of the container process. Uses runtime + default if unset. May also be set in + PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container + must run as a non-root user. If true, + the Kubelet will validate the image + at runtime to ensure that it does not + run as UID 0 (root) and fail to start + the container if it does. If unset or + false, no such validation will be performed. + May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint + of the container process. Defaults to + user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be + applied to the container. If unspecified, + the container runtime will allocate + a random SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + properties: + level: + description: Level is SELinux level + label that applies to the container. + type: string + role: + description: Role is a SELinux role + label that applies to the container. + type: string + type: + description: Type is a SELinux type + label that applies to the container. + type: string + user: + description: User is a SELinux user + label that applies to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use + by this container. If seccomp options + are provided at both the pod & container + level, the container options override + the pod options. + properties: + localhostProfile: + description: localhostProfile indicates + a profile defined in a file on the + node should be used. The profile + must be preconfigured on the node + to work. Must be a descending path, + relative to the kubelet's configured + seccomp profile location. Must only + be set if type is "Localhost". + type: string + type: + description: "type indicates which + kind of seccomp profile will be + applied. Valid options are: \n Localhost + - a profile defined in a file on + the node should be used. RuntimeDefault + - the container runtime default + profile should be used. Unconfined + - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings + applied to all containers. If unspecified, + the options from the PodSecurityContext + will be used. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is + where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA + credential spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. + type: string + hostProcess: + description: HostProcess determines + if a container should be run as + a 'Host Process' container. This + field is alpha-level and will only + be honored by components that enable + the WindowsHostProcessContainers + feature flag. Setting this field + without the feature flag will result + in errors when validating the Pod. + All of a Pod's containers must have + the same effective HostProcess value + (it is not allowed to have a mix + of HostProcess containers and non-HostProcess + containers). In addition, if HostProcess + is true then HostNetwork must also + be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows + to run the entrypoint of the container + process. Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext and + PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should + allocate a buffer for stdin in the container + runtime. If this is not set, reads from + stdin in the container will always result + in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after it + has been opened by a single attach. When + stdin is true the stdin stream will remain + open across multiple attach sessions. If + stdinOnce is set to true, stdin is opened + on container start, is empty until the first + client attaches to stdin, and then remains + open and accepts data until the client disconnects, + at which time stdin is closed and remains + closed until the container is restarted. + If this flag is false, a container processes + that reads from stdin will never receive + an EOF. Default is false + type: boolean + targetContainerName: + description: If set, the name of the container + from PodSpec that this ephemeral container + targets. The ephemeral container will be + run in the namespaces (IPC, PID, etc) of + this container. If not set then the ephemeral + container is run in whatever namespaces + are shared for the pod. Note that the container + runtime must support this feature. + type: string + terminationMessagePath: + description: 'Optional: Path at which the + file to which the container''s termination + message will be written is mounted into + the container''s filesystem. Message written + is intended to be brief final status, such + as an assertion failure message. Will be + truncated by the node if greater than 4096 + bytes. The total message length across all + containers will be limited to 12kb. Defaults + to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File will use + the contents of terminationMessagePath to + populate the container status message on + both success and failure. FallbackToLogsOnError + will use the last chunk of container log + output if the termination message file is + empty and the container exited with an error. + The log output is limited to 2048 bytes + or 80 lines, whichever is smaller. Defaults + to File. Cannot be updated. + type: string + tty: + description: Whether this container should + allocate a TTY for itself, also requires + 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of + block devices to be used by the container. + items: + description: volumeDevice describes a mapping + of a raw block device within a container. + properties: + devicePath: + description: devicePath is the path + inside of the container that the device + will be mapped to. + type: string + name: + description: name must match the name + of a persistentVolumeClaim in the + pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the + container's filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting + of a Volume within a container. + properties: + mountPath: + description: Path within the container + at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines + how mounts are propagated from the + host to container and the other way + around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name + of a Volume. + type: string + readOnly: + description: Mounted read-only if true, + read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults to "" + (volume's root). + type: string + subPathExpr: + description: Expanded path within the + volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) are + expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might be configured + in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + hostAliases: + description: HostAliases is an optional list of + hosts and IPs that will be injected into the pod's + hosts file if specified. This is only valid for + non-hostNetwork pods. + items: + description: HostAlias holds the mapping between + IP and hostnames that will be injected as an + entry in the pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + ip: + description: IP address of the host file entry. + type: string + type: object + type: array + hostIPC: + description: 'Use the host''s ipc namespace. Optional: + Default to false.' + type: boolean + hostNetwork: + description: Host networking requested for this + pod. Use the host's network namespace. If this + option is set, the ports that will be used must + be specified. Default to false. + type: boolean + hostPID: + description: 'Use the host''s pid namespace. Optional: + Default to false.' + type: boolean + hostname: + description: Specifies the hostname of the Pod If + not specified, the pod's hostname will be set + to a system-defined value. + type: string + imagePullSecrets: + description: 'ImagePullSecrets is an optional list + of references to secrets in the same namespace + to use for pulling any of the images used by this + PodSpec. If specified, these secrets will be passed + to individual puller implementations for them + to use. For example, in the case of docker, only + DockerConfig type secrets are honored. More info: + https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + items: + description: LocalObjectReference contains enough + information to let you locate the referenced + object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + type: array + initContainers: + description: 'List of initialization containers + belonging to the pod. Init containers are executed + in order prior to containers being started. If + any init container fails, the pod is considered + to have failed and is handled according to its + restartPolicy. The name for an init container + or normal container must be unique among all containers. + Init containers may not have Lifecycle actions, + Readiness probes, Liveness probes, or Startup + probes. The resourceRequirements of an init container + are taken into account during scheduling by finding + the highest request/limit for each resource type, + and then using the max of of that value or the + sum of the normal containers. Limits are applied + to init containers in a similar fashion. Init + containers cannot currently be added or removed. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + items: + description: A single application container that + you want to run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if this + is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. Double + $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal + "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed + within a shell. The docker image''s ENTRYPOINT + is used if this is not provided. Variable + references $(VAR_NAME) are expanded using + the container''s environment. If a variable + cannot be resolved, the reference in the + input string will be unchanged. Double $$ + are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal + "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables + to set in the container. Cannot be updated. + items: + description: EnvVar represents an environment + variable present in a Container. + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previously + defined environment variables in the + container and any service environment + variables. If a variable cannot be + resolved, the reference in the input + string will be unchanged. Double $$ + are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: + i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped + references will never be expanded, + regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be used if + value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a + ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its key must + be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of + the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the + schema the FieldPath is written + in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field + to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: + required for volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a + secret in the pod's namespace + properties: + key: + description: The key of the + secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined + within a source must be a C_IDENTIFIER. + All invalid keys will be reported as an + event when the container is starting. When + a key exists in multiple sources, the value + associated with the last source will take + precedence. Values defined by an Env with + a duplicate key will take precedence. Cannot + be updated. + items: + description: EnvFromSource represents the + source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select + from + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the + ConfigMap must be defined + type: boolean + type: object + prefix: + description: An optional identifier + to prepend to each key in the ConfigMap. + Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the + Secret must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: + https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level + config management to default or override + container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, + Never, IfNotPresent. Defaults to Always + if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: + https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system + should take in response to container lifecycle + events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the + handler fails, the container is terminated + and restarted according to its restart + policy. Other management of the container + blocks until the hook completes. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the + following should be specified. Exec + specifies the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the + command is root ('/') in the + container's filesystem. The + command is simply exec'd, it + is not run inside a shell, so + traditional shell instructions + ('|', etc) won't work. To use + a shell, you need to explicitly + call out to that shell. Exit + status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the + http request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to + set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on + the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the + pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately + before a container is terminated due + to an API request or management event + such as liveness/startup probe failure, + preemption, resource contention, etc. + The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s + termination grace period countdown begins + before the PreStop hooked is executed. + Regardless of the outcome of the handler, + the container will eventually terminate + within the Pod''s termination grace + period. Other management of the container + blocks until the hook completes or until + the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the + following should be specified. Exec + specifies the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the + command is root ('/') in the + container's filesystem. The + command is simply exec'd, it + is not run inside a shell, so + traditional shell instructions + ('|', etc) won't work. To use + a shell, you need to explicitly + call out to that shell. Exit + status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the + http request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to + set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on + the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the + pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of + the port to access on the container. + Number must be in the range + 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container + liveness. Container will be restarted if + the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified + as a DNS_LABEL. Each container in a pod + must have a unique name (DNS_LABEL). Cannot + be updated. + type: string + ports: + description: List of ports to expose from + the container. Exposing a port here gives + the system additional information about + the network connections a container uses, + but is primarily informational. Not specifying + a port here DOES NOT prevent that port from + being exposed. Any port which is listening + on the default "0.0.0.0" address inside + a container will be accessible from the + network. Cannot be updated. + items: + description: ContainerPort represents a + network port in a single container. + properties: + containerPort: + description: Number of port to expose + on the pod's IP address. This must + be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the + external port to. + type: string + hostPort: + description: Number of port to expose + on the host. If specified, this must + be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this + must match ContainerPort. Most containers + do not need this. + format: int32 + type: integer + name: + description: If specified, this must + be an IANA_SVC_NAME and unique within + the pod. Each named port in a pod + must have a unique name. Name for + the port that can be referred to by + services. + type: string + protocol: + default: TCP + description: Protocol for port. Must + be UDP, TCP, or SCTP. Defaults to + "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container + service readiness. Container will be removed + from service endpoints if the probe fails. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by + this container. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum + amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. + If Requests is omitted for a container, + it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + securityContext: + description: 'SecurityContext defines the + security options the container should be + run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can gain + more privileges than its parent process. + This bool directly controls if the no_new_privs + flag will be set on the container process. + AllowPrivilegeEscalation is true always + when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop + when running containers. Defaults to + the default set of capabilities granted + by the container runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent + POSIX capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent + POSIX capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged + mode. Processes in privileged containers + are essentially equivalent to root on + the host. Defaults to false. + type: boolean + procMount: + description: procMount denotes the type + of proc mount to use for the containers. + The default is DefaultProcMount which + uses the container runtime defaults + for readonly paths and masked paths. + This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has + a read-only root filesystem. Default + is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint + of the container process. Uses runtime + default if unset. May also be set in + PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container + must run as a non-root user. If true, + the Kubelet will validate the image + at runtime to ensure that it does not + run as UID 0 (root) and fail to start + the container if it does. If unset or + false, no such validation will be performed. + May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint + of the container process. Defaults to + user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be + applied to the container. If unspecified, + the container runtime will allocate + a random SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + properties: + level: + description: Level is SELinux level + label that applies to the container. + type: string + role: + description: Role is a SELinux role + label that applies to the container. + type: string + type: + description: Type is a SELinux type + label that applies to the container. + type: string + user: + description: User is a SELinux user + label that applies to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use + by this container. If seccomp options + are provided at both the pod & container + level, the container options override + the pod options. + properties: + localhostProfile: + description: localhostProfile indicates + a profile defined in a file on the + node should be used. The profile + must be preconfigured on the node + to work. Must be a descending path, + relative to the kubelet's configured + seccomp profile location. Must only + be set if type is "Localhost". + type: string + type: + description: "type indicates which + kind of seccomp profile will be + applied. Valid options are: \n Localhost + - a profile defined in a file on + the node should be used. RuntimeDefault + - the container runtime default + profile should be used. Unconfined + - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings + applied to all containers. If unspecified, + the options from the PodSecurityContext + will be used. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is + where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA + credential spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. + type: string + hostProcess: + description: HostProcess determines + if a container should be run as + a 'Host Process' container. This + field is alpha-level and will only + be honored by components that enable + the WindowsHostProcessContainers + feature flag. Setting this field + without the feature flag will result + in errors when validating the Pod. + All of a Pod's containers must have + the same effective HostProcess value + (it is not allowed to have a mix + of HostProcess containers and non-HostProcess + containers). In addition, if HostProcess + is true then HostNetwork must also + be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows + to run the entrypoint of the container + process. Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext and + PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that + the Pod has successfully initialized. If + specified, no other probes are executed + until this completes successfully. If this + probe fails, the Pod will be restarted, + just as if the livenessProbe failed. This + can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, + when it might take a long time to load data + or warm a cache, than during steady-state + operation. This cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies + the action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it + is not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures + for the probe to be considered failed + after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect + to, defaults to the pod IP. You + probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set + in the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in + HTTP probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the + HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after + the container has started before liveness + probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to + perform the probe. Default to 10 seconds. + Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes + for the probe to be considered successful + after having failed. Defaults to 1. + Must be 1 for liveness and startup. + Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not + yet supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name + to connect to, defaults to the pod + IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the + port to access on the container. + Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully + upon probe failure. The grace period + is the duration in seconds after the + processes running in the pod are sent + a termination signal and the time when + the processes are forcibly halted with + a kill signal. Set this value longer + than the expected cleanup time for your + process. If this value is nil, the pod's + terminationGracePeriodSeconds will be + used. Otherwise, this value overrides + the value provided by the pod spec. + Value must be non-negative integer. + The value zero indicates stop immediately + via the kill signal (no opportunity + to shut down). This is a beta field + and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after + which the probe times out. Defaults + to 1 second. Minimum value is 1. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should + allocate a buffer for stdin in the container + runtime. If this is not set, reads from + stdin in the container will always result + in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after it + has been opened by a single attach. When + stdin is true the stdin stream will remain + open across multiple attach sessions. If + stdinOnce is set to true, stdin is opened + on container start, is empty until the first + client attaches to stdin, and then remains + open and accepts data until the client disconnects, + at which time stdin is closed and remains + closed until the container is restarted. + If this flag is false, a container processes + that reads from stdin will never receive + an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the + file to which the container''s termination + message will be written is mounted into + the container''s filesystem. Message written + is intended to be brief final status, such + as an assertion failure message. Will be + truncated by the node if greater than 4096 + bytes. The total message length across all + containers will be limited to 12kb. Defaults + to /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File will use + the contents of terminationMessagePath to + populate the container status message on + both success and failure. FallbackToLogsOnError + will use the last chunk of container log + output if the termination message file is + empty and the container exited with an error. + The log output is limited to 2048 bytes + or 80 lines, whichever is smaller. Defaults + to File. Cannot be updated. + type: string + tty: + description: Whether this container should + allocate a TTY for itself, also requires + 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of + block devices to be used by the container. + items: + description: volumeDevice describes a mapping + of a raw block device within a container. + properties: + devicePath: + description: devicePath is the path + inside of the container that the device + will be mapped to. + type: string + name: + description: name must match the name + of a persistentVolumeClaim in the + pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the + container's filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting + of a Volume within a container. + properties: + mountPath: + description: Path within the container + at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines + how mounts are propagated from the + host to container and the other way + around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name + of a Volume. + type: string + readOnly: + description: Mounted read-only if true, + read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults to "" + (volume's root). + type: string + subPathExpr: + description: Expanded path within the + volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) are + expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might be configured + in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + nodeName: + description: NodeName is a request to schedule this + pod onto a specific node. If it is non-empty, + the scheduler simply schedules this pod onto that + node, assuming that it fits resource requirements. + type: string + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must + be true for the pod to fit on a node. Selector + which must match a node''s labels for the pod + to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + x-kubernetes-map-type: atomic + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Overhead represents the resource overhead + associated with running a pod for a given RuntimeClass. + This field will be autopopulated at admission + time by the RuntimeClass admission controller. + If the RuntimeClass admission controller is enabled, + overhead must not be set in Pod create requests. + The RuntimeClass admission controller will reject + Pod create requests which have the overhead already + set. If RuntimeClass is configured and selected + in the PodSpec, Overhead will be set to the value + defined in the corresponding RuntimeClass, otherwise + it will remain unset and treated as zero. More + info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md + This field is beta-level as of Kubernetes v1.18, + and is only honored by servers that enable the + PodOverhead feature.' + type: object + preemptionPolicy: + description: PreemptionPolicy is the Policy for + preempting pods with lower priority. One of Never, + PreemptLowerPriority. Defaults to PreemptLowerPriority + if unset. This field is beta-level, gated by the + NonPreemptingPriority feature-gate. + type: string + priority: + description: The priority value. Various system + components use this field to find the priority + of the pod. When Priority Admission Controller + is enabled, it prevents users from setting this + field. The admission controller populates this + field from PriorityClassName. The higher the value, + the higher the priority. + format: int32 + type: integer + priorityClassName: + description: If specified, indicates the pod's priority. + "system-node-critical" and "system-cluster-critical" + are two special keywords which indicate the highest + priorities with the former being the highest priority. + Any other name must be defined by creating a PriorityClass + object with that name. If not specified, the pod + priority will be default or zero if there is no + default. + type: string + readinessGates: + description: 'If specified, all readiness gates + will be evaluated for pod readiness. A pod is + ready when all its containers are ready AND all + conditions specified in the readiness gates have + status equal to "True" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates' + items: + description: PodReadinessGate contains the reference + to a pod condition + properties: + conditionType: + description: ConditionType refers to a condition + in the pod's condition list with matching + type. + type: string + required: + - conditionType + type: object + type: array + restartPolicy: + description: 'Restart policy for all containers + within the pod. One of Always, OnFailure, Never. + Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' + type: string + runtimeClassName: + description: 'RuntimeClassName refers to a RuntimeClass + object in the node.k8s.io group, which should + be used to run this pod. If no RuntimeClass resource + matches the named class, the pod will not be run. + If unset or empty, the "legacy" RuntimeClass will + be used, which is an implicit class with an empty + definition that uses the default runtime handler. + More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class + This is a beta feature as of Kubernetes v1.14.' + type: string + schedulerName: + description: If specified, the pod will be dispatched + by specified scheduler. If not specified, the + pod will be dispatched by default scheduler. + type: string + securityContext: + description: 'SecurityContext holds pod-level security + attributes and common container settings. Optional: + Defaults to empty. See type description for default + values of each field.' + properties: + fsGroup: + description: "A special supplemental group that + applies to all containers in a pod. Some volume + types allow the Kubelet to change the ownership + of that volume to be owned by the pod: \n + 1. The owning GID will be the FSGroup 2. The + setgid bit is set (new files created in the + volume will be owned by FSGroup) 3. The permission + bits are OR'd with rw-rw---- \n If unset, + the Kubelet will not modify the ownership + and permissions of any volume." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior + of changing ownership and permission of the + volume before being exposed inside Pod. This + field will only apply to volume types which + support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume + types such as: secret, configmaps and emptydir. + Valid values are "OnRootMismatch" and "Always". + If not specified, "Always" is used.' + type: string + runAsGroup: + description: The GID to run the entrypoint of + the container process. Uses runtime default + if unset. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence for that container. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must + run as a non-root user. If true, the Kubelet + will validate the image at runtime to ensure + that it does not run as UID 0 (root) and fail + to start the container if it does. If unset + or false, no such validation will be performed. + May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of + the container process. Defaults to user specified + in image metadata if unspecified. May also + be set in SecurityContext. If set in both + SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence + for that container. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied + to all containers. If unspecified, the container + runtime will allocate a random SELinux context + for each container. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence for that container. + properties: + level: + description: Level is SELinux level label + that applies to the container. + type: string + role: + description: Role is a SELinux role label + that applies to the container. + type: string + type: + description: Type is a SELinux type label + that applies to the container. + type: string + user: + description: User is a SELinux user label + that applies to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the + containers in this pod. + properties: + localhostProfile: + description: localhostProfile indicates + a profile defined in a file on the node + should be used. The profile must be preconfigured + on the node to work. Must be a descending + path, relative to the kubelet's configured + seccomp profile location. Must only be + set if type is "Localhost". + type: string + type: + description: "type indicates which kind + of seccomp profile will be applied. Valid + options are: \n Localhost - a profile + defined in a file on the node should be + used. RuntimeDefault - the container runtime + default profile should be used. Unconfined + - no profile should be applied." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the + first process run in each container, in addition + to the container's primary GID. If unspecified, + no groups will be added to any container. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced + sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail + to launch. + items: + description: Sysctl defines a kernel parameter + to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied + to all containers. If unspecified, the options + within a container's SecurityContext will + be used. If set in both SecurityContext and + PodSecurityContext, the value specified in + SecurityContext takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where + the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential + spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the + name of the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a + container should be run as a 'Host Process' + container. This field is alpha-level and + will only be honored by components that + enable the WindowsHostProcessContainers + feature flag. Setting this field without + the feature flag will result in errors + when validating the Pod. All of a Pod's + containers must have the same effective + HostProcess value (it is not allowed to + have a mix of HostProcess containers and + non-HostProcess containers). In addition, + if HostProcess is true then HostNetwork + must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to + run the entrypoint of the container process. + Defaults to the user specified in image + metadata if unspecified. May also be set + in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + serviceAccount: + description: 'DeprecatedServiceAccount is a depreciated + alias for ServiceAccountName. Deprecated: Use + serviceAccountName instead.' + type: string + serviceAccountName: + description: 'ServiceAccountName is the name of + the ServiceAccount to use to run this pod. More + info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' + type: string + setHostnameAsFQDN: + description: If true the pod's hostname will be + configured as the pod's FQDN, rather than the + leaf name (the default). In Linux containers, + this means setting the FQDN in the hostname field + of the kernel (the nodename field of struct utsname). + In Windows containers, this means setting the + registry value of hostname for the registry key + HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters + to FQDN. If a pod does not have FQDN, this has + no effect. Default to false. + type: boolean + shareProcessNamespace: + description: 'Share a single process namespace between + all of the containers in a pod. When this is set + containers will be able to view and signal processes + from other containers in the same pod, and the + first process in each container will not be assigned + PID 1. HostPID and ShareProcessNamespace cannot + both be set. Optional: Default to false.' + type: boolean + subdomain: + description: If specified, the fully qualified Pod + hostname will be "...svc.". If not specified, + the pod will not have a domainname at all. + type: string + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod + needs to terminate gracefully. May be decreased + in delete request. Value must be non-negative + integer. The value zero indicates stop immediately + via the kill signal (no opportunity to shut down). + If this value is nil, the default grace period + will be used instead. The grace period is the + duration in seconds after the processes running + in the pod are sent a termination signal and the + time when the processes are forcibly halted with + a kill signal. Set this value longer than the + expected cleanup time for your process. Defaults + to 30 seconds. + format: int64 + type: integer + tolerations: + description: If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached + to tolerates any taint that matches the triple + using the matching operator + . + properties: + effect: + description: Effect indicates the taint effect + to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, + PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the + toleration applies to. Empty means match + all taint keys. If the key is empty, operator + must be Exists; this combination means to + match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship + to the value. Valid operators are Exists + and Equal. Defaults to Equal. Exists is + equivalent to wildcard for value, so that + a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents + the period of time the toleration (which + must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By + default, it is not set, which means tolerate + the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict + immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the + toleration matches to. If the operator is + Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints describes + how a group of pods ought to spread across topology + domains. Scheduler will schedule pods in a way + which abides by the constraints. All topologySpreadConstraints + are ANDed. + items: + description: TopologySpreadConstraint specifies + how to spread matching pods among the given + topology. + properties: + labelSelector: + description: LabelSelector is used to find + matching pods. Pods that match this label + selector are counted to determine the number + of pods in their corresponding topology + domain. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree + to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, + it is the maximum permitted difference between + the number of matching pods in the target + topology and the global minimum. For example, + in a 3-zone cluster, MaxSkew is set to 1, + and pods with the same labelSelector spread + as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can only + be scheduled to zone3 to become 1/1/1; scheduling + it onto zone1(zone2) would make the ActualSkew(2-0) + on zone1(zone2) violate MaxSkew(1). - if + MaxSkew is 2, incoming pod can be scheduled + onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, + it is used to give higher precedence to + topologies that satisfy it. It''s a required + field. Default value is 1 and 0 is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node + labels. Nodes that have a label with this + key and identical values are considered + to be in the same topology. We consider + each as a "bucket", and try + to put balanced number of pods into each + bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates + how to deal with a pod if it doesn''t satisfy + the spread constraint. - DoNotSchedule (default) + tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to + schedule the pod in any location, but + giving higher precedence to topologies that + would help reduce the skew. A constraint + is considered "Unsatisfiable" for an incoming + pod if and only if every possible node assigment + for that pod would violate "MaxSkew" on + some topology. For example, in a 3-zone + cluster, MaxSkew is set to 1, and pods with + the same labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, + incoming pod can only be scheduled to zone2(zone3) + to become 3/2/1(3/1/2) as ActualSkew(2-1) + on zone2(zone3) satisfies MaxSkew(1). In + other words, the cluster can still be imbalanced, + but scheduler won''t make it *more* imbalanced. + It''s a required field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + description: 'List of volumes that can be mounted + by containers belonging to the pod. More info: + https://kubernetes.io/docs/concepts/storage/volumes' + items: + description: Volume represents a named volume + in a pod that may be accessed by any container + in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents + an AWS Disk resource that is attached to + a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume + that you want to mount. Tip: Ensure + that the filesystem type is supported + by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + partition: + description: 'The partition in the volume + that you want to mount. If omitted, + the default is to mount by volume name. + Examples: For volume /dev/sda1, you + specify the partition as "1". Similarly, + the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force + and set the ReadOnly property in VolumeMounts + to "true". If omitted, the default is + "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent + disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure + Data Disk mount on the host and bind mount + to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk + in the blob storage + type: string + diskURI: + description: The URI the data disk in + the blob storage + type: string + fsType: + description: Filesystem type to mount. + Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: + multiple blob disks per storage account Dedicated: + single blob disk per storage account Managed: + azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure + File Service mount on the host and bind + mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount + on the host that shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a + collection of Ceph monitors More info: + https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted + root, rather than the full Ceph tree, + default is /' + type: string + readOnly: + description: 'Optional: Defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is + the path to key ring for User, default + is /etc/ceph/user.secret More info: + https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference + to the authentication secret for User, + default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados + user name, default is admin More info: + https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume + attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. + Must be a filesystem type supported + by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: + https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret + object containing parameters used to + connect to OpenStack.' + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify + the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits used + to set permissions on created files + by default. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. + Directories within the path are not + affected by this setting. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the + volume as a file whose name is the key + and content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the ConfigMap, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or + start with '..'. + items: + description: Maps a string key to a + path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of + the file to map the key to. May + not be an absolute path. May not + contain the path element '..'. + May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) + represents ephemeral storage that is handled + by certain external CSI drivers (Beta feature). + properties: + driver: + description: Driver is the name of the + CSI driver that handles this volume. + Consult with your admin for the correct + name as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. + Ex. "ext4", "xfs", "ntfs". If not provided, + the empty value is passed to the associated + CSI driver which will determine the + default filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a + reference to the secret object containing + sensitive information to pass to the + CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This + field is optional, and may be empty + if no secret is required. If the secret + object contains more than one secret, + all secret references are passed. + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific + properties that are passed to the CSI + driver. Consult your driver's documentation + for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward + API about the pod that should populate this + volume + properties: + defaultMode: + description: 'Optional: mode bits to use + on created files by default. Must be + a Optional: mode bits used to set permissions + on created files by default. Must be + an octal value between 0000 and 0777 + or a decimal value between 0 and 511. + YAML accepts both octal and decimal + values, JSON requires decimal values + for mode bits. Defaults to 0644. Directories + within the path are not affected by + this setting. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward + API volume file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects + a field of the pod: only annotations, + labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the + schema the FieldPath is written + in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field + to select in the specified + API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits + used to set permissions on this + file, must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file + to be created. Must not be absolute + or contain the ''..'' path. Must + be utf-8 encoded. The first item + of the relative path must not + start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, requests.cpu and + requests.memory) are currently + supported.' + properties: + containerName: + description: 'Container name: + required for volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary + directory that shares a pod''s lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium + should back this directory. The default + is "" which means to use the node''s + default medium. Must be an empty string + (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage + required for this EmptyDir volume. The + size limit is also applicable for memory + medium. The maximum usage on memory + medium EmptyDir would be the minimum + value between the SizeLimit specified + here and the sum of memory limits of + all containers in a pod. The default + is nil which means that the limit is + undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "Ephemeral represents a volume + that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod + that defines it - it will be created before + the pod starts, and deleted when the pod + is removed. \n Use this if: a) the volume + is only needed while the pod runs, b) features + of normal volumes like restoring from snapshot + or capacity tracking are needed, c) the + storage driver is specified through a storage + class, and d) the storage driver supports + dynamic volume provisioning through a + PersistentVolumeClaim (see EphemeralVolumeSource + for more information on the connection + between this volume type and PersistentVolumeClaim). + \n Use PersistentVolumeClaim or one of the + vendor-specific APIs for volumes that persist + for longer than the lifecycle of an individual + pod. \n Use CSI for light-weight local ephemeral + volumes if the CSI driver is meant to be + used that way - see the documentation of + the driver for more information." + properties: + volumeClaimTemplate: + description: "Will be used to create a + stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource + is embedded will be the owner of the + PVC, i.e. the PVC will be deleted together + with the pod. The name of the PVC will + be `-` where + `` is the name from the + `PodSpec.Volumes` array entry. Pod validation + will reject the pod if the concatenated + name is not valid for a PVC (for example, + too long). \n An existing PVC with that + name that is not owned by the pod will + *not* be used for the pod to avoid using + an unrelated volume by mistake. Starting + the pod is then blocked until the unrelated + PVC is removed. If such a pre-created + PVC is meant to be used by the pod, + the PVC has to updated with an owner + reference to the pod once the pod exists. + Normally this should not be necessary, + but it may be useful when manually reconstructing + a broken cluster. \n This field is read-only + and no changes will be made by Kubernetes + to the PVC after it has been created." + properties: + metadata: + description: May contain labels and + annotations that will be copied + into the PVC when creating it. No + other fields are allowed and will + be rejected during validation. + type: object + spec: + description: The specification for + the PersistentVolumeClaim. The entire + content is copied unchanged into + the PVC that gets created from this + template. The same fields as in + a PersistentVolumeClaim are also + valid here. + properties: + accessModes: + description: 'AccessModes contains + the desired access modes the + volume should have. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be + used to specify either: * An + existing VolumeSnapshot object + (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external + controller can support the specified + data source, it will create + a new volume based on the contents + of the specified data source. + If the AnyVolumeDataSource feature + gate is enabled, this field + will always have the same contents + as the DataSourceRef field.' + properties: + apiGroup: + description: APIGroup is the + group for the resource being + referenced. If APIGroup + is not specified, the specified + Kind must be in the core + API group. For any other + third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type + of resource being referenced + type: string + name: + description: Name is the name + of resource being referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'Specifies the object + from which to populate the volume + with data, if a non-empty volume + is desired. This may be any + local object from a non-empty + API group (non core object) + or a PersistentVolumeClaim object. + When this field is specified, + volume binding will only succeed + if the type of the specified + object matches some installed + volume populator or dynamic + provisioner. This field will + replace the functionality of + the DataSource field and as + such if both fields are non-empty, + they must have the same value. + For backwards compatibility, + both fields (DataSource and + DataSourceRef) will be set to + the same value automatically + if one of them is empty and + the other is non-empty. There + are two important differences + between DataSource and DataSourceRef: + * While DataSource only allows + two specific types of objects, + DataSourceRef allows any non-core + object, as well as PersistentVolumeClaim + objects.' + properties: + apiGroup: + description: APIGroup is the + group for the resource being + referenced. If APIGroup + is not specified, the specified + Kind must be in the core + API group. For any other + third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type + of resource being referenced + type: string + name: + description: Name is the name + of resource being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents + the minimum resources the volume + should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes + the maximum amount of compute + resources allowed. More + info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes + the minimum amount of compute + resources required. If Requests + is omitted for a container, + it defaults to Limits if + that is explicitly specified, + otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: A label query over + volumes to consider for binding. + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key and + values. + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to a + set of values. Valid + operators are In, + NotIn, Exists and + DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, the + values array must + be non-empty. If the + operator is Exists + or DoesNotExist, the + values array must + be empty. This array + is replaced during + a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is + a map of {key,value} pairs. + A single {key,value} in + the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", + the operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass + required by the claim. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines + what type of volume is required + by the claim. Value of Filesystem + is implied when not included + in claim spec. + type: string + volumeName: + description: VolumeName is the + binding reference to the PersistentVolume + backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: FC represents a Fibre Channel + resource that is attached to a kubelet's + host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. + Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. TODO: how + do we prevent errors in the filesystem + from compromising the machine' + type: string + lun: + description: 'Optional: FC target lun + number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world + wide identifiers (wwids) Either wwids + or combination of targetWWNs and lun + must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic + volume resource that is provisioned/attached + using an exec based plugin. + properties: + driver: + description: Driver is the name of the + driver to use for this volume. + type: string + fsType: + description: Filesystem type to mount. + Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem + depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command + options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false + (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference + to the secret object containing sensitive + information to pass to the plugin scripts. + This may be empty if no secret object + is specified. If the secret object contains + more than one secret, all secrets are + passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker + volume attached to a kubelet's host machine. + This depends on the Flocker control service + being running + properties: + datasetName: + description: Name of the dataset stored + as metadata -> name on the dataset for + Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents + a GCE Disk resource that is attached to + a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume + that you want to mount. Tip: Ensure + that the filesystem type is supported + by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + partition: + description: 'The partition in the volume + that you want to mount. If omitted, + the default is to mount by volume name. + Examples: For volume /dev/sda1, you + specify the partition as "1". Similarly, + the volume partition for /dev/sda is + "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource + in GCE. Used to identify the disk in + GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force + the ReadOnly setting in VolumeMounts. + Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository + at a particular revision. DEPRECATED: GitRepo + is deprecated. To provision a container + with a git repo, mount an EmptyDir into + an InitContainer that clones the repo using + git, then mount the EmptyDir into the Pod''s + container.' + properties: + directory: + description: Target directory name. Must + not contain or start with '..'. If + '.' is supplied, the volume directory + will be the git repository. Otherwise, + if specified, the volume will contain + the git repository in the subdirectory + with the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs + mount on the host that shares a pod''s lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint + name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume + path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force + the Glusterfs volume to be mounted with + read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing + file or directory on the host machine that + is directly exposed to the container. This + is generally used for system agents or other + privileged things that are allowed to see + the host machine. Most containers will NOT + need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who + can use host directory mounts and who can/can + not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on + the host. If the path is a symlink, + it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume + Defaults to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk + resource that is attached to a kubelet''s + host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery + CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session + CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume + that you want to mount. Tip: Ensure + that the filesystem type is supported + by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface + : will be + created for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that + uses an iSCSI transport. Defaults to + 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. + The portal is either an IP or ip_addr:port + if the port is other than default (typically + TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force + the ReadOnly setting in VolumeMounts. + Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The + Portal is either an IP or ip_addr:port + if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL + and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount + on the host that shares a pod''s lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by + the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force + the NFS export to be mounted with read-only + permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or + IP address of the NFS server. More info: + https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource + represents a reference to a PersistentVolumeClaim + in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of + a PersistentVolumeClaim in the same + namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting + in VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents + a PhotonController persistent disk attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. + Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon + Controller persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx + volume attached and mounted on kubelets + host machine + properties: + fsType: + description: FSType represents the filesystem + type to mount Must be a filesystem type + supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies + a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources + secrets, configmaps, and downward API + properties: + defaultMode: + description: Mode bits used to set permissions + on created files by default. Must be + an octal value between 0000 and 0777 + or a decimal value between 0 and 511. + YAML accepts both octal and decimal + values, JSON requires decimal values + for mode bits. Directories within the + path are not affected by this setting. + This might be in conflict with other + options that affect the file mode, like + fsGroup, and the result can be other + mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be + projected along with other supported + volume types + properties: + configMap: + description: information about the + configMap data to project + properties: + items: + description: If unspecified, + each key-value pair in the + Data field of the referenced + ConfigMap will be projected + into the volume as a file + whose name is the key and + content is the value. If specified, + the listed keys will be projected + into the specified paths, + and unlisted keys will not + be present. If a key is specified + which is not present in the + ConfigMap, the volume setup + will error unless it is marked + optional. Paths must be relative + and may not contain the '..' + path or start with '..'. + items: + description: Maps a string + key to a path within a volume. + properties: + key: + description: The key to + project. + type: string + mode: + description: 'Optional: + mode bits used to set + permissions on this + file. Must be an octal + value between 0000 and + 0777 or a decimal value + between 0 and 511. YAML + accepts both octal and + decimal values, JSON + requires decimal values + for mode bits. If not + specified, the volume + defaultMode will be + used. This might be + in conflict with other + options that affect + the file mode, like + fsGroup, and the result + can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative + path of the file to + map the key to. May + not be an absolute path. + May not contain the + path element '..'. May + not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its keys + must be defined + type: boolean + type: object + downwardAPI: + description: information about the + downwardAPI data to project + properties: + items: + description: Items is a list + of DownwardAPIVolume file + items: + description: DownwardAPIVolumeFile + represents information to + create the file containing + the pod field + properties: + fieldRef: + description: 'Required: + Selects a field of the + pod: only annotations, + labels, name and namespace + are supported.' + properties: + apiVersion: + description: Version + of the schema the + FieldPath is written + in terms of, defaults + to "v1". + type: string + fieldPath: + description: Path + of the field to + select in the specified + API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: + mode bits used to set + permissions on this + file, must be an octal + value between 0000 and + 0777 or a decimal value + between 0 and 511. YAML + accepts both octal and + decimal values, JSON + requires decimal values + for mode bits. If not + specified, the volume + defaultMode will be + used. This might be + in conflict with other + options that affect + the file mode, like + fsGroup, and the result + can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: + Path is the relative + path name of the file + to be created. Must + not be absolute or contain + the ''..'' path. Must + be utf-8 encoded. The + first item of the relative + path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects + a resource of the container: + only resources limits + and requests (limits.cpu, + limits.memory, requests.cpu + and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container + name: required for + volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies + the output format + of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: + resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the + secret data to project + properties: + items: + description: If unspecified, + each key-value pair in the + Data field of the referenced + Secret will be projected into + the volume as a file whose + name is the key and content + is the value. If specified, + the listed keys will be projected + into the specified paths, + and unlisted keys will not + be present. If a key is specified + which is not present in the + Secret, the volume setup will + error unless it is marked + optional. Paths must be relative + and may not contain the '..' + path or start with '..'. + items: + description: Maps a string + key to a path within a volume. + properties: + key: + description: The key to + project. + type: string + mode: + description: 'Optional: + mode bits used to set + permissions on this + file. Must be an octal + value between 0000 and + 0777 or a decimal value + between 0 and 511. YAML + accepts both octal and + decimal values, JSON + requires decimal values + for mode bits. If not + specified, the volume + defaultMode will be + used. This might be + in conflict with other + options that affect + the file mode, like + fsGroup, and the result + can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative + path of the file to + map the key to. May + not be an absolute path. + May not contain the + path element '..'. May + not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key must + be defined + type: boolean + type: object + serviceAccountToken: + description: information about the + serviceAccountToken data to project + properties: + audience: + description: Audience is the + intended audience of the token. + A recipient of a token must + identify itself with an identifier + specified in the audience + of the token, and otherwise + should reject the token. The + audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds + is the requested duration + of validity of the service + account token. As the token + approaches expiration, the + kubelet volume plugin will + proactively rotate the service + account token. The kubelet + will start trying to rotate + the token if the token is + older than 80 percent of its + time to live or if the token + is older than 24 hours.Defaults + to 1 hour and must be at least + 10 minutes. + format: int64 + type: integer + path: + description: Path is the path + relative to the mount point + of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: Quobyte represents a Quobyte + mount on the host that shares a pod's lifetime + properties: + group: + description: Group to map volume access + to Default is no group + type: string + readOnly: + description: ReadOnly here will force + the Quobyte volume to be mounted with + read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single + or multiple Quobyte Registry services + specified as a string as host:port pair + (multiple entries are separated with + commas) which acts as the central registry + for volumes + type: string + tenant: + description: Tenant owning the given Quobyte + volume in the Backend Used with dynamically + provisioned Quobyte volumes, value is + set by the plugin + type: string + user: + description: User to map volume access + to Defaults to serivceaccount user + type: string + volume: + description: Volume is a string that references + an already created Quobyte volume by + name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block + Device mount on the host that shares a pod''s + lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume + that you want to mount. Tip: Ensure + that the filesystem type is supported + by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + image: + description: 'The rados image name. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key + ring for RBDUser. Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default + is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force + the ReadOnly setting in VolumeMounts. + Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the + authentication secret for RBDUser. If + provided overrides keyring. Default + is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default + is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO + persistent volume attached and mounted on + Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. + Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection + Domain for the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the + secret for ScaleIO user and other sensitive + information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL + communication with Gateway, default + false + type: boolean + storageMode: + description: Indicates whether the storage + for a volume should be ThickProvisioned + or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool + associated with the protection domain. + type: string + system: + description: The name of the storage system + as configured in ScaleIO. + type: string + volumeName: + description: The name of a volume already + created in the ScaleIO system that is + associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that + should populate this volume. More info: + https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits used + to set permissions on created files + by default. Must be an octal value between + 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and + decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. + Directories within the path are not + affected by this setting. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the volume + as a file whose name is the key and + content is the value. If specified, + the listed keys will be projected into + the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the Secret, + the volume setup will error unless it + is marked optional. Paths must be relative + and may not contain the '..' path or + start with '..'. + items: + description: Maps a string key to a + path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + used to set permissions on this + file. Must be an octal value between + 0000 and 0777 or a decimal value + between 0 and 511. YAML accepts + both octal and decimal values, + JSON requires decimal values for + mode bits. If not specified, the + volume defaultMode will be used. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of + the file to map the key to. May + not be an absolute path. May not + contain the path element '..'. + May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret + or its keys must be defined + type: boolean + secretName: + description: 'Name of the secret in the + pod''s namespace to use. More info: + https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS + volume attached and mounted on Kubernetes + nodes. + properties: + fsType: + description: Filesystem type to mount. + Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret + to use for obtaining the StorageOS API + credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable + name of the StorageOS volume. Volume + names are only unique within a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies + the scope of the volume within StorageOS. If + no namespace is specified then the Pod's + namespace will be used. This allows + the Kubernetes name scoping to be mirrored + within StorageOS for tighter integration. + Set VolumeName to any name to override + the default behaviour. Set to "default" + if you are not using namespaces within + StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere + volume attached and mounted on kubelets + host machine + properties: + fsType: + description: Filesystem type to mount. + Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management + (SPBM) profile ID associated with the + StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management + (SPBM) profile name. + type: string + volumePath: + description: Path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + required: + - containers + type: object + type: object + required: + - selector + - template + type: object + required: + - spec + type: object + type: array + additionalServices: + items: + description: DeploymentTemplateSpec defines the pool template of + Deployment. + properties: + metadata: + type: object + spec: + description: ServiceSpec describes the attributes that a user + creates on a service. + properties: + allocateLoadBalancerNodePorts: + description: allocateLoadBalancerNodePorts defines if NodePorts + will be automatically allocated for services with type + LoadBalancer. Default is "true". It may be set to "false" + if the cluster load-balancer does not rely on NodePorts. If + the caller requests specific NodePorts (by specifying + a value), those requests will be respected, regardless + of this field. This field may only be set for services + with type LoadBalancer and will be cleared if the type + is changed to any other type. This field is beta-level + and is only honored by servers that enable the ServiceLBNodePortControl + feature. + type: boolean + clusterIP: + description: clusterIP is the IP address of the service + and is usually assigned randomly. If an address is specified + manually, is in-range (as per system configuration), and + is not in use, it will be allocated to the service; otherwise + creation of the service will fail. This field may not + be changed through updates unless the type field is also + being changed to ExternalName (which requires this field + to be blank) or the type field is being changed from ExternalName + (in which case this field may optionally be specified, + as describe above). Valid values are "None", empty string + (""), or a valid IP address. Setting this to "None" makes + a "headless service" (no virtual IP), which is useful + when direct endpoint connections are preferred and proxying + is not required. Only applies to types ClusterIP, NodePort, + and LoadBalancer. If this field is specified when creating + a Service of type ExternalName, creation will fail. This + field will be wiped when updating a Service to type ExternalName. + type: string + clusterIPs: + description: ClusterIPs is a list of IP addresses assigned + to this service, and are usually assigned randomly. If + an address is specified manually, is in-range (as per + system configuration), and is not in use, it will be allocated + to the service; otherwise creation of the service will + fail. This field may not be changed through updates unless + the type field is also being changed to ExternalName (which + requires this field to be empty) or the type field is + being changed from ExternalName (in which case this field + may optionally be specified, as describe above). Valid + values are "None", empty string (""), or a valid IP address. Setting + this to "None" makes a "headless service" (no virtual + IP), which is useful when direct endpoint connections + are preferred and proxying is not required. Only applies + to types ClusterIP, NodePort, and LoadBalancer. If this + field is specified when creating a Service of type ExternalName, + creation will fail. + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this + service. These IPs are not managed by Kubernetes. The + user is responsible for ensuring that traffic arrives + at a node with this IP. A common example is external + load-balancers that are not part of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that + discovery mechanisms will return as an alias for this + service (e.g. a DNS CNAME record). No proxying will be + involved. Must be a lowercase RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) + and requires `type` to be "ExternalName". + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service + desires to route external traffic to node-local or cluster-wide + endpoints. "Local" preserves the client source IP and + avoids a second hop for LoadBalancer and Nodeport type + services, but risks potentially imbalanced traffic spreading. + "Cluster" obscures the client source IP and may cause + a second hop to another node, but should have good overall + load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck + nodePort for the service. This only applies when type + is set to LoadBalancer and externalTrafficPolicy is set + to Local. If a value is specified, is in-range, and is + not in use, it will be used. If not specified, a value + will be automatically allocated. External systems (e.g. + load-balancers) can use this port to determine if a given + node holds endpoints for this service or not. If this + field is specified when creating a Service which does + not need it, creation will fail. This field will be wiped + when updating a Service to no longer need it (e.g. changing + type). + format: int32 + type: integer + internalTrafficPolicy: + description: InternalTrafficPolicy specifies if the cluster + internal traffic should be routed to all endpoints or + node-local endpoints only. "Cluster" routes internal traffic + to a Service to all endpoints. "Local" routes traffic + to node-local endpoints only, traffic is dropped if no + node-local endpoints are ready. The default value is "Cluster". + type: string + ipFamilies: + description: "IPFamilies is a list of IP families (e.g. + IPv4, IPv6) assigned to this service, and is gated by + the \"IPv6DualStack\" feature gate. This field is usually + assigned automatically based on cluster configuration + and the ipFamilyPolicy field. If this field is specified + manually, the requested family is available in the cluster, + and ipFamilyPolicy allows it, it will be used; otherwise + creation of the service will fail. This field is conditionally + mutable: it allows for adding or removing a secondary + IP family, but it does not allow changing the primary + IP family of the Service. Valid values are \"IPv4\" and + \"IPv6\". This field only applies to Services of types + ClusterIP, NodePort, and LoadBalancer, and does apply + to \"headless\" services. This field will be wiped when + updating a Service to type ExternalName. \n This field + may hold a maximum of two entries (dual-stack families, + in either order). These families must correspond to the + values of the clusterIPs field, if specified." + items: + description: IPFamily represents the IP Family (IPv4 or + IPv6). This type is used to express the family of an + IP expressed by a type (e.g. service.spec.ipFamilies). + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + description: IPFamilyPolicy represents the dual-stack-ness + requested or required by this Service, and is gated by + the "IPv6DualStack" feature gate. If there is no value + provided, then this field will be set to SingleStack. + Services can be "SingleStack" (a single IP family), "PreferDualStack" + (two IP families on dual-stack configured clusters or + a single IP family on single-stack clusters), or "RequireDualStack" + (two IP families on dual-stack configured clusters, otherwise + fail). The ipFamilies and clusterIPs fields depend on + the value of this field. This field will be wiped when + updating a service to type ExternalName. + type: string + loadBalancerClass: + description: loadBalancerClass is the class of the load + balancer implementation this Service belongs to. If specified, + the value of this field must be a label-style identifier, + with an optional prefix, e.g. "internal-vip" or "example.com/internal-vip". + Unprefixed names are reserved for end-users. This field + can only be set when the Service type is 'LoadBalancer'. + If not set, the default load balancer implementation is + used, today this is typically done through the cloud provider + integration, but should apply for any default implementation. + If set, it is assumed that a load balancer implementation + is watching for Services with a matching class. Any default + load balancer implementation (e.g. cloud providers) should + ignore Services that set this field. This field can only + be set when creating or updating a Service to type 'LoadBalancer'. + Once set, it can not be changed. This field will be wiped + when a service is updated to a non 'LoadBalancer' type. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer + LoadBalancer will get created with the IP specified in + this field. This feature depends on whether the underlying + cloud-provider supports specifying the loadBalancerIP + when a load balancer is created. This field will be ignored + if the cloud-provider does not support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, + this will restrict traffic through the cloud-provider + load-balancer will be restricted to the specified client + IPs. This field will be ignored if the cloud-provider + does not support the feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this + service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + appProtocol: + description: The application protocol for this port. + This field follows standard Kubernetes label syntax. + Un-prefixed names are reserved for IANA standard + service names (as per RFC-6335 and http://www.iana.org/assignments/service-names). + Non-standard protocols should use prefixed names + such as mycompany.com/my-custom-protocol. + type: string + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field + in the EndpointPort. Optional if only one ServicePort + is defined on this service. + type: string + nodePort: + description: 'The port on each node on which this + service is exposed when type is NodePort or LoadBalancer. Usually + assigned by the system. If a value is specified, + in-range, and not in use it will be used, otherwise + the operation will fail. If not specified, a port + will be allocated if this Service requires one. If + this field is specified when creating a Service + which does not need it, creation will fail. This + field will be wiped when updating a Service to no + longer need it (e.g. changing type from NodePort + to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this + service. + format: int32 + type: integer + protocol: + default: TCP + description: The IP protocol for this port. Supports + "TCP", "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access + on the pods targeted by the service. Number must + be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this + is not specified, the value of the ''port'' field + is used (an identity map). This field is ignored + for services with clusterIP=None, and should be + omitted or set equal to the ''port'' field. More + info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + description: publishNotReadyAddresses indicates that any + agent which deals with endpoints for this Service should + disregard any indications of ready/not-ready. The primary + use case for setting this field is for a StatefulSet's + Headless Service to propagate SRV DNS records for its + Pods for the purpose of peer discovery. The Kubernetes + controllers that generate Endpoints and EndpointSlice + resources for Services interpret this to mean that all + endpoints are considered "ready" even if the Pods themselves + are not. Agents which consume only Kubernetes generated + endpoints through the Endpoints or EndpointSlice resources + can safely assume this behavior. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only + applies to types ClusterIP, NodePort, and LoadBalancer. + Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of + Client IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds + of ClientIP type session sticky time. The value + must be >0 && <=86400(for 1 day) if ServiceAffinity + == "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. + Defaults to ClusterIP. Valid options are ExternalName, + ClusterIP, NodePort, and LoadBalancer. "ClusterIP" allocates + a cluster-internal IP address for load-balancing to endpoints. + Endpoints are determined by the selector or if that is + not specified, by manual construction of an Endpoints + object or EndpointSlice objects. If clusterIP is "None", + no virtual IP is allocated and the endpoints are published + as a set of endpoints rather than a virtual IP. "NodePort" + builds on ClusterIP and allocates a port on every node + which routes to the same endpoints as the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the + same endpoints as the clusterIP. "ExternalName" aliases + this service to the specified externalName. Several other + fields do not apply to ExternalName services. More info: + https://kubernetes.' + type: string + type: object + required: + - spec + type: object + type: array + imageRegistry: + type: string + poolName: + type: string + serviceType: + description: Service Type string describes ingress methods for a service + type: string + version: + type: string + type: object + status: + description: PlatformAdminStatus defines the observed state of PlatformAdmin + properties: + conditions: + description: Current PlatformAdmin state + items: + description: PlatformAdminCondition describes current state of a + PlatformAdmin. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of in place set condition. + type: string + type: object + type: array + deploymentReadyReplicas: + format: int32 + type: integer + deploymentReplicas: + format: int32 + type: integer + initialized: + type: boolean + ready: + type: boolean + serviceReadyReplicas: + format: int32 + type: integer + serviceReplicas: + format: int32 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: The platformadmin ready status + jsonPath: .status.ready + name: READY + type: boolean + - description: The Ready Component. + jsonPath: .status.readyComponentNum + name: ReadyComponentNum + type: integer + - description: The Unready Component. + jsonPath: .status.unreadyComponentNum + name: UnreadyComponentNum + type: integer + name: v1alpha2 + schema: + openAPIV3Schema: + description: PlatformAdmin is the Schema for the samples API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PlatformAdminSpec defines the desired state of PlatformAdmin + properties: + components: + items: + description: Component defines the components of EdgeX + properties: + image: + type: string + name: + type: string + required: + - name + type: object + type: array + imageRegistry: + type: string + platform: + type: string + poolName: + type: string + security: + type: boolean + version: + type: string + type: object + status: + description: PlatformAdminStatus defines the observed state of PlatformAdmin + properties: + conditions: + description: Current PlatformAdmin state + items: + description: PlatformAdminCondition describes current state of a + PlatformAdmin. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of in place set condition. + type: string + type: object + type: array + initialized: + type: boolean + ready: + type: boolean + readyComponentNum: + format: int32 + type: integer + unreadyComponentNum: + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index 3dda7a3d1b4..78365265389 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -373,6 +373,84 @@ rules: - get - list - watch +- apiGroups: + - device.openyurt.io + resources: + - deviceprofiles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - device.openyurt.io + resources: + - deviceprofiles/finalizers + verbs: + - update +- apiGroups: + - device.openyurt.io + resources: + - deviceprofiles/status + verbs: + - get + - patch + - update +- apiGroups: + - device.openyurt.io + resources: + - devices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - device.openyurt.io + resources: + - devices/finalizers + verbs: + - update +- apiGroups: + - device.openyurt.io + resources: + - devices/status + verbs: + - get + - patch + - update +- apiGroups: + - device.openyurt.io + resources: + - deviceservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - device.openyurt.io + resources: + - deviceservices/finalizers + verbs: + - update +- apiGroups: + - device.openyurt.io + resources: + - deviceservices/status + verbs: + - get + - patch + - update - apiGroups: - discovery.k8s.io resources: diff --git a/cmd/yurt-iot-carrier/app/core.go b/cmd/yurt-iot-carrier/app/core.go new file mode 100644 index 00000000000..baecf802e30 --- /dev/null +++ b/cmd/yurt-iot-carrier/app/core.go @@ -0,0 +1,190 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 app + +import ( + "context" + "fmt" + "os" + + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + + "k8s.io/klog/v2" + "k8s.io/klog/v2/klogr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func NewCmdYurtIotCarrier(stopCh <-chan struct{}) *cobra.Command { + yurtDeviceControllerOptions := options.NewYurtIotCarrierOptions() + cmd := &cobra.Command{ + Use: "yurt-iot-carrier", + Short: "Launch yurt-iot-carrier", + Long: "Launch yurt-iot-carrier", + Run: func(cmd *cobra.Command, args []string) { + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + klog.V(1).Infof("FLAG: --%s=%q", flag.Name, flag.Value) + }) + if err := options.ValidateOptions(yurtDeviceControllerOptions); err != nil { + klog.Fatalf("validate options: %v", err) + } + Run(yurtDeviceControllerOptions, stopCh) + }, + } + + yurtDeviceControllerOptions.AddFlags(cmd.Flags()) + return cmd +} + +func Run(opts *options.YurtIotCarrierOptions, stopCh <-chan struct{}) { + ctrl.SetLogger(klogr.New()) + cfg := ctrl.GetConfigOrDie() + + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: opts.MetricsAddr, + HealthProbeBindAddress: opts.ProbeAddr, + LeaderElection: opts.EnableLeaderElection, + LeaderElectionID: "yurt-iot-carrier", + Namespace: opts.Namespace, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + // perform preflight check + setupLog.Info("[preflight] Running pre-flight checks") + if err := preflightCheck(mgr, opts); err != nil { + setupLog.Error(err, "failed to run pre-flight checks") + os.Exit(1) + } + + // register the field indexers + setupLog.Info("[preflight] Registering the field indexers") + if err := util.RegisterFieldIndexers(mgr.GetFieldIndexer()); err != nil { + setupLog.Error(err, "failed to register field indexers") + os.Exit(1) + } + + // get nodepool where device-controller run + if opts.Nodepool == "" { + opts.Nodepool, err = util.GetNodePool(mgr.GetConfig()) + if err != nil { + setupLog.Error(err, "failed to get the nodepool where device-controller run") + os.Exit(1) + } + } + + // setup the DeviceProfile Reconciler and Syncer + if err = (&controllers.DeviceProfileReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr, opts); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "DeviceProfile") + os.Exit(1) + } + dfs, err := controllers.NewDeviceProfileSyncer(mgr.GetClient(), opts) + if err != nil { + setupLog.Error(err, "unable to create syncer", "syncer", "DeviceProfile") + os.Exit(1) + } + err = mgr.Add(dfs.NewDeviceProfileSyncerRunnable()) + if err != nil { + setupLog.Error(err, "unable to create syncer runnable", "syncer", "DeviceProfile") + os.Exit(1) + } + + // setup the Device Reconciler and Syncer + if err = (&controllers.DeviceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr, opts); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Device") + os.Exit(1) + } + ds, err := controllers.NewDeviceSyncer(mgr.GetClient(), opts) + if err != nil { + setupLog.Error(err, "unable to create syncer", "controller", "Device") + os.Exit(1) + } + err = mgr.Add(ds.NewDeviceSyncerRunnable()) + if err != nil { + setupLog.Error(err, "unable to create syncer runnable", "syncer", "Device") + os.Exit(1) + } + + // setup the DeviceService Reconciler and Syncer + if err = (&controllers.DeviceServiceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr, opts); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "DeviceService") + os.Exit(1) + } + dss, err := controllers.NewDeviceServiceSyncer(mgr.GetClient(), opts) + if err != nil { + setupLog.Error(err, "unable to create syncer", "syncer", "DeviceService") + os.Exit(1) + } + err = mgr.Add(dss.NewDeviceServiceSyncerRunnable()) + if err != nil { + setupLog.Error(err, "unable to create syncer runnable", "syncer", "DeviceService") + os.Exit(1) + } + //+kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("health", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("check", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("[run controllers] Starting manager, acting on " + fmt.Sprintf("[NodePool: %s, Namespace: %s]", opts.Nodepool, opts.Namespace)) + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "failed to running manager") + os.Exit(1) + } +} + +func preflightCheck(mgr ctrl.Manager, opts *options.YurtIotCarrierOptions) error { + client, err := kubernetes.NewForConfig(mgr.GetConfig()) + if err != nil { + return err + } + if _, err := client.CoreV1().Namespaces().Get(context.TODO(), opts.Namespace, metav1.GetOptions{}); err != nil { + return err + } + return nil +} diff --git a/cmd/yurt-iot-carrier/app/options/options.go b/cmd/yurt-iot-carrier/app/options/options.go new file mode 100644 index 00000000000..73bf25115a4 --- /dev/null +++ b/cmd/yurt-iot-carrier/app/options/options.go @@ -0,0 +1,82 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 options + +import ( + "fmt" + "net" + + "github.com/spf13/pflag" +) + +// YurtIotCarrierOptions is the main settings for the yurt-iot-carrier +type YurtIotCarrierOptions struct { + MetricsAddr string + ProbeAddr string + EnableLeaderElection bool + Nodepool string + Namespace string + CoreDataAddr string + CoreMetadataAddr string + CoreCommandAddr string + EdgeSyncPeriod uint +} + +func NewYurtIotCarrierOptions() *YurtIotCarrierOptions { + return &YurtIotCarrierOptions{ + MetricsAddr: ":8080", + ProbeAddr: ":8080", + EnableLeaderElection: false, + Nodepool: "", + Namespace: "default", + CoreDataAddr: "edgex-core-data:59880", + CoreMetadataAddr: "edgex-core-metadata:59881", + CoreCommandAddr: "edgex-core-command:59882", + EdgeSyncPeriod: 5, + } +} + +func ValidateOptions(options *YurtIotCarrierOptions) error { + if err := ValidateEdgePlatformAddress(options); err != nil { + return err + } + return nil +} + +func (o *YurtIotCarrierOptions) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&o.MetricsAddr, "metrics-bind-address", o.MetricsAddr, "The address the metric endpoint binds to.") + fs.StringVar(&o.ProbeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + fs.BoolVar(&o.EnableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+"Enabling this will ensure there is only one active controller manager.") + fs.StringVar(&o.Nodepool, "nodepool", "", "The nodePool deviceController is deployed in.(just for debugging)") + fs.StringVar(&o.Namespace, "namespace", "default", "The cluster namespace for edge resources synchronization.") + fs.StringVar(&o.CoreDataAddr, "core-data-address", "edgex-core-data:59880", "The address of edge core-data service.") + fs.StringVar(&o.CoreMetadataAddr, "core-metadata-address", "edgex-core-metadata:59881", "The address of edge core-metadata service.") + fs.StringVar(&o.CoreCommandAddr, "core-command-address", "edgex-core-command:59882", "The address of edge core-command service.") + fs.UintVar(&o.EdgeSyncPeriod, "edge-sync-period", 5, "The period of the device management platform synchronizing the device status to the cloud.(in seconds,not less than 5 seconds)") +} + +func ValidateEdgePlatformAddress(options *YurtIotCarrierOptions) error { + addrs := []string{options.CoreDataAddr, options.CoreMetadataAddr, options.CoreCommandAddr} + for _, addr := range addrs { + if addr != "" { + if _, _, err := net.SplitHostPort(addr); err != nil { + return fmt.Errorf("invalid address: %s", err) + } + } + } + return nil +} diff --git a/cmd/yurt-iot-carrier/yurt-iot-carrier.go b/cmd/yurt-iot-carrier/yurt-iot-carrier.go new file mode 100644 index 00000000000..0a0696515fa --- /dev/null +++ b/cmd/yurt-iot-carrier/yurt-iot-carrier.go @@ -0,0 +1,43 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 main + +import ( + "flag" + "math/rand" + "time" + + "k8s.io/apimachinery/pkg/util/wait" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "k8s.io/klog/v2" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app" +) + +func main() { + rand.Seed(time.Now().UnixNano()) + klog.InitFlags(nil) + defer klog.Flush() + + cmd := app.NewCmdYurtIotCarrier(wait.NeverStop) + cmd.Flags().AddGoFlagSet(flag.CommandLine) + if err := cmd.Execute(); err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod index f79da3cf042..b875d1c12f9 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,14 @@ go 1.18 require ( github.com/aliyun/alibaba-cloud-sdk-go v1.62.156 github.com/davecgh/go-spew v1.1.1 + github.com/edgexfoundry/go-mod-core-contracts/v2 v2.3.0 + github.com/go-resty/resty/v2 v2.4.0 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/hashicorp/go-version v1.6.0 + github.com/jarcoal/httpmock v1.3.0 + github.com/onsi/ginkgo v1.16.4 github.com/onsi/ginkgo/v2 v2.1.4 github.com/onsi/gomega v1.19.0 github.com/opencontainers/selinux v1.11.0 @@ -24,6 +28,7 @@ require ( go.etcd.io/etcd/api/v3 v3.5.0 go.etcd.io/etcd/client/pkg/v3 v3.5.0 go.etcd.io/etcd/client/v3 v3.5.0 + golang.org/x/net v0.9.0 golang.org/x/sys v0.10.0 google.golang.org/grpc v1.56.2 gopkg.in/cheggaaa/pb.v1 v1.0.28 @@ -86,10 +91,14 @@ require ( github.com/felixge/httpsnoop v1.0.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fvbommel/sortorder v1.0.1 // indirect + github.com/fxamacker/cbor/v2 v2.4.0 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.11.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -103,6 +112,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.7 // indirect @@ -113,6 +123,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/nxadm/tail v1.4.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/runc v1.1.5 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect @@ -124,6 +135,7 @@ require ( github.com/spf13/afero v1.6.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect + github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/contrib v0.20.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 // indirect @@ -139,7 +151,6 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.5.0 // indirect - golang.org/x/net v0.9.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/term v0.7.0 // indirect @@ -152,6 +163,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.22.2 // indirect k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect diff --git a/go.sum b/go.sum index 4c71d04330b..c77bd68f7d3 100644 --- a/go.sum +++ b/go.sum @@ -175,6 +175,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/edgexfoundry/go-mod-core-contracts/v2 v2.3.0 h1:8Svk1HTehXEgwxgyA4muVhSkP3D9n1q+oSHI3B1Ac90= +github.com/edgexfoundry/go-mod-core-contracts/v2 v2.3.0/go.mod h1:4/e61acxVkhQWCTjQ4XcHVJDnrMDloFsZZB1B6STCRw= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -214,6 +216,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= +github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -245,6 +249,16 @@ github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfT github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-resty/resty/v2 v2.4.0 h1:s6TItTLejEI+2mn98oijC5w/Rk2YU+OA6x0mnZN6r6k= +github.com/go-resty/resty/v2 v2.4.0/go.mod h1:B88+xCTEwvfD94NOuE6GS1wMlnoKNY8eEiNizfNwOwA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -385,6 +399,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= +github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= +github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -422,6 +438,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -429,6 +446,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubernetes/kubernetes v1.22.3 h1:0gYnqsr5nZiAO+iDkEU7RJ6Ne2CMyoinJXVm5qVSTiE= github.com/kubernetes/kubernetes v1.22.3/go.mod h1:Snea7fgIObGgHmLbUJ3OgjGEr5bjj16iEdp5oHS6eS8= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= @@ -452,6 +471,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= @@ -543,6 +563,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -591,6 +612,8 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= @@ -676,6 +699,8 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3C github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= @@ -756,6 +781,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -837,6 +863,7 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -914,8 +941,10 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -936,6 +965,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1086,6 +1116,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/hack/dockerfiles/build/Dockerfile.yurt-iot-carrier b/hack/dockerfiles/build/Dockerfile.yurt-iot-carrier new file mode 100644 index 00000000000..340eb4f3492 --- /dev/null +++ b/hack/dockerfiles/build/Dockerfile.yurt-iot-carrier @@ -0,0 +1,8 @@ +# multi-arch image building for yurt-iot-carrier + +FROM --platform=${TARGETPLATFORM} alpine:3.17 +ARG TARGETOS TARGETARCH MIRROR_REPO +RUN if [ ! -z "${MIRROR_REPO+x}" ]; then sed -i "s/dl-cdn.alpinelinux.org/${MIRROR_REPO}/g" /etc/apk/repositories; fi && \ + apk add ca-certificates bash libc6-compat iptables ip6tables && update-ca-certificates && rm /var/cache/apk/* +COPY ./_output/local/bin/${TARGETOS}/${TARGETARCH}/yurt-iot-carrier /usr/local/bin/yurt-iot-carrier +ENTRYPOINT ["/usr/local/bin/yurt-iot-carrier"] \ No newline at end of file diff --git a/hack/make-rules/image_build.sh b/hack/make-rules/image_build.sh index b069cfcbc0b..c94e06464a5 100755 --- a/hack/make-rules/image_build.sh +++ b/hack/make-rules/image_build.sh @@ -25,6 +25,7 @@ readonly IMAGE_TARGETS=( yurt-node-servant yurthub yurt-manager + yurt-iot-carrier ) http_proxy=${http_proxy:-} diff --git a/pkg/apis/iot/v1alpha1/device_types.go b/pkg/apis/iot/v1alpha1/device_types.go new file mode 100644 index 00000000000..88bc09a948a --- /dev/null +++ b/pkg/apis/iot/v1alpha1/device_types.go @@ -0,0 +1,185 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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" +) + +const ( + DeviceFinalizer = "v1alpha1.device.finalizer" + // DeviceSyncedCondition indicates that the device exists in both OpenYurt and edge platform + DeviceSyncedCondition DeviceConditionType = "DeviceSynced" + // DeviceManagingCondition indicates that the device is being managed by cloud and its properties are being reconciled + DeviceManagingCondition DeviceConditionType = "DeviceManaging" + + DeviceManagingReason = "This device is not managed by openyurt" + + DeviceCreateSyncedReason = "Failed to create device on edge platform" + + DeviceVistedCoreMetadataSyncedReason = "Failed to visit the EdgeX core-metadata-service" + + DeviceUpdateStateReason = "Failed to update AdminState or OperatingState of device on edge platform" +) + +// DeviceConditionType indicates valid conditions type of a Device. +type DeviceConditionType string + +type AdminState string + +const ( + Locked AdminState = "LOCKED" + UnLocked AdminState = "UNLOCKED" +) + +type OperatingState string + +const ( + Unknown OperatingState = "UNKNOWN" + Up OperatingState = "UP" + Down OperatingState = "DOWN" +) + +type ProtocolProperties map[string]string + +// DeviceSpec defines the desired state of Device +type DeviceSpec struct { + // Information describing the device + Description string `json:"description,omitempty"` + // Admin state (locked/unlocked) + AdminState AdminState `json:"adminState,omitempty"` + // Operating state (enabled/disabled) + OperatingState OperatingState `json:"operatingState,omitempty"` + // A map of supported protocols for the given device + Protocols map[string]ProtocolProperties `json:"protocols,omitempty"` + // Other labels applied to the device to help with searching + Labels []string `json:"labels,omitempty"` + // Device service specific location (interface{} is an empty interface so + // it can be anything) + // TODO: location type in edgex is interface{} + Location string `json:"location,omitempty"` + // Associated Device Service - One per device + Service string `json:"serviceName"` + // Associated Device Profile - Describes the device + Profile string `json:"profileName"` + Notify bool `json:"notify"` + // True means device is managed by cloud, cloud can update the related fields + // False means cloud can't update the fields + Managed bool `json:"managed,omitempty"` + // NodePool indicates which nodePool the device comes from + NodePool string `json:"nodePool,omitempty"` + // TODO support the following field + // A list of auto-generated events coming from the device + // AutoEvents []AutoEvent `json:"autoEvents"` + // DeviceProperties represents the expected state of the device's properties + DeviceProperties map[string]DesiredPropertyState `json:"deviceProperties,omitempty"` +} + +type DesiredPropertyState struct { + Name string `json:"name"` + PutURL string `json:"putURL,omitempty"` + DesiredValue string `json:"desiredValue"` +} + +type ActualPropertyState struct { + Name string `json:"name"` + GetURL string `json:"getURL,omitempty"` + ActualValue string `json:"actualValue"` +} + +// DeviceStatus defines the observed state of Device +type DeviceStatus struct { + // Time (milliseconds) that the device last provided any feedback or + // responded to any request + LastConnected int64 `json:"lastConnected,omitempty"` + // Time (milliseconds) that the device reported data to the core + // microservice + LastReported int64 `json:"lastReported,omitempty"` + // Synced indicates whether the device already exists on both OpenYurt and edge platform + Synced bool `json:"synced,omitempty"` + // it represents the actual state of the device's properties + DeviceProperties map[string]ActualPropertyState `json:"deviceProperties,omitempty"` + EdgeId string `json:"edgeId,omitempty"` + // Admin state (locked/unlocked) + AdminState AdminState `json:"adminState,omitempty"` + // Operating state (up/down/unknown) + OperatingState OperatingState `json:"operatingState,omitempty"` + // current device state + // +optional + Conditions []DeviceCondition `json:"conditions,omitempty"` +} + +// DeviceCondition describes current state of a Device. +type DeviceCondition struct { + // Type of in place set condition. + Type DeviceConditionType `json:"type,omitempty"` + + // Status of the condition, one of True, False, Unknown. + Status corev1.ConditionStatus `json:"status,omitempty"` + + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + + // A human readable message indicating details about the transition. + Message string `json:"message,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:shortName=dev +//+kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of device" +//+kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of device" +//+kubebuilder:printcolumn:name="MANAGED",type="boolean",priority=1,JSONPath=".spec.managed",description="The managed status of device" +//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" + +// Device is the Schema for the devices API +type Device struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DeviceSpec `json:"spec,omitempty"` + Status DeviceStatus `json:"status,omitempty"` +} + +func (d *Device) SetConditions(conditions []DeviceCondition) { + d.Status.Conditions = conditions +} + +func (d *Device) GetConditions() []DeviceCondition { + return d.Status.Conditions +} + +func (d *Device) IsAddedToEdgeX() bool { + return d.Status.Synced +} + +//+kubebuilder:object:root=true + +// DeviceList contains a list of Device +type DeviceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Device `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Device{}, &DeviceList{}) +} diff --git a/pkg/apis/iot/v1alpha1/deviceprofile_types.go b/pkg/apis/iot/v1alpha1/deviceprofile_types.go new file mode 100644 index 00000000000..4dcb01958d6 --- /dev/null +++ b/pkg/apis/iot/v1alpha1/deviceprofile_types.go @@ -0,0 +1,120 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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" +) + +const ( + DeviceProfileFinalizer = "v1alpha1.deviceProfile.finalizer" +) + +type DeviceResource struct { + Description string `json:"description"` + Name string `json:"name"` + Tag string `json:"tag,omitempty"` + IsHidden bool `json:"isHidden"` + Properties ResourceProperties `json:"properties"` + Attributes map[string]string `json:"attributes,omitempty"` +} + +type ResourceProperties struct { + ReadWrite string `json:"readWrite,omitempty"` // Read/Write Permissions set for this property + Minimum string `json:"minimum,omitempty"` // Minimum value that can be get/set from this property + Maximum string `json:"maximum,omitempty"` // Maximum value that can be get/set from this property + DefaultValue string `json:"defaultValue,omitempty"` // Default value set to this property if no argument is passed + Mask string `json:"mask,omitempty"` // Mask to be applied prior to get/set of property + Shift string `json:"shift,omitempty"` // Shift to be applied after masking, prior to get/set of property + Scale string `json:"scale,omitempty"` // Multiplicative factor to be applied after shifting, prior to get/set of property + Offset string `json:"offset,omitempty"` // Additive factor to be applied after multiplying, prior to get/set of property + Base string `json:"base,omitempty"` // Base for property to be applied to, leave 0 for no power operation (i.e. base ^ property: 2 ^ 10) + Assertion string `json:"assertion,omitempty"` + MediaType string `json:"mediaType,omitempty"` + Units string `json:"units,omitempty"` + ValueType string `json:"valueType,omitempty"` +} + +type DeviceCommand struct { + Name string `json:"name"` + IsHidden bool `json:"isHidden"` + ReadWrite string `json:"readWrite"` + ResourceOperations []ResourceOperation `json:"resourceOperations"` +} + +type ResourceOperation struct { + DeviceResource string `json:"deviceResource,omitempty"` + Mappings map[string]string `json:"mappings,omitempty"` + DefaultValue string `json:"defaultValue"` +} + +// DeviceProfileSpec defines the desired state of DeviceProfile +type DeviceProfileSpec struct { + // NodePool specifies which nodePool the deviceProfile belongs to + NodePool string `json:"nodePool,omitempty"` + Description string `json:"description,omitempty"` + // Manufacturer of the device + Manufacturer string `json:"manufacturer,omitempty"` + // Model of the device + Model string `json:"model,omitempty"` + // Labels used to search for groups of profiles on EdgeX Foundry + Labels []string `json:"labels,omitempty"` + DeviceResources []DeviceResource `json:"deviceResources,omitempty"` + DeviceCommands []DeviceCommand `json:"deviceCommands,omitempty"` +} + +// DeviceProfileStatus defines the observed state of DeviceProfile +type DeviceProfileStatus struct { + EdgeId string `json:"id,omitempty"` + Synced bool `json:"synced,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:shortName=dp +//+kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of deviceProfile" +//+kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of deviceProfile" +//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" + +// DeviceProfile represents the attributes and operational capabilities of a device. +// It is a template for which there can be multiple matching devices within a given system. +// NOTE This struct is derived from +// edgex/go-mod-core-contracts/models/deviceprofile.go +type DeviceProfile struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DeviceProfileSpec `json:"spec,omitempty"` + Status DeviceProfileStatus `json:"status,omitempty"` +} + +func (dp *DeviceProfile) IsAddedToEdgeX() bool { + return dp.Status.Synced +} + +//+kubebuilder:object:root=true + +// DeviceProfileList contains a list of DeviceProfile +type DeviceProfileList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DeviceProfile `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DeviceProfile{}, &DeviceProfileList{}) +} diff --git a/pkg/apis/iot/v1alpha1/deviceservice_types.go b/pkg/apis/iot/v1alpha1/deviceservice_types.go new file mode 100644 index 00000000000..e2a1eb89518 --- /dev/null +++ b/pkg/apis/iot/v1alpha1/deviceservice_types.go @@ -0,0 +1,129 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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" +) + +const ( + DeviceServiceFinalizer = "v1alpha1.deviceService.finalizer" + // DeviceServiceSyncedCondition indicates that the deviceService exists in both OpenYurt and edge platform + DeviceServiceSyncedCondition DeviceServiceConditionType = "DeviceServiceSynced" + // DeviceServiceManagingCondition indicates that the deviceService is being managed by cloud and its field are being reconciled + DeviceServiceManagingCondition DeviceServiceConditionType = "DeviceServiceManaging" + + DeviceServiceManagingReason = "This deviceService is not managed by openyurt" + + DeviceServiceCreateSyncedReason = "Failed to add DeviceService to EdgeX" + + DeviceServiceUpdateStatusSyncedReason = "Failed to update DeviceService status" +) + +// DeviceServiceConditionType indicates valid conditions type of a Device. +type DeviceServiceConditionType string + +// DeviceServiceSpec defines the desired state of DeviceService +type DeviceServiceSpec struct { + BaseAddress string `json:"baseAddress"` + // Information describing the device + Description string `json:"description,omitempty"` + // tags or other labels applied to the device service for search or other + // identification needs on the EdgeX Foundry + Labels []string `json:"labels,omitempty"` + // Device Service Admin State + AdminState AdminState `json:"adminState,omitempty"` + // True means deviceService is managed by cloud, cloud can update the related fields + // False means cloud can't update the fields + Managed bool `json:"managed,omitempty"` + // NodePool indicates which nodePool the deviceService comes from + NodePool string `json:"nodePool,omitempty"` +} + +// DeviceServiceStatus defines the observed state of DeviceService +type DeviceServiceStatus struct { + // Synced indicates whether the device already exists on both OpenYurt and edge platform + Synced bool `json:"synced,omitempty"` + // the Id assigned by the edge platform + EdgeId string `json:"edgeId,omitempty"` + // time in milliseconds that the device last reported data to the core + LastConnected int64 `json:"lastConnected,omitempty"` + // time in milliseconds that the device last reported data to the core + LastReported int64 `json:"lastReported,omitempty"` + // Device Service Admin State + AdminState AdminState `json:"adminState,omitempty"` + // current deviceService state + // +optional + Conditions []DeviceServiceCondition `json:"conditions,omitempty"` +} + +// DeviceServiceCondition describes current state of a Device. +type DeviceServiceCondition struct { + // Type of in place set condition. + Type DeviceServiceConditionType `json:"type,omitempty"` + + // Status of the condition, one of True, False, Unknown. + Status corev1.ConditionStatus `json:"status,omitempty"` + + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + + // A human readable message indicating details about the transition. + Message string `json:"message,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:shortName=dsvc +//+kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of deviceService" +//+kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of deviceService" +//+kubebuilder:printcolumn:name="MANAGED",type="boolean",priority=1,JSONPath=".spec.managed",description="The managed status of deviceService" +//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" + +// DeviceService is the Schema for the deviceservices API +type DeviceService struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DeviceServiceSpec `json:"spec,omitempty"` + Status DeviceServiceStatus `json:"status,omitempty"` +} + +func (ds *DeviceService) SetConditions(conditions []DeviceServiceCondition) { + ds.Status.Conditions = conditions +} + +func (ds *DeviceService) GetConditions() []DeviceServiceCondition { + return ds.Status.Conditions +} + +//+kubebuilder:object:root=true + +// DeviceServiceList contains a list of DeviceService +type DeviceServiceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DeviceService `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DeviceService{}, &DeviceServiceList{}) +} diff --git a/pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go index 62aaa062a52..3162ed46c6e 100644 --- a/pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go @@ -25,6 +25,21 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ActualPropertyState) DeepCopyInto(out *ActualPropertyState) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActualPropertyState. +func (in *ActualPropertyState) DeepCopy() *ActualPropertyState { + if in == nil { + return nil + } + out := new(ActualPropertyState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeploymentTemplateSpec) DeepCopyInto(out *DeploymentTemplateSpec) { *out = *in @@ -42,6 +57,439 @@ func (in *DeploymentTemplateSpec) DeepCopy() *DeploymentTemplateSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DesiredPropertyState) DeepCopyInto(out *DesiredPropertyState) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DesiredPropertyState. +func (in *DesiredPropertyState) DeepCopy() *DesiredPropertyState { + if in == nil { + return nil + } + out := new(DesiredPropertyState) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Device) DeepCopyInto(out *Device) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Device. +func (in *Device) DeepCopy() *Device { + if in == nil { + return nil + } + out := new(Device) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Device) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceCommand) DeepCopyInto(out *DeviceCommand) { + *out = *in + if in.ResourceOperations != nil { + in, out := &in.ResourceOperations, &out.ResourceOperations + *out = make([]ResourceOperation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceCommand. +func (in *DeviceCommand) DeepCopy() *DeviceCommand { + if in == nil { + return nil + } + out := new(DeviceCommand) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceCondition) DeepCopyInto(out *DeviceCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceCondition. +func (in *DeviceCondition) DeepCopy() *DeviceCondition { + if in == nil { + return nil + } + out := new(DeviceCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceList) DeepCopyInto(out *DeviceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Device, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceList. +func (in *DeviceList) DeepCopy() *DeviceList { + if in == nil { + return nil + } + out := new(DeviceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DeviceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceProfile) DeepCopyInto(out *DeviceProfile) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceProfile. +func (in *DeviceProfile) DeepCopy() *DeviceProfile { + if in == nil { + return nil + } + out := new(DeviceProfile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DeviceProfile) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceProfileList) DeepCopyInto(out *DeviceProfileList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DeviceProfile, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceProfileList. +func (in *DeviceProfileList) DeepCopy() *DeviceProfileList { + if in == nil { + return nil + } + out := new(DeviceProfileList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DeviceProfileList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceProfileSpec) DeepCopyInto(out *DeviceProfileSpec) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.DeviceResources != nil { + in, out := &in.DeviceResources, &out.DeviceResources + *out = make([]DeviceResource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DeviceCommands != nil { + in, out := &in.DeviceCommands, &out.DeviceCommands + *out = make([]DeviceCommand, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceProfileSpec. +func (in *DeviceProfileSpec) DeepCopy() *DeviceProfileSpec { + if in == nil { + return nil + } + out := new(DeviceProfileSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceProfileStatus) DeepCopyInto(out *DeviceProfileStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceProfileStatus. +func (in *DeviceProfileStatus) DeepCopy() *DeviceProfileStatus { + if in == nil { + return nil + } + out := new(DeviceProfileStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceResource) DeepCopyInto(out *DeviceResource) { + *out = *in + out.Properties = in.Properties + if in.Attributes != nil { + in, out := &in.Attributes, &out.Attributes + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceResource. +func (in *DeviceResource) DeepCopy() *DeviceResource { + if in == nil { + return nil + } + out := new(DeviceResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceService) DeepCopyInto(out *DeviceService) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceService. +func (in *DeviceService) DeepCopy() *DeviceService { + if in == nil { + return nil + } + out := new(DeviceService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DeviceService) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceServiceCondition) DeepCopyInto(out *DeviceServiceCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceServiceCondition. +func (in *DeviceServiceCondition) DeepCopy() *DeviceServiceCondition { + if in == nil { + return nil + } + out := new(DeviceServiceCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceServiceList) DeepCopyInto(out *DeviceServiceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DeviceService, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceServiceList. +func (in *DeviceServiceList) DeepCopy() *DeviceServiceList { + if in == nil { + return nil + } + out := new(DeviceServiceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DeviceServiceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceServiceSpec) DeepCopyInto(out *DeviceServiceSpec) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceServiceSpec. +func (in *DeviceServiceSpec) DeepCopy() *DeviceServiceSpec { + if in == nil { + return nil + } + out := new(DeviceServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceServiceStatus) DeepCopyInto(out *DeviceServiceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]DeviceServiceCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceServiceStatus. +func (in *DeviceServiceStatus) DeepCopy() *DeviceServiceStatus { + if in == nil { + return nil + } + out := new(DeviceServiceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceSpec) DeepCopyInto(out *DeviceSpec) { + *out = *in + if in.Protocols != nil { + in, out := &in.Protocols, &out.Protocols + *out = make(map[string]ProtocolProperties, len(*in)) + for key, val := range *in { + var outVal map[string]string + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make(ProtocolProperties, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + (*out)[key] = outVal + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.DeviceProperties != nil { + in, out := &in.DeviceProperties, &out.DeviceProperties + *out = make(map[string]DesiredPropertyState, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceSpec. +func (in *DeviceSpec) DeepCopy() *DeviceSpec { + if in == nil { + return nil + } + out := new(DeviceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceStatus) DeepCopyInto(out *DeviceStatus) { + *out = *in + if in.DeviceProperties != nil { + in, out := &in.DeviceProperties, &out.DeviceProperties + *out = make(map[string]ActualPropertyState, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]DeviceCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceStatus. +func (in *DeviceStatus) DeepCopy() *DeviceStatus { + if in == nil { + return nil + } + out := new(DeviceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PlatformAdmin) DeepCopyInto(out *PlatformAdmin) { *out = *in @@ -168,6 +616,64 @@ func (in *PlatformAdminStatus) DeepCopy() *PlatformAdminStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ProtocolProperties) DeepCopyInto(out *ProtocolProperties) { + { + in := &in + *out = make(ProtocolProperties, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProtocolProperties. +func (in ProtocolProperties) DeepCopy() ProtocolProperties { + if in == nil { + return nil + } + out := new(ProtocolProperties) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceOperation) DeepCopyInto(out *ResourceOperation) { + *out = *in + if in.Mappings != nil { + in, out := &in.Mappings, &out.Mappings + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceOperation. +func (in *ResourceOperation) DeepCopy() *ResourceOperation { + if in == nil { + return nil + } + out := new(ResourceOperation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceProperties) DeepCopyInto(out *ResourceProperties) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceProperties. +func (in *ResourceProperties) DeepCopy() *ResourceProperties { + if in == nil { + return nil + } + out := new(ResourceProperties) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceTemplateSpec) DeepCopyInto(out *ServiceTemplateSpec) { *out = *in diff --git a/pkg/controller/platformadmin/platformadmin_controller.go b/pkg/controller/platformadmin/platformadmin_controller.go index 542c147c0f2..a453b367d07 100644 --- a/pkg/controller/platformadmin/platformadmin_controller.go +++ b/pkg/controller/platformadmin/platformadmin_controller.go @@ -257,6 +257,8 @@ func (r *ReconcilePlatformAdmin) reconcileDelete(ctx context.Context, platformAd } desiredComponents = append(desiredComponents, additionalComponents...) + yurtIotCarrier, err := NewYurtIoTCarrierComponent(platformAdmin) + desiredComponents = append(desiredComponents, yurtIotCarrier) //TODO: handle PlatformAdmin.Spec.Components for _, dc := range desiredComponents { @@ -386,6 +388,8 @@ func (r *ReconcilePlatformAdmin) reconcileComponent(ctx context.Context, platfor } desireComponents = append(desireComponents, additionalComponents...) + yurtIotCarrier, err := NewYurtIoTCarrierComponent(platformAdmin) + desireComponents = append(desireComponents, yurtIotCarrier) //TODO: handle PlatformAdmin.Spec.Components defer func() { @@ -760,3 +764,115 @@ func (r *ReconcilePlatformAdmin) initFramework(ctx context.Context, platformAdmi } return nil } + +func NewYurtIoTCarrierComponent(platformAdmin *iotv1alpha2.PlatformAdmin) (*config.Component, error) { + var yurtIotCarrierComponent config.Component + var yurtIoTCarrierDeployment iotv1alpha1.DeploymentTemplateSpec + ver, ns, err := util.DefaultVersion(platformAdmin) + if err != nil { + return nil, err + } + // YurtIoTCarrier doesn't need a service yet + yurtIoTCarrierConfig := map[string]interface{}{ + "deploymentTemplate": map[string]interface{}{ + "metadata": map[string]interface{}{ + "creationTimestamp": nil, + "labels": map[string]interface{}{ + "app": "yurt-iot-carrier", + }, + "Namespace": ns, + }, + "spec": map[string]interface{}{ + "selector": map[string]interface{}{ + "matchLabels": map[string]interface{}{ + "app": "yurt-iot-carrier", + }, + }, + "strategy": map[string]interface{}{}, + "template": map[string]interface{}{ + "metadata": map[string]interface{}{ + "creationTimestamp": nil, + "labels": map[string]interface{}{ + "app": "yurt-iot-carrier", + "control-plane": "edgex-controller-manager", + }, + }, + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "args": []interface{}{ + "--health-probe-bind-address=:8081", + "--metrics-bind-address=127.0.0.1:80", + "--leader-elect=false", + }, + // "command": []interface{}{ + // "./yurt-iot-carrier", + // }, + "image": fmt.Sprintf("leoabyss/yurt-iot-carrier:%s", ver), + "imagePullPolicy": "IfNotPresent", + "livenessProbe": map[string]interface{}{ + "failureThreshold": 3, + "httpGet": map[string]interface{}{ + "path": "/healthz", + "port": 8081, + "scheme": "HTTP", + }, + "initialDelaySeconds": 15, + "periodSeconds": 20, + "successThreshold": 1, + "timeoutSeconds": 1, + }, + "name": "yurt-iot-carrier", + "readinessProbe": map[string]interface{}{ + "failureThreshold": 3, + "httpGet": map[string]interface{}{ + "path": "/readyz", + "port": 8081, + "scheme": "HTTP", + }, + "initialDelaySeconds": 5, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1, + }, + "resources": map[string]interface{}{ + "limits": map[string]interface{}{ + "cpu": "100m", + "memory": "512Mi", + }, + "requests": map[string]interface{}{ + "cpu": "100m", + "memory": "512Mi", + }, + }, + "securityContext": map[string]interface{}{ + "allowPrivilegeEscalation": false, + }, + }, + }, + "dnsPolicy": "ClusterFirst", + "restartPolicy": "Always", + "securityContext": map[string]interface{}{ + "runAsUser": 65532, + }, + }, + }, + }, + }, + } + yurtIoTCarrierConfigBytes, err := json.Marshal(yurtIoTCarrierConfig["deploymentTemplate"]) + if err != nil { + return nil, err + } + + err = json.Unmarshal([]byte(yurtIoTCarrierConfigBytes), &yurtIoTCarrierDeployment) + if err != nil { + return nil, err + } + + yurtIotCarrierComponent.Name = "yurt-iot-carrier" + yurtIotCarrierComponent.Deployment = &yurtIoTCarrierDeployment.Spec + yurtIotCarrierComponent.Service = nil + + return &yurtIotCarrierComponent, nil +} diff --git a/pkg/controller/platformadmin/utils/version.go b/pkg/controller/platformadmin/utils/version.go new file mode 100644 index 00000000000..c89e60d480a --- /dev/null +++ b/pkg/controller/platformadmin/utils/version.go @@ -0,0 +1,29 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 util + +import ( + iotv1alpha2 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha2" +) + +func DefaultVersion(platformAdmin *iotv1alpha2.PlatformAdmin) (string, string, error) { + + version := "v1.3.0-6a8902b" + ns := platformAdmin.Namespace + + return version, ns, nil +} diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go b/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go new file mode 100644 index 00000000000..c549f0d0eb5 --- /dev/null +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go @@ -0,0 +1,371 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 edgex_foundry + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/cookiejar" + "strings" + "time" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" + edgex_resp "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" + "github.com/go-resty/resty/v2" + "golang.org/x/net/publicsuffix" + "k8s.io/klog/v2" +) + +type EdgexDeviceClient struct { + *resty.Client + CoreMetaAddr string + CoreCommandAddr string +} + +func NewEdgexDeviceClient(coreMetaAddr, coreCommandAddr string) *EdgexDeviceClient { + cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) + instance := resty.NewWithClient(&http.Client{ + Jar: cookieJar, + Timeout: 10 * time.Second, + }) + return &EdgexDeviceClient{ + Client: instance, + CoreMetaAddr: coreMetaAddr, + CoreCommandAddr: coreCommandAddr, + } +} + +// Create function sends a POST request to EdgeX to add a new device +func (efc *EdgexDeviceClient) Create(ctx context.Context, device *iotv1alpha1.Device, options clients.CreateOptions) (*iotv1alpha1.Device, error) { + devs := []*iotv1alpha1.Device{device} + req := makeEdgeXDeviceRequest(devs) + klog.V(5).Infof("will add the Device: %s", device.Name) + reqBody, err := json.Marshal(req) + if err != nil { + return nil, err + } + postPath := fmt.Sprintf("http://%s%s", efc.CoreMetaAddr, DevicePath) + resp, err := efc.R(). + SetBody(reqBody).Post(postPath) + if err != nil { + return nil, err + } else if resp.StatusCode() != http.StatusMultiStatus { + return nil, fmt.Errorf("create device on edgex foundry failed, the response is : %s", resp.Body()) + } + + var edgexResps []*common.BaseWithIdResponse + if err = json.Unmarshal(resp.Body(), &edgexResps); err != nil { + return nil, err + } + createdDevice := device.DeepCopy() + if len(edgexResps) == 1 { + if edgexResps[0].StatusCode == http.StatusCreated { + createdDevice.Status.EdgeId = edgexResps[0].Id + createdDevice.Status.Synced = true + } else { + return nil, fmt.Errorf("create device on edgex foundry failed, the response is : %s", resp.Body()) + } + } else { + return nil, fmt.Errorf("edgex BaseWithIdResponse count mismatch device cound, the response is : %s", resp.Body()) + } + return createdDevice, err +} + +// Delete function sends a request to EdgeX to delete a device +func (efc *EdgexDeviceClient) Delete(ctx context.Context, name string, options clients.DeleteOptions) error { + klog.V(5).Infof("will delete the Device: %s", name) + delURL := fmt.Sprintf("http://%s%s/name/%s", efc.CoreMetaAddr, DevicePath, name) + resp, err := efc.R().Delete(delURL) + if err != nil { + return err + } + if resp.StatusCode() != http.StatusOK { + return errors.New(string(resp.Body())) + } + return nil +} + +// Update is used to set the admin or operating state of the device by unique name of the device. +// TODO support to update other fields +func (efc *EdgexDeviceClient) Update(ctx context.Context, device *iotv1alpha1.Device, options clients.UpdateOptions) (*iotv1alpha1.Device, error) { + actualDeviceName := getEdgeXName(device) + patchURL := fmt.Sprintf("http://%s%s", efc.CoreMetaAddr, DevicePath) + if device == nil { + return nil, nil + } + devs := []*iotv1alpha1.Device{device} + req := makeEdgeXDeviceUpdateRequest(devs) + reqBody, err := json.Marshal(req) + if err != nil { + return nil, err + } + rep, err := efc.R(). + SetHeader("Content-Type", "application/json"). + SetBody(reqBody). + Patch(patchURL) + if err != nil { + return nil, err + } else if rep.StatusCode() != http.StatusMultiStatus { + return nil, fmt.Errorf("failed to update device: %s, get response: %s", actualDeviceName, string(rep.Body())) + } + return device, nil +} + +// Get is used to query the device information corresponding to the device name +func (efc *EdgexDeviceClient) Get(ctx context.Context, deviceName string, options clients.GetOptions) (*iotv1alpha1.Device, error) { + klog.V(5).Infof("will get Devices: %s", deviceName) + var dResp edgex_resp.DeviceResponse + getURL := fmt.Sprintf("http://%s%s/name/%s", efc.CoreMetaAddr, DevicePath, deviceName) + resp, err := efc.R().Get(getURL) + if err != nil { + return nil, err + } + if resp.StatusCode() == http.StatusNotFound { + return nil, fmt.Errorf("Device %s not found", deviceName) + } + err = json.Unmarshal(resp.Body(), &dResp) + if err != nil { + return nil, err + } + device := toKubeDevice(dResp.Device) + return &device, err +} + +// List is used to get all device objects on edge platform +// TODO:support label filtering according to options +func (efc *EdgexDeviceClient) List(ctx context.Context, options clients.ListOptions) ([]iotv1alpha1.Device, error) { + lp := fmt.Sprintf("http://%s%s/all?limit=-1", efc.CoreMetaAddr, DevicePath) + resp, err := efc.R().EnableTrace().Get(lp) + if err != nil { + return nil, err + } + var mdResp edgex_resp.MultiDevicesResponse + if err := json.Unmarshal(resp.Body(), &mdResp); err != nil { + return nil, err + } + var res []iotv1alpha1.Device + for _, dp := range mdResp.Devices { + res = append(res, toKubeDevice(dp)) + } + return res, nil +} + +func (efc *EdgexDeviceClient) GetPropertyState(ctx context.Context, propertyName string, d *iotv1alpha1.Device, options clients.GetOptions) (*iotv1alpha1.ActualPropertyState, error) { + actualDeviceName := getEdgeXName(d) + // get the old property from status + oldAps, exist := d.Status.DeviceProperties[propertyName] + propertyGetURL := "" + // 1. query the Get URL of a property + if !exist || (exist && oldAps.GetURL == "") { + coreCommands, err := efc.GetCommandResponseByName(actualDeviceName) + if err != nil { + return &iotv1alpha1.ActualPropertyState{}, err + } + for _, c := range coreCommands { + if c.Name == propertyName && c.Get { + propertyGetURL = fmt.Sprintf("%s%s", c.Url, c.Path) + break + } + } + if propertyGetURL == "" { + return nil, &clients.NotFoundError{} + } + } else { + propertyGetURL = oldAps.GetURL + } + // 2. get the actual property value by the getURL + actualPropertyState := iotv1alpha1.ActualPropertyState{ + Name: propertyName, + GetURL: propertyGetURL, + } + if resp, err := efc.getPropertyState(propertyGetURL); err != nil { + return nil, err + } else { + var eResp edgex_resp.EventResponse + if err := json.Unmarshal(resp.Body(), &eResp); err != nil { + return nil, err + } + actualPropertyState.ActualValue = getPropertyValueFromEvent(propertyName, eResp.Event) + } + return &actualPropertyState, nil +} + +// getPropertyState returns different error messages according to the status code +func (efc *EdgexDeviceClient) getPropertyState(getURL string) (*resty.Response, error) { + resp, err := efc.R().Get(getURL) + if err != nil { + return resp, err + } + if resp.StatusCode() == 400 { + err = errors.New("request is in an invalid state") + } else if resp.StatusCode() == 404 { + err = errors.New("the requested resource does not exist") + } else if resp.StatusCode() == 423 { + err = errors.New("the device is locked (AdminState) or down (OperatingState)") + } else if resp.StatusCode() == 500 { + err = errors.New("an unexpected error occurred on the server") + } + return resp, err +} + +func (efc *EdgexDeviceClient) UpdatePropertyState(ctx context.Context, propertyName string, d *iotv1alpha1.Device, options clients.UpdateOptions) error { + // Get the actual device name + acturalDeviceName := getEdgeXName(d) + + dps := d.Spec.DeviceProperties[propertyName] + parameterName := dps.Name + if dps.PutURL == "" { + putCmd, err := efc.getPropertyPut(acturalDeviceName, dps.Name) + if err != nil { + return err + } + dps.PutURL = fmt.Sprintf("%s%s", putCmd.Url, putCmd.Path) + if len(putCmd.Parameters) == 1 { + parameterName = putCmd.Parameters[0].ResourceName + } + } + // set the device property to desired state + bodyMap := make(map[string]string) + bodyMap[parameterName] = dps.DesiredValue + body, _ := json.Marshal(bodyMap) + klog.V(5).Infof("setting the property to desired value", "propertyName", parameterName, "desiredValue", string(body)) + rep, err := efc.R(). + SetHeader("Content-Type", "application/json"). + SetBody(body). + Put(dps.PutURL) + if err != nil { + return err + } else if rep.StatusCode() != http.StatusOK { + return fmt.Errorf("failed to set property: %s, get response: %s", dps.Name, string(rep.Body())) + } else if rep.Body() != nil { + // If the parameters are illegal, such as out of range, the 200 status code is also returned, but the description appears in the body + a := string(rep.Body()) + if strings.Contains(a, "execWriteCmd") { + return fmt.Errorf("failed to set property: %s, get response: %s", dps.Name, string(rep.Body())) + } + } + return nil +} + +// Gets the models.Put from edgex foundry which is used to set the device property's value +func (efc *EdgexDeviceClient) getPropertyPut(deviceName, cmdName string) (dtos.CoreCommand, error) { + coreCommands, err := efc.GetCommandResponseByName(deviceName) + if err != nil { + return dtos.CoreCommand{}, err + } + for _, c := range coreCommands { + if cmdName == c.Name && c.Set { + return c, nil + } + } + return dtos.CoreCommand{}, errors.New("corresponding command is not found") +} + +// ListPropertiesState gets all the actual property information about a device +func (efc *EdgexDeviceClient) ListPropertiesState(ctx context.Context, device *iotv1alpha1.Device, options clients.ListOptions) (map[string]iotv1alpha1.DesiredPropertyState, map[string]iotv1alpha1.ActualPropertyState, error) { + actualDeviceName := getEdgeXName(device) + + dpsm := map[string]iotv1alpha1.DesiredPropertyState{} + apsm := map[string]iotv1alpha1.ActualPropertyState{} + coreCommands, err := efc.GetCommandResponseByName(actualDeviceName) + if err != nil { + return dpsm, apsm, err + } + + for _, c := range coreCommands { + // DesiredPropertyState only store the basic information and does not set DesiredValue + if c.Get { + getURL := fmt.Sprintf("%s%s", c.Url, c.Path) + aps, ok := apsm[c.Name] + if ok { + aps.GetURL = getURL + } else { + aps = iotv1alpha1.ActualPropertyState{Name: c.Name, GetURL: getURL} + } + apsm[c.Name] = aps + resp, err := efc.getPropertyState(getURL) + if err != nil { + klog.V(5).ErrorS(err, "getPropertyState failed", "propertyName", c.Name, "deviceName", actualDeviceName) + } else { + var eResp edgex_resp.EventResponse + if err := json.Unmarshal(resp.Body(), &eResp); err != nil { + klog.V(5).ErrorS(err, "failed to decode the response ", "response", resp) + continue + } + event := eResp.Event + readingName := c.Name + expectParams := c.Parameters + if len(expectParams) == 1 { + readingName = expectParams[0].ResourceName + } + klog.V(5).Infof("get reading name %s for command %s of device %s", readingName, c.Name, device.Name) + actualValue := getPropertyValueFromEvent(readingName, event) + aps.ActualValue = actualValue + apsm[c.Name] = aps + } + } + } + return dpsm, apsm, nil +} + +// The actual property value is resolved from the returned event +func getPropertyValueFromEvent(resName string, event dtos.Event) string { + actualValue := "" + for _, r := range event.Readings { + if resName == r.ResourceName { + if r.SimpleReading.Value != "" { + actualValue = r.SimpleReading.Value + } else if len(r.BinaryReading.BinaryValue) != 0 { + // TODO: how to demonstrate binary data + actualValue = fmt.Sprintf("%s:%s", r.BinaryReading.MediaType, "blob value") + } else if r.ObjectReading.ObjectValue != nil { + serializedBytes, _ := json.Marshal(r.ObjectReading.ObjectValue) + actualValue = string(serializedBytes) + } + break + } + } + return actualValue +} + +// GetCommandResponseByName gets all commands supported by the device +func (efc *EdgexDeviceClient) GetCommandResponseByName(deviceName string) ([]dtos.CoreCommand, error) { + klog.V(5).Infof("will get CommandResponses of device: %s", deviceName) + + var dcr edgex_resp.DeviceCoreCommandResponse + getURL := fmt.Sprintf("http://%s%s/name/%s", efc.CoreCommandAddr, CommandResponsePath, deviceName) + + resp, err := efc.R().Get(getURL) + if err != nil { + return nil, err + } + if resp.StatusCode() == http.StatusNotFound { + return nil, errors.New("Item not found") + } + err = json.Unmarshal(resp.Body(), &dcr) + if err != nil { + return nil, err + } + return dcr.DeviceCoreCommand.CoreCommands, nil +} diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go b/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go new file mode 100644 index 00000000000..6b564f1ff55 --- /dev/null +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go @@ -0,0 +1,203 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 edgex_foundry + +import ( + "context" + "encoding/json" + "testing" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + + edgex_resp "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/assert" +) + +const ( + DeviceListMetadata = `{"apiVersion":"v2","statusCode":200,"totalCount":5,"devices":[{"created":1661829206505,"modified":1661829206505,"id":"f6255845-f4b2-4182-bd3c-abc9eac4a649","name":"Random-Float-Device","description":"Example of Device Virtual","adminState":"UNLOCKED","operatingState":"UP","labels":["device-virtual-example"],"serviceName":"device-virtual","profileName":"Random-Float-Device","autoEvents":[{"interval":"30s","onChange":false,"sourceName":"Float32"},{"interval":"30s","onChange":false,"sourceName":"Float64"}],"protocols":{"other":{"Address":"device-virtual-float-01","Protocol":"300"}}},{"created":1661829206506,"modified":1661829206506,"id":"d29efe20-fdec-4aeb-90e5-99528cb6ca28","name":"Random-Binary-Device","description":"Example of Device Virtual","adminState":"UNLOCKED","operatingState":"UP","labels":["device-virtual-example"],"serviceName":"device-virtual","profileName":"Random-Binary-Device","protocols":{"other":{"Address":"device-virtual-binary-01","Port":"300"}}},{"created":1661829206504,"modified":1661829206504,"id":"6a7f00a4-9536-48b2-9380-a9fc202ac517","name":"Random-Integer-Device","description":"Example of Device Virtual","adminState":"UNLOCKED","operatingState":"UP","labels":["device-virtual-example"],"serviceName":"device-virtual","profileName":"Random-Integer-Device","autoEvents":[{"interval":"15s","onChange":false,"sourceName":"Int8"},{"interval":"15s","onChange":false,"sourceName":"Int16"},{"interval":"15s","onChange":false,"sourceName":"Int32"},{"interval":"15s","onChange":false,"sourceName":"Int64"}],"protocols":{"other":{"Address":"device-virtual-int-01","Protocol":"300"}}},{"created":1661829206503,"modified":1661829206503,"id":"439d47a2-fa72-4c27-9f47-c19356cc0c3b","name":"Random-Boolean-Device","description":"Example of Device Virtual","adminState":"UNLOCKED","operatingState":"UP","labels":["device-virtual-example"],"serviceName":"device-virtual","profileName":"Random-Boolean-Device","autoEvents":[{"interval":"10s","onChange":false,"sourceName":"Bool"}],"protocols":{"other":{"Address":"device-virtual-bool-01","Port":"300"}}},{"created":1661829206505,"modified":1661829206505,"id":"2890ab86-3ae4-4b5e-98ab-aad85fc540e6","name":"Random-UnsignedInteger-Device","description":"Example of Device Virtual","adminState":"UNLOCKED","operatingState":"UP","labels":["device-virtual-example"],"serviceName":"device-virtual","profileName":"Random-UnsignedInteger-Device","autoEvents":[{"interval":"20s","onChange":false,"sourceName":"Uint8"},{"interval":"20s","onChange":false,"sourceName":"Uint16"},{"interval":"20s","onChange":false,"sourceName":"Uint32"},{"interval":"20s","onChange":false,"sourceName":"Uint64"}],"protocols":{"other":{"Address":"device-virtual-uint-01","Protocol":"300"}}}]}` + DeviceMetadata = `{"apiVersion":"v2","statusCode":200,"device":{"created":1661829206505,"modified":1661829206505,"id":"f6255845-f4b2-4182-bd3c-abc9eac4a649","name":"Random-Float-Device","description":"Example of Device Virtual","adminState":"UNLOCKED","operatingState":"UP","labels":["device-virtual-example"],"serviceName":"device-virtual","profileName":"Random-Float-Device","autoEvents":[{"interval":"30s","onChange":false,"sourceName":"Float32"},{"interval":"30s","onChange":false,"sourceName":"Float64"}],"protocols":{"other":{"Address":"device-virtual-float-01","Protocol":"300"}}}}` + + DeviceCreateSuccess = `[{"apiVersion":"v2","statusCode":201,"id":"2fff4f1a-7110-442f-b347-9f896338ba57"}]` + DeviceCreateFail = `[{"apiVersion":"v2","message":"device name test-Random-Float-Device already exists","statusCode":409}]` + + DeviceDeleteSuccess = `{"apiVersion":"v2","statusCode":200}` + DeviceDeleteFail = `{"apiVersion":"v2","message":"fail to query device by name test-Random-Float-Device","statusCode":404}` + + DeviceCoreCommands = `{"apiVersion":"v2","statusCode":200,"deviceCoreCommand":{"deviceName":"Random-Float-Device","profileName":"Random-Float-Device","coreCommands":[{"name":"WriteFloat32ArrayValue","set":true,"path":"/api/v2/device/name/Random-Float-Device/WriteFloat32ArrayValue","url":"http://edgex-core-command:59882","parameters":[{"resourceName":"Float32Array","valueType":"Float32Array"},{"resourceName":"EnableRandomization_Float32Array","valueType":"Bool"}]},{"name":"WriteFloat64ArrayValue","set":true,"path":"/api/v2/device/name/Random-Float-Device/WriteFloat64ArrayValue","url":"http://edgex-core-command:59882","parameters":[{"resourceName":"Float64Array","valueType":"Float64Array"},{"resourceName":"EnableRandomization_Float64Array","valueType":"Bool"}]},{"name":"Float32","get":true,"set":true,"path":"/api/v2/device/name/Random-Float-Device/Float32","url":"http://edgex-core-command:59882","parameters":[{"resourceName":"Float32","valueType":"Float32"}]},{"name":"Float64","get":true,"set":true,"path":"/api/v2/device/name/Random-Float-Device/Float64","url":"http://edgex-core-command:59882","parameters":[{"resourceName":"Float64","valueType":"Float64"}]},{"name":"Float32Array","get":true,"set":true,"path":"/api/v2/device/name/Random-Float-Device/Float32Array","url":"http://edgex-core-command:59882","parameters":[{"resourceName":"Float32Array","valueType":"Float32Array"}]},{"name":"Float64Array","get":true,"set":true,"path":"/api/v2/device/name/Random-Float-Device/Float64Array","url":"http://edgex-core-command:59882","parameters":[{"resourceName":"Float64Array","valueType":"Float64Array"}]},{"name":"WriteFloat32Value","set":true,"path":"/api/v2/device/name/Random-Float-Device/WriteFloat32Value","url":"http://edgex-core-command:59882","parameters":[{"resourceName":"Float32","valueType":"Float32"},{"resourceName":"EnableRandomization_Float32","valueType":"Bool"}]},{"name":"WriteFloat64Value","set":true,"path":"/api/v2/device/name/Random-Float-Device/WriteFloat64Value","url":"http://edgex-core-command:59882","parameters":[{"resourceName":"Float64","valueType":"Float64"},{"resourceName":"EnableRandomization_Float64","valueType":"Bool"}]}]}}` + DeviceCommandResp = `{"apiVersion":"v2","statusCode":200,"event":{"apiVersion":"v2","id":"095090e4-de39-45a1-a0fa-18bc340104e6","deviceName":"Random-Float-Device","profileName":"Random-Float-Device","sourceName":"Float32","origin":1661851070562067780,"readings":[{"id":"972bf6be-3b01-49fc-b211-a43ed51d207d","origin":1661851070562067780,"deviceName":"Random-Float-Device","resourceName":"Float32","profileName":"Random-Float-Device","valueType":"Float32","value":"-2.038811e+38"}]}}` + + DeviceUpdateSuccess = `[{"apiVersion":"v2","statusCode":200}] ` + + DeviceUpdateProperty = `{"apiVersion":"v2","statusCode":200}` +) + +var deviceClient = NewEdgexDeviceClient("edgex-core-metadata:59881", "edgex-core-command:59882") + +func Test_Get(t *testing.T) { + httpmock.ActivateNonDefault(deviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/device/name/Random-Float-Device", + httpmock.NewStringResponder(200, DeviceMetadata)) + + device, err := deviceClient.Get(context.TODO(), "Random-Float-Device", clients.GetOptions{}) + assert.Nil(t, err) + + assert.Equal(t, "Random-Float-Device", device.Spec.Profile) +} + +func Test_List(t *testing.T) { + + httpmock.ActivateNonDefault(deviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/device/all?limit=-1", + httpmock.NewStringResponder(200, DeviceListMetadata)) + + devices, err := deviceClient.List(context.TODO(), clients.ListOptions{}) + assert.Nil(t, err) + + assert.Equal(t, len(devices), 5) +} + +func Test_Create(t *testing.T) { + httpmock.ActivateNonDefault(deviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("POST", "http://edgex-core-metadata:59881/api/v2/device", + httpmock.NewStringResponder(207, DeviceCreateSuccess)) + + var resp edgex_resp.DeviceResponse + + err := json.Unmarshal([]byte(DeviceMetadata), &resp) + assert.Nil(t, err) + + device := toKubeDevice(resp.Device) + device.Name = "test-Random-Float-Device" + + create, err := deviceClient.Create(context.TODO(), &device, clients.CreateOptions{}) + assert.Nil(t, err) + + assert.Equal(t, "test-Random-Float-Device", create.Name) + + httpmock.RegisterResponder("POST", "http://edgex-core-metadata:59881/api/v2/device", + httpmock.NewStringResponder(207, DeviceCreateFail)) + + create, err = deviceClient.Create(context.TODO(), &device, clients.CreateOptions{}) + assert.NotNil(t, err) + assert.Nil(t, create) +} + +func Test_Delete(t *testing.T) { + httpmock.ActivateNonDefault(deviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("DELETE", "http://edgex-core-metadata:59881/api/v2/device/name/test-Random-Float-Device", + httpmock.NewStringResponder(200, DeviceDeleteSuccess)) + + err := deviceClient.Delete(context.TODO(), "test-Random-Float-Device", clients.DeleteOptions{}) + assert.Nil(t, err) + + httpmock.RegisterResponder("DELETE", "http://edgex-core-metadata:59881/api/v2/device/name/test-Random-Float-Device", + httpmock.NewStringResponder(404, DeviceDeleteFail)) + + err = deviceClient.Delete(context.TODO(), "test-Random-Float-Device", clients.DeleteOptions{}) + assert.NotNil(t, err) +} + +func Test_GetPropertyState(t *testing.T) { + + httpmock.ActivateNonDefault(deviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "http://edgex-core-command:59882/api/v2/device/name/Random-Float-Device", + httpmock.NewStringResponder(200, DeviceCoreCommands)) + httpmock.RegisterResponder("GET", "http://edgex-core-command:59882/api/v2/device/name/Random-Float-Device/Float32", + httpmock.NewStringResponder(200, DeviceCommandResp)) + + var resp edgex_resp.DeviceResponse + + err := json.Unmarshal([]byte(DeviceMetadata), &resp) + assert.Nil(t, err) + + device := toKubeDevice(resp.Device) + + _, err = deviceClient.GetPropertyState(context.TODO(), "Float32", &device, clients.GetOptions{}) + assert.Nil(t, err) +} + +func Test_ListPropertiesState(t *testing.T) { + httpmock.ActivateNonDefault(deviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "http://edgex-core-command:59882/api/v2/device/name/Random-Float-Device", + httpmock.NewStringResponder(200, DeviceCoreCommands)) + + var resp edgex_resp.DeviceResponse + + err := json.Unmarshal([]byte(DeviceMetadata), &resp) + assert.Nil(t, err) + + device := toKubeDevice(resp.Device) + + _, _, err = deviceClient.ListPropertiesState(context.TODO(), &device, clients.ListOptions{}) + assert.Nil(t, err) +} + +func Test_UpdateDevice(t *testing.T) { + httpmock.ActivateNonDefault(deviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("PATCH", "http://edgex-core-metadata:59881/api/v2/device", + httpmock.NewStringResponder(207, DeviceUpdateSuccess)) + + var resp edgex_resp.DeviceResponse + + err := json.Unmarshal([]byte(DeviceMetadata), &resp) + assert.Nil(t, err) + + device := toKubeDevice(resp.Device) + device.Spec.AdminState = "LOCKED" + + _, err = deviceClient.Update(context.TODO(), &device, clients.UpdateOptions{}) + assert.Nil(t, err) +} + +func Test_UpdatePropertyState(t *testing.T) { + httpmock.ActivateNonDefault(deviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "http://edgex-core-command:59882/api/v2/device/name/Random-Float-Device", + httpmock.NewStringResponder(200, DeviceCoreCommands)) + + httpmock.RegisterResponder("PUT", "http://edgex-core-command:59882/api/v2/device/name/Random-Float-Device/Float32", + httpmock.NewStringResponder(200, DeviceUpdateSuccess)) + var resp edgex_resp.DeviceResponse + err := json.Unmarshal([]byte(DeviceMetadata), &resp) + assert.Nil(t, err) + + device := toKubeDevice(resp.Device) + device.Spec.DeviceProperties = map[string]iotv1alpha1.DesiredPropertyState{ + "Float32": iotv1alpha1.DesiredPropertyState{ + Name: "Float32", + DesiredValue: "66.66", + }, + } + + err = deviceClient.UpdatePropertyState(context.TODO(), "Float32", &device, clients.UpdateOptions{}) + assert.Nil(t, err) +} diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go new file mode 100644 index 00000000000..911f58bc00d --- /dev/null +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go @@ -0,0 +1,142 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 edgex_foundry + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + devcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" + + "github.com/go-resty/resty/v2" + "k8s.io/klog/v2" +) + +type EdgexDeviceProfile struct { + *resty.Client + CoreMetaAddr string +} + +func NewEdgexDeviceProfile(coreMetaAddr string) *EdgexDeviceProfile { + return &EdgexDeviceProfile{ + Client: resty.New(), + CoreMetaAddr: coreMetaAddr, + } +} + +// TODO: support label filtering +func getListDeviceProfileURL(address string, opts devcli.ListOptions) (string, error) { + url := fmt.Sprintf("http://%s%s/all?limit=-1", address, DeviceProfilePath) + return url, nil +} + +func (cdc *EdgexDeviceProfile) List(ctx context.Context, opts devcli.ListOptions) ([]v1alpha1.DeviceProfile, error) { + klog.V(5).Info("will list DeviceProfiles") + lp, err := getListDeviceProfileURL(cdc.CoreMetaAddr, opts) + if err != nil { + return nil, err + } + resp, err := cdc.R().EnableTrace().Get(lp) + if err != nil { + return nil, err + } + var mdpResp responses.MultiDeviceProfilesResponse + if err := json.Unmarshal(resp.Body(), &mdpResp); err != nil { + return nil, err + } + var deviceProfiles []v1alpha1.DeviceProfile + for _, dp := range mdpResp.Profiles { + deviceProfiles = append(deviceProfiles, toKubeDeviceProfile(&dp)) + } + return deviceProfiles, nil +} + +func (cdc *EdgexDeviceProfile) Get(ctx context.Context, name string, opts devcli.GetOptions) (*v1alpha1.DeviceProfile, error) { + klog.V(5).Infof("will get DeviceProfiles: %s", name) + var dpResp responses.DeviceProfileResponse + getURL := fmt.Sprintf("http://%s%s/name/%s", cdc.CoreMetaAddr, DeviceProfilePath, name) + resp, err := cdc.R().Get(getURL) + if err != nil { + return nil, err + } + if resp.StatusCode() == http.StatusNotFound { + return nil, fmt.Errorf("DeviceProfile %s not found", name) + } + if err = json.Unmarshal(resp.Body(), &dpResp); err != nil { + return nil, err + } + kubedp := toKubeDeviceProfile(&dpResp.Profile) + return &kubedp, nil +} + +func (cdc *EdgexDeviceProfile) Create(ctx context.Context, deviceProfile *v1alpha1.DeviceProfile, opts devcli.CreateOptions) (*v1alpha1.DeviceProfile, error) { + dps := []*v1alpha1.DeviceProfile{deviceProfile} + req := makeEdgeXDeviceProfilesRequest(dps) + klog.V(5).Infof("will add the DeviceProfile: %s", deviceProfile.Name) + reqBody, err := json.Marshal(req) + if err != nil { + return nil, err + } + postURL := fmt.Sprintf("http://%s%s", cdc.CoreMetaAddr, DeviceProfilePath) + resp, err := cdc.R().SetBody(reqBody).Post(postURL) + if err != nil { + return nil, err + } + if resp.StatusCode() != http.StatusMultiStatus { + return nil, fmt.Errorf("create edgex deviceProfile err: %s", string(resp.Body())) // 假定 resp.Body() 存了 msg 信息 + } + var edgexResps []*common.BaseWithIdResponse + if err = json.Unmarshal(resp.Body(), &edgexResps); err != nil { + return nil, err + } + createdDeviceProfile := deviceProfile.DeepCopy() + if len(edgexResps) == 1 { + if edgexResps[0].StatusCode == http.StatusCreated { + createdDeviceProfile.Status.EdgeId = edgexResps[0].Id + createdDeviceProfile.Status.Synced = true + } else { + return nil, fmt.Errorf("create deviceprofile on edgex foundry failed, the response is : %s", resp.Body()) + } + } else { + return nil, fmt.Errorf("edgex BaseWithIdResponse count mismatch DeviceProfile count, the response is : %s", resp.Body()) + } + return createdDeviceProfile, err +} + +// TODO: edgex does not support update DeviceProfile +func (cdc *EdgexDeviceProfile) Update(ctx context.Context, deviceProfile *v1alpha1.DeviceProfile, opts devcli.UpdateOptions) (*v1alpha1.DeviceProfile, error) { + return nil, nil +} + +func (cdc *EdgexDeviceProfile) Delete(ctx context.Context, name string, opts devcli.DeleteOptions) error { + klog.V(5).Infof("will delete the DeviceProfile: %s", name) + delURL := fmt.Sprintf("http://%s%s/name/%s", cdc.CoreMetaAddr, DeviceProfilePath, name) + resp, err := cdc.R().Delete(delURL) + if err != nil { + return err + } + if resp.StatusCode() != http.StatusOK { + return fmt.Errorf("delete edgex deviceProfile err: %s", string(resp.Body())) // 假定 resp.Body() 存了 msg 信息 + } + return nil +} diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go new file mode 100644 index 00000000000..2ca074545ca --- /dev/null +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go @@ -0,0 +1,107 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 edgex_foundry + +import ( + "context" + "encoding/json" + "testing" + + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + + edgex_resp "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/assert" +) + +const ( + DeviceProfileListMetaData = `{"apiVersion":"v2","statusCode":200,"totalCount":5,"profiles":[{"created":1661829206499,"modified":1661829206499,"id":"cf624c1f-c93a-48c0-b327-b00c7dc171f1","name":"Random-Binary-Device","manufacturer":"IOTech","description":"Example of Device-Virtual","model":"Device-Virtual-01","labels":["device-virtual-example"],"deviceResources":[{"description":"Generate random binary value","name":"Binary","isHidden":false,"tag":"","properties":{"valueType":"Binary","readWrite":"R","units":"","minimum":"","maximum":"","defaultValue":"","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":"random"},"attributes":null}],"deviceCommands":[]},{"created":1661829206501,"modified":1661829206501,"id":"adeafefa-2d11-4eee-8fe9-a4742f85f7fb","name":"Random-UnsignedInteger-Device","manufacturer":"IOTech","description":"Example of Device-Virtual","model":"Device-Virtual-01","labels":["device-virtual-example"],"deviceResources":[{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Uint8","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Uint16","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Uint32","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Uint64","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random uint8 value","name":"Uint8","isHidden":false,"tag":"","properties":{"valueType":"Uint8","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random uint16 value","name":"Uint16","isHidden":false,"tag":"","properties":{"valueType":"Uint16","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random uint32 value","name":"Uint32","isHidden":false,"tag":"","properties":{"valueType":"Uint32","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random uint64 value","name":"Uint64","isHidden":false,"tag":"","properties":{"valueType":"Uint64","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Uint8Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Uint16Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Uint32Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Uint64Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random uint8 array value","name":"Uint8Array","isHidden":false,"tag":"","properties":{"valueType":"Uint8Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random uint16 array value","name":"Uint16Array","isHidden":false,"tag":"","properties":{"valueType":"Uint16Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random uint32 array value","name":"Uint32Array","isHidden":false,"tag":"","properties":{"valueType":"Uint32Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random uint64 array value","name":"Uint64Array","isHidden":false,"tag":"","properties":{"valueType":"Uint64Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null}],"deviceCommands":[{"name":"WriteUint8Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Uint8","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Uint8","defaultValue":"false","mappings":null}]},{"name":"WriteUint16Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Uint16","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Uint16","defaultValue":"false","mappings":null}]},{"name":"WriteUint32Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Uint32","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Uint32","defaultValue":"false","mappings":null}]},{"name":"WriteUint64Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Uint64","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Uint64","defaultValue":"false","mappings":null}]},{"name":"WriteUint8ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Uint8Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Uint8Array","defaultValue":"false","mappings":null}]},{"name":"WriteUint16ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Uint16Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Uint16Array","defaultValue":"false","mappings":null}]},{"name":"WriteUint32ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Uint32Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Uint32Array","defaultValue":"false","mappings":null}]},{"name":"WriteUint64ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Uint64Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Uint64Array","defaultValue":"false","mappings":null}]}]},{"created":1661829206500,"modified":1661829206500,"id":"67f4a5a1-06e6-4051-b71d-655ec5dd4eb2","name":"Random-Integer-Device","manufacturer":"IOTech","description":"Example of Device-Virtual","model":"Device-Virtual-01","labels":["device-virtual-example"],"deviceResources":[{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Int8","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Int16","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Int32","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Int64","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random int8 value","name":"Int8","isHidden":false,"tag":"","properties":{"valueType":"Int8","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random int16 value","name":"Int16","isHidden":false,"tag":"","properties":{"valueType":"Int16","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random int32 value","name":"Int32","isHidden":false,"tag":"","properties":{"valueType":"Int32","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random int64 value","name":"Int64","isHidden":false,"tag":"","properties":{"valueType":"Int64","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Int8Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Int16Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Int32Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Int64Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random int8 array value","name":"Int8Array","isHidden":false,"tag":"","properties":{"valueType":"Int8Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random int16 array value","name":"Int16Array","isHidden":false,"tag":"","properties":{"valueType":"Int16Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random int32 array value","name":"Int32Array","isHidden":false,"tag":"","properties":{"valueType":"Int32Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random int64 array value","name":"Int64Array","isHidden":false,"tag":"","properties":{"valueType":"Int64Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null}],"deviceCommands":[{"name":"WriteInt8Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Int8","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Int8","defaultValue":"false","mappings":null}]},{"name":"WriteInt16Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Int16","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Int16","defaultValue":"false","mappings":null}]},{"name":"WriteInt32Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Int32","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Int32","defaultValue":"false","mappings":null}]},{"name":"WriteInt64Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Int64","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Int64","defaultValue":"false","mappings":null}]},{"name":"WriteInt8ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Int8Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Int8Array","defaultValue":"false","mappings":null}]},{"name":"WriteInt16ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Int16Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Int16Array","defaultValue":"false","mappings":null}]},{"name":"WriteInt32ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Int32Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Int32Array","defaultValue":"false","mappings":null}]},{"name":"WriteInt64ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Int64Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Int64Array","defaultValue":"false","mappings":null}]}]},{"created":1661829206500,"modified":1661829206500,"id":"30b8448f-0532-44fb-aed7-5fe4bca16f9a","name":"Random-Float-Device","manufacturer":"IOTech","description":"Example of Device-Virtual","model":"Device-Virtual-01","labels":["device-virtual-example"],"deviceResources":[{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Float32","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Float64","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random float32 value","name":"Float32","isHidden":false,"tag":"","properties":{"valueType":"Float32","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random float64 value","name":"Float64","isHidden":false,"tag":"","properties":{"valueType":"Float64","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"0","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Float32Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Float64Array","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random float32 array value","name":"Float32Array","isHidden":false,"tag":"","properties":{"valueType":"Float32Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random float64 array value","name":"Float64Array","isHidden":false,"tag":"","properties":{"valueType":"Float64Array","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[0]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null}],"deviceCommands":[{"name":"WriteFloat32Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Float32","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Float32","defaultValue":"false","mappings":null}]},{"name":"WriteFloat64Value","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Float64","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Float64","defaultValue":"false","mappings":null}]},{"name":"WriteFloat32ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Float32Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Float32Array","defaultValue":"false","mappings":null}]},{"name":"WriteFloat64ArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Float64Array","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Float64Array","defaultValue":"false","mappings":null}]}]},{"created":1661829206499,"modified":1661829206499,"id":"01dfe04d-f361-41fd-b1c4-7ca0718f461a","name":"Random-Boolean-Device","manufacturer":"IOTech","description":"Example of Device-Virtual","model":"Device-Virtual-01","labels":["device-virtual-example"],"deviceResources":[{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Bool","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random boolean value","name":"Bool","isHidden":false,"tag":"","properties":{"valueType":"Bool","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_BoolArray","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random boolean array value","name":"BoolArray","isHidden":false,"tag":"","properties":{"valueType":"BoolArray","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[true]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null}],"deviceCommands":[{"name":"WriteBoolValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Bool","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Bool","defaultValue":"false","mappings":null}]},{"name":"WriteBoolArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"BoolArray","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_BoolArray","defaultValue":"false","mappings":null}]}]}]}` + DeviceProfileMetaData = `{"apiVersion":"v2","statusCode":200,"profile":{"created":1661829206499,"modified":1661829206499,"id":"01dfe04d-f361-41fd-b1c4-7ca0718f461a","name":"Random-Boolean-Device","manufacturer":"IOTech","description":"Example of Device-Virtual","model":"Device-Virtual-01","labels":["device-virtual-example"],"deviceResources":[{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_Bool","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random boolean value","name":"Bool","isHidden":false,"tag":"","properties":{"valueType":"Bool","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"used to decide whether to re-generate a random value","name":"EnableRandomization_BoolArray","isHidden":true,"tag":"","properties":{"valueType":"Bool","readWrite":"W","units":"","minimum":"","maximum":"","defaultValue":"true","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null},{"description":"Generate random boolean array value","name":"BoolArray","isHidden":false,"tag":"","properties":{"valueType":"BoolArray","readWrite":"RW","units":"","minimum":"","maximum":"","defaultValue":"[true]","mask":"","shift":"","scale":"","offset":"","base":"","assertion":"","mediaType":""},"attributes":null}],"deviceCommands":[{"name":"WriteBoolValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"Bool","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_Bool","defaultValue":"false","mappings":null}]},{"name":"WriteBoolArrayValue","isHidden":false,"readWrite":"W","resourceOperations":[{"deviceResource":"BoolArray","defaultValue":"","mappings":null},{"deviceResource":"EnableRandomization_BoolArray","defaultValue":"false","mappings":null}]}]}}` + + ProfileCreateSuccess = `[{"apiVersion":"v2","statusCode":201,"id":"a583b97d-7c4d-4b7c-8b93-51da9e68518c"}]` + ProfileCreateFail = `[{"apiVersion":"v2","message":"device profile name test-Random-Boolean-Device exists","statusCode":409}]` + + ProfileDeleteSuccess = `{"apiVersion":"v2","statusCode":200}` + ProfileDeleteFail = `{"apiVersion":"v2","message":"fail to delete the device profile with name test-Random-Boolean-Device","statusCode":404}` +) + +var profileClient = NewEdgexDeviceProfile("edgex-core-metadata:59881") + +func Test_ListProfile(t *testing.T) { + + httpmock.ActivateNonDefault(profileClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/deviceprofile/all?limit=-1", + httpmock.NewStringResponder(200, DeviceProfileListMetaData)) + profiles, err := profileClient.List(context.TODO(), clients.ListOptions{}) + assert.Nil(t, err) + + assert.Equal(t, 5, len(profiles)) +} + +func Test_GetProfile(T *testing.T) { + httpmock.ActivateNonDefault(profileClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/deviceprofile/name/Random-Boolean-Device", + httpmock.NewStringResponder(200, DeviceProfileMetaData)) + + _, err := profileClient.Get(context.TODO(), "Random-Boolean-Device", clients.GetOptions{}) + assert.Nil(T, err) +} + +func Test_CreateProfile(t *testing.T) { + httpmock.ActivateNonDefault(profileClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("POST", "http://edgex-core-metadata:59881/api/v2/deviceprofile", + httpmock.NewStringResponder(207, ProfileCreateSuccess)) + + var resp edgex_resp.DeviceProfileResponse + + err := json.Unmarshal([]byte(DeviceProfileMetaData), &resp) + assert.Nil(t, err) + + profile := toKubeDeviceProfile(&resp.Profile) + profile.Name = "test-Random-Boolean-Device" + + _, err = profileClient.Create(context.TODO(), &profile, clients.CreateOptions{}) + assert.Nil(t, err) + + httpmock.RegisterResponder("POST", "http://edgex-core-metadata:59881/api/v2/deviceprofile", + httpmock.NewStringResponder(207, ProfileCreateFail)) + + _, err = profileClient.Create(context.TODO(), &profile, clients.CreateOptions{}) + assert.NotNil(t, err) +} + +func Test_DeleteProfile(t *testing.T) { + httpmock.ActivateNonDefault(profileClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("DELETE", "http://edgex-core-metadata:59881/api/v2/deviceprofile/name/test-Random-Boolean-Device", + httpmock.NewStringResponder(200, ProfileDeleteSuccess)) + + err := profileClient.Delete(context.TODO(), "test-Random-Boolean-Device", clients.DeleteOptions{}) + assert.Nil(t, err) + + httpmock.RegisterResponder("DELETE", "http://edgex-core-metadata:59881/api/v2/deviceprofile/name/test-Random-Boolean-Device", + httpmock.NewStringResponder(404, ProfileDeleteFail)) + + err = profileClient.Delete(context.TODO(), "test-Random-Boolean-Device", clients.DeleteOptions{}) + assert.NotNil(t, err) +} diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go new file mode 100644 index 00000000000..367fd68d633 --- /dev/null +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go @@ -0,0 +1,167 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 edgex_foundry + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" + + "github.com/go-resty/resty/v2" + "k8s.io/klog/v2" +) + +type EdgexDeviceServiceClient struct { + *resty.Client + CoreMetaAddr string +} + +func NewEdgexDeviceServiceClient(coreMetaAddr string) *EdgexDeviceServiceClient { + return &EdgexDeviceServiceClient{ + Client: resty.New(), + CoreMetaAddr: coreMetaAddr, + } +} + +// Create function sends a POST request to EdgeX to add a new deviceService +func (eds *EdgexDeviceServiceClient) Create(ctx context.Context, deviceService *v1alpha1.DeviceService, options edgeCli.CreateOptions) (*v1alpha1.DeviceService, error) { + dss := []*v1alpha1.DeviceService{deviceService} + req := makeEdgeXDeviceService(dss) + klog.V(5).InfoS("will add the DeviceServices", "DeviceService", deviceService.Name) + jsonBody, err := json.Marshal(req) + if err != nil { + return nil, err + } + postPath := fmt.Sprintf("http://%s%s", eds.CoreMetaAddr, DeviceServicePath) + resp, err := eds.R(). + SetBody(jsonBody).Post(postPath) + if err != nil { + return nil, err + } else if resp.StatusCode() != http.StatusMultiStatus { + return nil, fmt.Errorf("create DeviceService on edgex foundry failed, the response is : %s", resp.Body()) + } + + var edgexResps []*common.BaseWithIdResponse + if err = json.Unmarshal(resp.Body(), &edgexResps); err != nil { + return nil, err + } + createdDeviceService := deviceService.DeepCopy() + if len(edgexResps) == 1 { + if edgexResps[0].StatusCode == http.StatusCreated { + createdDeviceService.Status.EdgeId = edgexResps[0].Id + createdDeviceService.Status.Synced = true + } else { + return nil, fmt.Errorf("create DeviceService on edgex foundry failed, the response is : %s", resp.Body()) + } + } else { + return nil, fmt.Errorf("edgex BaseWithIdResponse count mismatch DeviceService count, the response is : %s", resp.Body()) + } + return createdDeviceService, err +} + +// Delete function sends a request to EdgeX to delete a deviceService +func (eds *EdgexDeviceServiceClient) Delete(ctx context.Context, name string, option edgeCli.DeleteOptions) error { + klog.V(5).InfoS("will delete the DeviceService", "DeviceService", name) + delURL := fmt.Sprintf("http://%s%s/name/%s", eds.CoreMetaAddr, DeviceServicePath, name) + resp, err := eds.R().Delete(delURL) + if err != nil { + return err + } + if resp.StatusCode() != http.StatusOK { + return fmt.Errorf("delete edgex deviceservice err: %s", string(resp.Body())) + } + return nil +} + +// Update is used to set the admin or operating state of the deviceService by unique name of the deviceService. +// TODO support to update other fields +func (eds *EdgexDeviceServiceClient) Update(ctx context.Context, ds *v1alpha1.DeviceService, options edgeCli.UpdateOptions) (*v1alpha1.DeviceService, error) { + patchURL := fmt.Sprintf("http://%s%s", eds.CoreMetaAddr, DeviceServicePath) + if ds == nil { + return nil, nil + } + + if ds.Status.EdgeId == "" { + return nil, fmt.Errorf("failed to update deviceservice %s with empty edgex id", ds.Name) + } + edgeDs := toEdgexDeviceService(ds) + edgeDs.Id = ds.Status.EdgeId + dsJson, err := json.Marshal(&edgeDs) + if err != nil { + return nil, err + } + resp, err := eds.R(). + SetBody(dsJson).Patch(patchURL) + if err != nil { + return nil, err + } + + if resp.StatusCode() == http.StatusOK || resp.StatusCode() == http.StatusMultiStatus { + return ds, nil + } else { + return nil, fmt.Errorf("request to patch deviceservice failed, errcode:%d", resp.StatusCode()) + } +} + +// Get is used to query the deviceService information corresponding to the deviceService name +func (eds *EdgexDeviceServiceClient) Get(ctx context.Context, name string, options edgeCli.GetOptions) (*v1alpha1.DeviceService, error) { + klog.V(5).InfoS("will get DeviceServices", "DeviceService", name) + var dsResp responses.DeviceServiceResponse + getURL := fmt.Sprintf("http://%s%s/name/%s", eds.CoreMetaAddr, DeviceServicePath, name) + resp, err := eds.R().Get(getURL) + if err != nil { + return nil, err + } + if resp.StatusCode() == http.StatusNotFound { + return nil, fmt.Errorf("deviceservice %s not found", name) + } + err = json.Unmarshal(resp.Body(), &dsResp) + if err != nil { + return nil, err + } + ds := toKubeDeviceService(dsResp.Service) + return &ds, nil +} + +// List is used to get all deviceService objects on edge platform +// The Hanoi version currently supports only a single label and does not support other filters +func (eds *EdgexDeviceServiceClient) List(ctx context.Context, options edgeCli.ListOptions) ([]v1alpha1.DeviceService, error) { + klog.V(5).Info("will list DeviceServices") + lp := fmt.Sprintf("http://%s%s/all?limit=-1", eds.CoreMetaAddr, DeviceServicePath) + resp, err := eds.R(). + EnableTrace(). + Get(lp) + if err != nil { + return nil, err + } + var mdsResponse responses.MultiDeviceServicesResponse + if err := json.Unmarshal(resp.Body(), &mdsResponse); err != nil { + return nil, err + } + var res []v1alpha1.DeviceService + for _, ds := range mdsResponse.Services { + res = append(res, toKubeDeviceService(ds)) + } + return res, nil +} diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go new file mode 100644 index 00000000000..eafd9edfae6 --- /dev/null +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go @@ -0,0 +1,126 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 edgex_foundry + +import ( + "context" + "encoding/json" + "testing" + + edgex_resp "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" + + "github.com/jarcoal/httpmock" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + "github.com/stretchr/testify/assert" +) + +const ( + DeviceServiceListMetaData = `{"apiVersion":"v2","statusCode":200,"totalCount":1,"services":[{"created":1661829206490,"modified":1661850999190,"id":"74516e96-973d-4cad-bad1-afd4b3a8ea46","name":"device-virtual","baseAddress":"http://edgex-device-virtual:59900","adminState":"UNLOCKED"}]}` + DeviceServiceMetaData = `{"apiVersion":"v2","statusCode":200,"service":{"created":1661829206490,"modified":1661850999190,"id":"74516e96-973d-4cad-bad1-afd4b3a8ea46","name":"device-virtual","baseAddress":"http://edgex-device-virtual:59900","adminState":"UNLOCKED"}}` + ServiceCreateSuccess = `[{"apiVersion":"v2","statusCode":201,"id":"a583b97d-7c4d-4b7c-8b93-51da9e68518c"}]` + ServiceCreateFail = `[{"apiVersion":"v2","message":"device service name test-device-virtual exists","statusCode":409}]` + + ServiceDeleteSuccess = `{"apiVersion":"v2","statusCode":200}` + ServiceDeleteFail = `{"apiVersion":"v2","message":"fail to delete the device profile with name test-Random-Boolean-Device","statusCode":404}` + + ServiceUpdateSuccess = `[{"apiVersion":"v2","statusCode":200}]` + ServiceUpdateFail = `[{"apiVersion":"v2","message":"fail to query object *models.DeviceService, because id: md|ds:01dfe04d-f361-41fd-b1c4-7ca0718f461a doesn't exist in the database","statusCode":404}]` +) + +var serviceClient = NewEdgexDeviceServiceClient("edgex-core-metadata:59881") + +func Test_GetService(t *testing.T) { + httpmock.ActivateNonDefault(serviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/deviceservice/name/device-virtual", + httpmock.NewStringResponder(200, DeviceServiceMetaData)) + + _, err := serviceClient.Get(context.TODO(), "device-virtual", clients.GetOptions{}) + assert.Nil(t, err) +} + +func Test_ListService(t *testing.T) { + httpmock.ActivateNonDefault(serviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/deviceservice/all?limit=-1", + httpmock.NewStringResponder(200, DeviceServiceListMetaData)) + + services, err := serviceClient.List(context.TODO(), clients.ListOptions{}) + assert.Nil(t, err) + assert.Equal(t, 1, len(services)) +} + +func Test_CreateService(t *testing.T) { + httpmock.ActivateNonDefault(serviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("POST", "http://edgex-core-metadata:59881/api/v2/deviceservice", + httpmock.NewStringResponder(207, ServiceCreateSuccess)) + + var resp edgex_resp.DeviceServiceResponse + + err := json.Unmarshal([]byte(DeviceServiceMetaData), &resp) + assert.Nil(t, err) + + service := toKubeDeviceService(resp.Service) + service.Name = "test-device-virtual" + + _, err = serviceClient.Create(context.TODO(), &service, clients.CreateOptions{}) + assert.Nil(t, err) + + httpmock.RegisterResponder("POST", "http://edgex-core-metadata:59881/api/v2/deviceservice", + httpmock.NewStringResponder(207, ServiceCreateFail)) +} + +func Test_DeleteService(t *testing.T) { + httpmock.ActivateNonDefault(serviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("DELETE", "http://edgex-core-metadata:59881/api/v2/deviceservice/name/test-device-virtual", + httpmock.NewStringResponder(200, ServiceDeleteSuccess)) + + err := serviceClient.Delete(context.TODO(), "test-device-virtual", clients.DeleteOptions{}) + assert.Nil(t, err) + + httpmock.RegisterResponder("DELETE", "http://edgex-core-metadata:59881/api/v2/deviceservice/name/test-device-virtual", + httpmock.NewStringResponder(404, ServiceDeleteFail)) + + err = serviceClient.Delete(context.TODO(), "test-device-virtual", clients.DeleteOptions{}) + assert.NotNil(t, err) +} + +func Test_UpdateService(t *testing.T) { + httpmock.ActivateNonDefault(serviceClient.Client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("PATCH", "http://edgex-core-metadata:59881/api/v2/deviceservice", + httpmock.NewStringResponder(200, ServiceUpdateSuccess)) + var resp edgex_resp.DeviceServiceResponse + + err := json.Unmarshal([]byte(DeviceServiceMetaData), &resp) + assert.Nil(t, err) + + service := toKubeDeviceService(resp.Service) + _, err = serviceClient.Update(context.TODO(), &service, clients.UpdateOptions{}) + assert.Nil(t, err) + + httpmock.RegisterResponder("PATCH", "http://edgex-core-metadata:59881/api/v2/deviceservice", + httpmock.NewStringResponder(404, ServiceUpdateFail)) + + _, err = serviceClient.Update(context.TODO(), &service, clients.UpdateOptions{}) + assert.NotNil(t, err) +} diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/edgexobject.go b/pkg/yurtiotcarrier/clients/edgex-foundry/edgexobject.go new file mode 100644 index 00000000000..4a093bf9834 --- /dev/null +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/edgexobject.go @@ -0,0 +1,21 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 edgex_foundry + +type EdgeXObject interface { + IsAddedToEdgeX() bool +} diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/util.go b/pkg/yurtiotcarrier/clients/edgex-foundry/util.go new file mode 100644 index 00000000000..2214e5dab3d --- /dev/null +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/util.go @@ -0,0 +1,456 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 edgex_foundry + +import ( + "fmt" + "strings" + + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/requests" + "github.com/edgexfoundry/go-mod-core-contracts/v2/models" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + EdgeXObjectName = "device-controller/edgex-object.name" + DeviceServicePath = "/api/v2/deviceservice" + DeviceProfilePath = "/api/v2/deviceprofile" + DevicePath = "/api/v2/device" + CommandResponsePath = "/api/v2/device" + + APIVersionV2 = "v2" +) + +type ClientURL struct { + Host string + Port int +} + +func getEdgeXName(provider metav1.Object) string { + var actualDeviceName string + if _, ok := provider.GetLabels()[EdgeXObjectName]; ok { + actualDeviceName = provider.GetLabels()[EdgeXObjectName] + } else { + actualDeviceName = provider.GetName() + } + return actualDeviceName +} + +func toEdgexDeviceService(ds *iotv1alpha1.DeviceService) dtos.DeviceService { + return dtos.DeviceService{ + Description: ds.Spec.Description, + Name: getEdgeXName(ds), + LastConnected: ds.Status.LastConnected, + LastReported: ds.Status.LastReported, + Labels: ds.Spec.Labels, + AdminState: string(ds.Spec.AdminState), + BaseAddress: ds.Spec.BaseAddress, + } +} + +func toEdgeXDeviceResourceSlice(drs []iotv1alpha1.DeviceResource) []dtos.DeviceResource { + var ret []dtos.DeviceResource + for _, dr := range drs { + ret = append(ret, toEdgeXDeviceResource(dr)) + } + return ret +} + +func toEdgeXDeviceResource(dr iotv1alpha1.DeviceResource) dtos.DeviceResource { + genericAttrs := make(map[string]interface{}) + for k, v := range dr.Attributes { + genericAttrs[k] = v + } + + return dtos.DeviceResource{ + Description: dr.Description, + Name: dr.Name, + Tag: dr.Tag, + Properties: toEdgeXProfileProperty(dr.Properties), + Attributes: genericAttrs, + } +} + +func toEdgeXProfileProperty(pp iotv1alpha1.ResourceProperties) dtos.ResourceProperties { + return dtos.ResourceProperties{ + ReadWrite: pp.ReadWrite, + Minimum: pp.Minimum, + Maximum: pp.Maximum, + DefaultValue: pp.DefaultValue, + Mask: pp.Mask, + Shift: pp.Shift, + Scale: pp.Scale, + Offset: pp.Offset, + Base: pp.Base, + Assertion: pp.Assertion, + MediaType: pp.MediaType, + Units: pp.Units, + ValueType: pp.ValueType, + } +} + +func toKubeDeviceService(ds dtos.DeviceService) iotv1alpha1.DeviceService { + return iotv1alpha1.DeviceService{ + ObjectMeta: metav1.ObjectMeta{ + Name: toKubeName(ds.Name), + Namespace: "default", + Labels: map[string]string{ + EdgeXObjectName: ds.Name, + }, + }, + Spec: iotv1alpha1.DeviceServiceSpec{ + Description: ds.Description, + Labels: ds.Labels, + AdminState: iotv1alpha1.AdminState(ds.AdminState), + BaseAddress: ds.BaseAddress, + }, + Status: iotv1alpha1.DeviceServiceStatus{ + EdgeId: ds.Id, + LastConnected: ds.LastConnected, + LastReported: ds.LastReported, + AdminState: iotv1alpha1.AdminState(ds.AdminState), + }, + } +} + +func toEdgeXDevice(d *iotv1alpha1.Device) dtos.Device { + md := dtos.Device{ + Description: d.Spec.Description, + Name: getEdgeXName(d), + AdminState: string(toEdgeXAdminState(d.Spec.AdminState)), + OperatingState: string(toEdgeXOperatingState(d.Spec.OperatingState)), + Protocols: toEdgeXProtocols(d.Spec.Protocols), + LastConnected: d.Status.LastConnected, + LastReported: d.Status.LastReported, + Labels: d.Spec.Labels, + Location: d.Spec.Location, + ServiceName: d.Spec.Service, + ProfileName: d.Spec.Profile, + } + if d.Status.EdgeId != "" { + md.Id = d.Status.EdgeId + } + return md +} + +func toEdgeXUpdateDevice(d *iotv1alpha1.Device) dtos.UpdateDevice { + adminState := string(toEdgeXAdminState(d.Spec.AdminState)) + operationState := string(toEdgeXOperatingState(d.Spec.OperatingState)) + md := dtos.UpdateDevice{ + Description: &d.Spec.Description, + Name: &d.Name, + AdminState: &adminState, + OperatingState: &operationState, + Protocols: toEdgeXProtocols(d.Spec.Protocols), + LastConnected: &d.Status.LastConnected, + LastReported: &d.Status.LastReported, + Labels: d.Spec.Labels, + Location: d.Spec.Location, + ServiceName: &d.Spec.Service, + ProfileName: &d.Spec.Profile, + Notify: &d.Spec.Notify, + } + if d.Status.EdgeId != "" { + md.Id = &d.Status.EdgeId + } + return md +} + +func toEdgeXProtocols( + pps map[string]iotv1alpha1.ProtocolProperties) map[string]dtos.ProtocolProperties { + ret := map[string]dtos.ProtocolProperties{} + for k, v := range pps { + ret[k] = dtos.ProtocolProperties(v) + } + return ret +} + +func toEdgeXAdminState(as iotv1alpha1.AdminState) models.AdminState { + if as == iotv1alpha1.Locked { + return models.Locked + } + return models.Unlocked +} + +func toEdgeXOperatingState(os iotv1alpha1.OperatingState) models.OperatingState { + if os == iotv1alpha1.Up { + return models.Up + } else if os == iotv1alpha1.Down { + return models.Down + } + return models.Unknown +} + +// toKubeDevice serialize the EdgeX Device to the corresponding Kubernetes Device +func toKubeDevice(ed dtos.Device) iotv1alpha1.Device { + var loc string + if ed.Location != nil { + loc = ed.Location.(string) + } + return iotv1alpha1.Device{ + ObjectMeta: metav1.ObjectMeta{ + Name: toKubeName(ed.Name), + Namespace: "default", + Labels: map[string]string{ + EdgeXObjectName: ed.Name, + }, + }, + Spec: iotv1alpha1.DeviceSpec{ + Description: ed.Description, + AdminState: iotv1alpha1.AdminState(ed.AdminState), + OperatingState: iotv1alpha1.OperatingState(ed.OperatingState), + Protocols: toKubeProtocols(ed.Protocols), + Labels: ed.Labels, + Location: loc, + Service: ed.ServiceName, + Profile: ed.ProfileName, + // TODO: Notify + }, + Status: iotv1alpha1.DeviceStatus{ + LastConnected: ed.LastConnected, + LastReported: ed.LastReported, + Synced: true, + EdgeId: ed.Id, + AdminState: iotv1alpha1.AdminState(ed.AdminState), + OperatingState: iotv1alpha1.OperatingState(ed.OperatingState), + }, + } +} + +// toKubeProtocols serialize the EdgeX ProtocolProperties to the corresponding +// Kubernetes OperatingState +func toKubeProtocols( + eps map[string]dtos.ProtocolProperties) map[string]iotv1alpha1.ProtocolProperties { + ret := map[string]iotv1alpha1.ProtocolProperties{} + for k, v := range eps { + ret[k] = iotv1alpha1.ProtocolProperties(v) + } + return ret +} + +// toKubeDeviceProfile create DeviceProfile in cloud according to devicProfile in edge +func toKubeDeviceProfile(dp *dtos.DeviceProfile) iotv1alpha1.DeviceProfile { + return iotv1alpha1.DeviceProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: toKubeName(dp.Name), + Namespace: "default", + Labels: map[string]string{ + EdgeXObjectName: dp.Name, + }, + }, + Spec: iotv1alpha1.DeviceProfileSpec{ + Description: dp.Description, + Manufacturer: dp.Manufacturer, + Model: dp.Model, + Labels: dp.Labels, + DeviceResources: toKubeDeviceResources(dp.DeviceResources), + DeviceCommands: toKubeDeviceCommand(dp.DeviceCommands), + }, + Status: iotv1alpha1.DeviceProfileStatus{ + EdgeId: dp.Id, + Synced: true, + }, + } +} + +func toKubeDeviceCommand(dcs []dtos.DeviceCommand) []iotv1alpha1.DeviceCommand { + var ret []iotv1alpha1.DeviceCommand + for _, dc := range dcs { + ret = append(ret, iotv1alpha1.DeviceCommand{ + Name: dc.Name, + ReadWrite: dc.ReadWrite, + IsHidden: dc.IsHidden, + ResourceOperations: toKubeResourceOperations(dc.ResourceOperations), + }) + } + return ret +} + +func toEdgeXDeviceCommand(dcs []iotv1alpha1.DeviceCommand) []dtos.DeviceCommand { + var ret []dtos.DeviceCommand + for _, dc := range dcs { + ret = append(ret, dtos.DeviceCommand{ + Name: dc.Name, + ReadWrite: dc.ReadWrite, + IsHidden: dc.IsHidden, + ResourceOperations: toEdgeXResourceOperations(dc.ResourceOperations), + }) + } + return ret +} + +func toKubeResourceOperations(ros []dtos.ResourceOperation) []iotv1alpha1.ResourceOperation { + var ret []iotv1alpha1.ResourceOperation + for _, ro := range ros { + ret = append(ret, iotv1alpha1.ResourceOperation{ + DeviceResource: ro.DeviceResource, + Mappings: ro.Mappings, + DefaultValue: ro.DefaultValue, + }) + } + return ret +} + +func toEdgeXResourceOperations(ros []iotv1alpha1.ResourceOperation) []dtos.ResourceOperation { + var ret []dtos.ResourceOperation + for _, ro := range ros { + ret = append(ret, dtos.ResourceOperation{ + DeviceResource: ro.DeviceResource, + Mappings: ro.Mappings, + DefaultValue: ro.DefaultValue, + }) + } + return ret +} + +func toKubeDeviceResources(drs []dtos.DeviceResource) []iotv1alpha1.DeviceResource { + var ret []iotv1alpha1.DeviceResource + for _, dr := range drs { + ret = append(ret, toKubeDeviceResource(dr)) + } + return ret +} + +func toKubeDeviceResource(dr dtos.DeviceResource) iotv1alpha1.DeviceResource { + concreteAttrs := make(map[string]string) + for k, v := range dr.Attributes { + switch asserted := v.(type) { + case string: + concreteAttrs[k] = asserted + continue + case int: + concreteAttrs[k] = fmt.Sprintf("%d", asserted) + continue + case float64: + concreteAttrs[k] = fmt.Sprintf("%f", asserted) + continue + case fmt.Stringer: + concreteAttrs[k] = asserted.String() + continue + } + } + + return iotv1alpha1.DeviceResource{ + Description: dr.Description, + Name: dr.Name, + Tag: dr.Tag, + IsHidden: dr.IsHidden, + Properties: toKubeProfileProperty(dr.Properties), + Attributes: concreteAttrs, + } +} + +func toKubeProfileProperty(rp dtos.ResourceProperties) iotv1alpha1.ResourceProperties { + return iotv1alpha1.ResourceProperties{ + ValueType: rp.ValueType, + ReadWrite: rp.ReadWrite, + Minimum: rp.Minimum, + Maximum: rp.Maximum, + DefaultValue: rp.DefaultValue, + Mask: rp.Mask, + Shift: rp.Shift, + Scale: rp.Scale, + Offset: rp.Offset, + Base: rp.Base, + Assertion: rp.Assertion, + MediaType: rp.MediaType, + Units: rp.Units, + } +} + +// toEdgeXDeviceProfile create DeviceProfile in edge according to devicProfile in cloud +func toEdgeXDeviceProfile(dp *iotv1alpha1.DeviceProfile) dtos.DeviceProfile { + return dtos.DeviceProfile{ + DeviceProfileBasicInfo: dtos.DeviceProfileBasicInfo{ + Description: dp.Spec.Description, + Name: getEdgeXName(dp), + Manufacturer: dp.Spec.Manufacturer, + Model: dp.Spec.Model, + Labels: dp.Spec.Labels, + }, + DeviceResources: toEdgeXDeviceResourceSlice(dp.Spec.DeviceResources), + DeviceCommands: toEdgeXDeviceCommand(dp.Spec.DeviceCommands), + } +} + +func makeEdgeXDeviceProfilesRequest(dps []*iotv1alpha1.DeviceProfile) []*requests.DeviceProfileRequest { + var req []*requests.DeviceProfileRequest + for _, dp := range dps { + req = append(req, &requests.DeviceProfileRequest{ + BaseRequest: common.BaseRequest{ + Versionable: common.Versionable{ + ApiVersion: APIVersionV2, + }, + }, + Profile: toEdgeXDeviceProfile(dp), + }) + } + return req +} + +func makeEdgeXDeviceUpdateRequest(devs []*iotv1alpha1.Device) []*requests.UpdateDeviceRequest { + var req []*requests.UpdateDeviceRequest + for _, dev := range devs { + req = append(req, &requests.UpdateDeviceRequest{ + BaseRequest: common.BaseRequest{ + Versionable: common.Versionable{ + ApiVersion: APIVersionV2, + }, + }, + Device: toEdgeXUpdateDevice(dev), + }) + } + return req +} + +func makeEdgeXDeviceRequest(devs []*iotv1alpha1.Device) []*requests.AddDeviceRequest { + var req []*requests.AddDeviceRequest + for _, dev := range devs { + req = append(req, &requests.AddDeviceRequest{ + BaseRequest: common.BaseRequest{ + Versionable: common.Versionable{ + ApiVersion: APIVersionV2, + }, + }, + Device: toEdgeXDevice(dev), + }) + } + return req +} + +func makeEdgeXDeviceService(dss []*iotv1alpha1.DeviceService) []*requests.AddDeviceServiceRequest { + var req []*requests.AddDeviceServiceRequest + for _, ds := range dss { + req = append(req, &requests.AddDeviceServiceRequest{ + BaseRequest: common.BaseRequest{ + Versionable: common.Versionable{ + ApiVersion: APIVersionV2, + }, + }, + Service: toEdgexDeviceService(ds), + }) + } + return req +} + +func toKubeName(edgexName string) string { + return strings.ReplaceAll(strings.ToLower(edgexName), "_", "-") +} diff --git a/pkg/yurtiotcarrier/clients/errors.go b/pkg/yurtiotcarrier/clients/errors.go new file mode 100644 index 00000000000..b5d6d5b6a2b --- /dev/null +++ b/pkg/yurtiotcarrier/clients/errors.go @@ -0,0 +1,29 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 clients + +import "strings" + +type NotFoundError struct{} + +func (e *NotFoundError) Error() string { return "Item not found" } + +func IsNotFoundErr(err error) bool { + if err == nil { + return false + } + return strings.Contains(err.Error(), "not found") || strings.HasPrefix(err.Error(), "no item found") +} diff --git a/pkg/yurtiotcarrier/clients/interface.go b/pkg/yurtiotcarrier/clients/interface.go new file mode 100644 index 00000000000..fdfe654fea1 --- /dev/null +++ b/pkg/yurtiotcarrier/clients/interface.go @@ -0,0 +1,86 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 clients + +import ( + "context" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" +) + +// CreateOptions defines additional options when creating an object +// Additional general field definitions can be added +type CreateOptions struct{} + +// DeleteOptions defines additional options when deleting an object +// Additional general field definitions can be added +type DeleteOptions struct{} + +// UpdateOptions defines additional options when updating an object +// Additional general field definitions can be added +type UpdateOptions struct{} + +// GetOptions defines additional options when getting an object +// Additional general field definitions can be added +type GetOptions struct{} + +// ListOptions defines additional options when listing an object +type ListOptions struct { + // A selector to restrict the list of returned objects by their labels. + // Defaults to everything. + // +optional + LabelSelector map[string]string + // A selector to restrict the list of returned objects by their fields. + // Defaults to everything. + // +optional + FieldSelector map[string]string +} + +// DeviceInterface defines the interfaces which used to create, delete, update, get and list Device objects on edge-side platform +type DeviceInterface interface { + DevicePropertyInterface + Create(ctx context.Context, device *iotv1alpha1.Device, options CreateOptions) (*iotv1alpha1.Device, error) + Delete(ctx context.Context, name string, options DeleteOptions) error + Update(ctx context.Context, device *iotv1alpha1.Device, options UpdateOptions) (*iotv1alpha1.Device, error) + Get(ctx context.Context, name string, options GetOptions) (*iotv1alpha1.Device, error) + List(ctx context.Context, options ListOptions) ([]iotv1alpha1.Device, error) +} + +// DevicePropertyInterface defines the interfaces which used to get, list and set the actual status value of the device properties +type DevicePropertyInterface interface { + GetPropertyState(ctx context.Context, propertyName string, device *iotv1alpha1.Device, options GetOptions) (*iotv1alpha1.ActualPropertyState, error) + UpdatePropertyState(ctx context.Context, propertyName string, device *iotv1alpha1.Device, options UpdateOptions) error + ListPropertiesState(ctx context.Context, device *iotv1alpha1.Device, options ListOptions) (map[string]iotv1alpha1.DesiredPropertyState, map[string]iotv1alpha1.ActualPropertyState, error) +} + +// DeviceServiceInterface defines the interfaces which used to create, delete, update, get and list DeviceService objects on edge-side platform +type DeviceServiceInterface interface { + Create(ctx context.Context, deviceService *iotv1alpha1.DeviceService, options CreateOptions) (*iotv1alpha1.DeviceService, error) + Delete(ctx context.Context, name string, options DeleteOptions) error + Update(ctx context.Context, deviceService *iotv1alpha1.DeviceService, options UpdateOptions) (*iotv1alpha1.DeviceService, error) + Get(ctx context.Context, name string, options GetOptions) (*iotv1alpha1.DeviceService, error) + List(ctx context.Context, options ListOptions) ([]iotv1alpha1.DeviceService, error) +} + +// DeviceProfileInterface defines the interfaces which used to create, delete, update, get and list DeviceProfile objects on edge-side platform +type DeviceProfileInterface interface { + Create(ctx context.Context, deviceProfile *iotv1alpha1.DeviceProfile, options CreateOptions) (*iotv1alpha1.DeviceProfile, error) + Delete(ctx context.Context, name string, options DeleteOptions) error + Update(ctx context.Context, deviceProfile *iotv1alpha1.DeviceProfile, options UpdateOptions) (*iotv1alpha1.DeviceProfile, error) + Get(ctx context.Context, name string, options GetOptions) (*iotv1alpha1.DeviceProfile, error) + List(ctx context.Context, options ListOptions) ([]iotv1alpha1.DeviceProfile, error) +} diff --git a/pkg/yurtiotcarrier/controllers/device_controller.go b/pkg/yurtiotcarrier/controllers/device_controller.go new file mode 100644 index 00000000000..fb3d2216a93 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/device_controller.go @@ -0,0 +1,296 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + util "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + corev1 "k8s.io/api/core/v1" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// DeviceReconciler reconciles a Device object +type DeviceReconciler struct { + client.Client + Scheme *runtime.Scheme + deviceCli clients.DeviceInterface + // which nodePool deviceController is deployed in + NodePool string +} + +//+kubebuilder:rbac:groups=device.openyurt.io,resources=devices,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=device.openyurt.io,resources=devices/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=device.openyurt.io,resources=devices/finalizers,verbs=update + +func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var d iotv1alpha1.Device + if err := r.Get(ctx, req.NamespacedName, &d); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // If objects doesn't belong to the Edge platform to which the controller is connected, the controller does not handle events for that object + if d.Spec.NodePool != r.NodePool { + return ctrl.Result{}, nil + } + klog.V(3).Infof("Reconciling the Device: %s", d.GetName()) + + deviceStatus := d.Status.DeepCopy() + // Update the conditions for device + defer func() { + if !d.Spec.Managed { + util.SetDeviceCondition(deviceStatus, util.NewDeviceCondition(iotv1alpha1.DeviceManagingCondition, corev1.ConditionFalse, iotv1alpha1.DeviceManagingReason, "")) + } + + err := r.Status().Update(ctx, &d) + if client.IgnoreNotFound(err) != nil { + if !apierrors.IsConflict(err) { + klog.V(4).ErrorS(err, "update device conditions failed", "DeviceName", d.GetName()) + } + } + }() + + // 1. Handle the device deletion event + if err := r.reconcileDeleteDevice(ctx, &d); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } else if !d.ObjectMeta.DeletionTimestamp.IsZero() { + return ctrl.Result{}, nil + } + + if !d.Status.Synced { + // 2. Synchronize OpenYurt device objects to edge platform + if err := r.reconcileCreateDevice(ctx, &d, deviceStatus); err != nil { + if apierrors.IsConflict(err) { + return ctrl.Result{Requeue: true}, nil + } else { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil + } else if d.Spec.Managed { + // 3. If the device has been synchronized and is managed by the cloud, reconcile the device properties + if err := r.reconcileUpdateDevice(ctx, &d, deviceStatus); err != nil { + if apierrors.IsConflict(err) { + return ctrl.Result{RequeueAfter: time.Second * 2}, nil + } + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *DeviceReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIotCarrierOptions) error { + r.deviceCli = edgexCli.NewEdgexDeviceClient(opts.CoreMetadataAddr, opts.CoreCommandAddr) + r.NodePool = opts.Nodepool + + return ctrl.NewControllerManagedBy(mgr). + For(&iotv1alpha1.Device{}). + WithEventFilter(genFirstUpdateFilter("device")). + Complete(r) +} + +func (r *DeviceReconciler) reconcileDeleteDevice(ctx context.Context, d *iotv1alpha1.Device) error { + // gets the actual name of the device on the Edge platform from the Label of the device + edgeDeviceName := util.GetEdgeDeviceName(d, EdgeXObjectName) + if d.ObjectMeta.DeletionTimestamp.IsZero() { + if len(d.GetFinalizers()) == 0 { + patchData, _ := json.Marshal(map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": []string{iotv1alpha1.DeviceFinalizer}, + }, + }) + if err := r.Patch(ctx, d, client.RawPatch(types.MergePatchType, patchData)); err != nil { + return err + } + } + } else { + // delete the device object on the edge platform + err := r.deviceCli.Delete(context.TODO(), edgeDeviceName, clients.DeleteOptions{}) + if err != nil && !clients.IsNotFoundErr(err) { + return err + } + + // delete the device in OpenYurt + patchData, _ := json.Marshal(map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": []string{}, + }, + }) + if err = r.Patch(ctx, d, client.RawPatch(types.MergePatchType, patchData)); err != nil { + return err + } + } + return nil +} + +func (r *DeviceReconciler) reconcileCreateDevice(ctx context.Context, d *iotv1alpha1.Device, deviceStatus *iotv1alpha1.DeviceStatus) error { + // get the actual name of the device on the Edge platform from the Label of the device + edgeDeviceName := util.GetEdgeDeviceName(d, EdgeXObjectName) + newDeviceStatus := d.Status.DeepCopy() + klog.V(4).Infof("Checking if device already exist on the edge platform: %s", d.GetName()) + // Checking if device already exist on the edge platform + edgeDevice, err := r.deviceCli.Get(context.TODO(), edgeDeviceName, clients.GetOptions{}) + if err == nil { + // a. If object exists, the status of the device on OpenYurt is updated + klog.V(4).Infof("Device already exists on edge platform: %s", d.GetName()) + newDeviceStatus.EdgeId = edgeDevice.Status.EdgeId + newDeviceStatus.Synced = true + } else if clients.IsNotFoundErr(err) { + // b. If the object does not exist, a request is sent to the edge platform to create a new device + klog.V(4).Infof("Adding device to the edge platform: %s", d.GetName()) + createdEdgeObj, err := r.deviceCli.Create(context.TODO(), d, clients.CreateOptions{}) + if err != nil { + util.SetDeviceCondition(deviceStatus, util.NewDeviceCondition(iotv1alpha1.DeviceSyncedCondition, corev1.ConditionFalse, iotv1alpha1.DeviceCreateSyncedReason, err.Error())) + return fmt.Errorf("fail to add Device to edge platform: %v", err) + } else { + klog.V(4).Infof("Successfully add Device to edge platform, Name: %s, EdgeId: %s", edgeDeviceName, createdEdgeObj.Status.EdgeId) + newDeviceStatus.EdgeId = createdEdgeObj.Status.EdgeId + newDeviceStatus.Synced = true + } + } else { + klog.V(4).ErrorS(err, "failed to visit the edge platform") + util.SetDeviceCondition(deviceStatus, util.NewDeviceCondition(iotv1alpha1.DeviceSyncedCondition, corev1.ConditionFalse, iotv1alpha1.DeviceVistedCoreMetadataSyncedReason, "")) + return nil + } + d.Status = *newDeviceStatus + util.SetDeviceCondition(deviceStatus, util.NewDeviceCondition(iotv1alpha1.DeviceSyncedCondition, corev1.ConditionTrue, "", "")) + + return r.Status().Update(ctx, d) +} + +func (r *DeviceReconciler) reconcileUpdateDevice(ctx context.Context, d *iotv1alpha1.Device, deviceStatus *iotv1alpha1.DeviceStatus) error { + // the device has been added to the edge platform, check if each device property are in the desired state + newDeviceStatus := d.Status.DeepCopy() + // This list is used to hold the names of properties that failed to reconcile + var failedPropertyNames []string + + // 1. reconciling the AdminState and OperatingState field of device + klog.V(3).Infof("DeviceName: %s, reconciling the AdminState and OperatingState field of device", d.GetName()) + updateDevice := d.DeepCopy() + if d.Spec.AdminState != "" && d.Spec.AdminState != d.Status.AdminState { + newDeviceStatus.AdminState = d.Spec.AdminState + } else { + updateDevice.Spec.AdminState = "" + } + + if d.Spec.OperatingState != "" && d.Spec.OperatingState != d.Status.OperatingState { + newDeviceStatus.OperatingState = d.Spec.OperatingState + } else { + updateDevice.Spec.OperatingState = "" + } + _, err := r.deviceCli.Update(context.TODO(), updateDevice, clients.UpdateOptions{}) + if err != nil { + util.SetDeviceCondition(deviceStatus, util.NewDeviceCondition(iotv1alpha1.DeviceManagingCondition, corev1.ConditionFalse, iotv1alpha1.DeviceUpdateStateReason, err.Error())) + return err + } + + // 2. reconciling the device properties' value + klog.V(3).Infof("DeviceName: %s, reconciling the device properties", d.GetName()) + // property updates are made only when the device is up and unlocked + if newDeviceStatus.OperatingState == iotv1alpha1.Up && newDeviceStatus.AdminState == iotv1alpha1.UnLocked { + newDeviceStatus, failedPropertyNames = r.reconcileDeviceProperties(d, newDeviceStatus) + } + + d.Status = *newDeviceStatus + + // 3. update the device status on OpenYurt + klog.V(3).Infof("DeviceName: %s, update the device status", d.GetName()) + if err := r.Status().Update(ctx, d); err != nil { + util.SetDeviceCondition(deviceStatus, util.NewDeviceCondition(iotv1alpha1.DeviceManagingCondition, corev1.ConditionFalse, iotv1alpha1.DeviceUpdateStateReason, err.Error())) + return err + } else if len(failedPropertyNames) != 0 { + err = fmt.Errorf("the following device properties failed to reconcile: %v", failedPropertyNames) + util.SetDeviceCondition(deviceStatus, util.NewDeviceCondition(iotv1alpha1.DeviceManagingCondition, corev1.ConditionFalse, err.Error(), "")) + return nil + } + + util.SetDeviceCondition(deviceStatus, util.NewDeviceCondition(iotv1alpha1.DeviceManagingCondition, corev1.ConditionTrue, "", "")) + return nil +} + +// Update the actual property value of the device on edge platform, +// return the latest status and the names of the property that failed to update +func (r *DeviceReconciler) reconcileDeviceProperties(d *iotv1alpha1.Device, deviceStatus *iotv1alpha1.DeviceStatus) (*iotv1alpha1.DeviceStatus, []string) { + newDeviceStatus := deviceStatus.DeepCopy() + // This list is used to hold the names of properties that failed to reconcile + var failedPropertyNames []string + // 2. reconciling the device properties' value + klog.V(3).Infof("DeviceName: %s, reconciling the value of device properties", d.GetName()) + for _, desiredProperty := range d.Spec.DeviceProperties { + if desiredProperty.DesiredValue == "" { + continue + } + propertyName := desiredProperty.Name + // 1.1. gets the actual property value of the current device from edge platform + klog.V(4).Infof("DeviceName: %s, getting the actual value of property: %s", d.GetName(), propertyName) + actualProperty, err := r.deviceCli.GetPropertyState(context.TODO(), propertyName, d, clients.GetOptions{}) + if err != nil { + if !clients.IsNotFoundErr(err) { + klog.Errorf("DeviceName: %s, failed to get actual property value of %s, err:%v", d.GetName(), propertyName, err) + failedPropertyNames = append(failedPropertyNames, propertyName) + continue + } + klog.Errorf("DeviceName: %s, property read command not found", d.GetName()) + } else { + klog.V(4).Infof("DeviceName: %s, got the actual property state, {Name: %s, GetURL: %s, ActualValue: %s}", + d.GetName(), propertyName, actualProperty.GetURL, actualProperty.ActualValue) + } + + if newDeviceStatus.DeviceProperties == nil { + newDeviceStatus.DeviceProperties = map[string]iotv1alpha1.ActualPropertyState{} + } else { + newDeviceStatus.DeviceProperties[propertyName] = *actualProperty + } + + // 1.2. set the device attribute in the edge platform to the expected value + if actualProperty == nil || desiredProperty.DesiredValue != actualProperty.ActualValue { + klog.V(4).Infof("DeviceName: %s, the desired value and the actual value are different, desired: %s, actual: %s", + d.GetName(), desiredProperty.DesiredValue, actualProperty.ActualValue) + if err := r.deviceCli.UpdatePropertyState(context.TODO(), propertyName, d, clients.UpdateOptions{}); err != nil { + klog.ErrorS(err, "failed to update property", "DeviceName", d.GetName(), "propertyName", propertyName) + failedPropertyNames = append(failedPropertyNames, propertyName) + continue + } + + klog.V(4).Infof("DeviceName: %s, successfully set the property %s to desired value", d.GetName(), propertyName) + newActualProperty := iotv1alpha1.ActualPropertyState{ + Name: propertyName, + GetURL: actualProperty.GetURL, + ActualValue: desiredProperty.DesiredValue, + } + newDeviceStatus.DeviceProperties[propertyName] = newActualProperty + } + } + return newDeviceStatus, failedPropertyNames +} diff --git a/pkg/yurtiotcarrier/controllers/device_controller_test.go b/pkg/yurtiotcarrier/controllers/device_controller_test.go new file mode 100644 index 00000000000..e0fcc00a037 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/device_controller_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// +kubebuilder:docs-gen:collapse=Imports + +var _ = Describe("Device controller", func() { + + // Define utility constants for object names and testing timeouts/durations and intervals. + const () + + Context("When updating Device Status", func() { + It("Should trigger Device instance", func() { + By("By creating a new Device resource") + ctx := context.Background() + + dev := &iotv1alpha1.Device{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "device.openyurt.io/v1alpha1", + Kind: "Device", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: DeviceName, + Namespace: CommonNamespace, + }, + Spec: iotv1alpha1.DeviceSpec{ + Description: "unit test device", + AdminState: "UNLOCKED", + NodePool: PoolName, + Managed: true, + Notify: true, + Service: ServiceName, + }, + } + Expect(k8sClient.Create(ctx, dev)).Should(Succeed()) + + lookupKey := types.NamespacedName{Name: DeviceName, Namespace: CommonNamespace} + created := &iotv1alpha1.Device{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, lookupKey, created) + return err == nil + }, timeout, interval).Should(BeTrue()) + time.Sleep(5 * time.Second) + Expect(created.Spec.NodePool).Should(Equal(PoolName)) + + Expect(k8sClient.Delete(ctx, created)).Should(Succeed()) + }) + }) + +}) diff --git a/pkg/yurtiotcarrier/controllers/device_syncer.go b/pkg/yurtiotcarrier/controllers/device_syncer.go new file mode 100644 index 00000000000..a07860b2ed4 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/device_syncer.go @@ -0,0 +1,239 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "strings" + "time" + + edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + efCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type DeviceSyncer struct { + // kubernetes client + client.Client + // which nodePool deviceController is deployed in + NodePool string + // edge platform's client + deviceCli edgeCli.DeviceInterface + // syncing period in seconds + syncPeriod time.Duration + Namespace string +} + +// NewDeviceSyncer initialize a New DeviceSyncer +func NewDeviceSyncer(client client.Client, opts *options.YurtIotCarrierOptions) (DeviceSyncer, error) { + return DeviceSyncer{ + syncPeriod: time.Duration(opts.EdgeSyncPeriod) * time.Second, + deviceCli: efCli.NewEdgexDeviceClient(opts.CoreMetadataAddr, opts.CoreCommandAddr), + Client: client, + NodePool: opts.Nodepool, + Namespace: opts.Namespace, + }, nil +} + +// NewDeviceSyncerRunnable initialize a controller-runtime manager runnable +func (ds *DeviceSyncer) NewDeviceSyncerRunnable() ctrlmgr.RunnableFunc { + return func(ctx context.Context) error { + ds.Run(ctx.Done()) + return nil + } +} + +func (ds *DeviceSyncer) Run(stop <-chan struct{}) { + klog.V(1).Info("[Device] Starting the syncer...") + go func() { + for { + <-time.After(ds.syncPeriod) + klog.V(2).Info("[Device] Start a round of synchronization.") + // 1. get device on edge platform and OpenYurt + edgeDevices, kubeDevices, err := ds.getAllDevices() + if err != nil { + klog.V(3).ErrorS(err, "fail to list the devices") + continue + } + + // 2. find the device that need to be synchronized + redundantEdgeDevices, redundantKubeDevices, syncedDevices := ds.findDiffDevice(edgeDevices, kubeDevices) + klog.V(2).Infof("[Device] The number of objects waiting for synchronization { %s:%d, %s:%d, %s:%d }", + "Edge device should be added to OpenYurt", len(redundantEdgeDevices), + "OpenYurt device that should be deleted", len(redundantKubeDevices), + "Devices that should be synchronized", len(syncedDevices)) + + // 3. create device on OpenYurt which are exists in edge platform but not in OpenYurt + if err := ds.syncEdgeToKube(redundantEdgeDevices); err != nil { + klog.V(3).ErrorS(err, "fail to create devices on OpenYurt") + } + + // 4. delete redundant device on OpenYurt + if err := ds.deleteDevices(redundantKubeDevices); err != nil { + klog.V(3).ErrorS(err, "fail to delete redundant devices on OpenYurt") + } + + // 5. update device status on OpenYurt + if err := ds.updateDevices(syncedDevices); err != nil { + klog.V(3).ErrorS(err, "fail to update devices status") + } + klog.V(2).Info("[Device] One round of synchronization is complete") + } + }() + + <-stop + klog.V(1).Info("[Device] Stopping the syncer") +} + +// Get the existing Device on the Edge platform, as well as OpenYurt existing Device +// edgeDevice:map[actualName]device +// kubeDevice:map[actualName]device +func (ds *DeviceSyncer) getAllDevices() (map[string]iotv1alpha1.Device, map[string]iotv1alpha1.Device, error) { + edgeDevice := map[string]iotv1alpha1.Device{} + kubeDevice := map[string]iotv1alpha1.Device{} + // 1. list devices on edge platform + eDevs, err := ds.deviceCli.List(context.TODO(), edgeCli.ListOptions{}) + if err != nil { + klog.V(4).ErrorS(err, "fail to list the devices object on the Edge Platform") + return edgeDevice, kubeDevice, err + } + // 2. list devices on OpenYurt (filter objects belonging to edgeServer) + var kDevs iotv1alpha1.DeviceList + listOptions := client.MatchingFields{util.IndexerPathForNodepool: ds.NodePool} + if err = ds.List(context.TODO(), &kDevs, listOptions, client.InNamespace(ds.Namespace)); err != nil { + klog.V(4).ErrorS(err, "fail to list the devices object on the OpenYurt") + return edgeDevice, kubeDevice, err + } + for i := range eDevs { + deviceName := util.GetEdgeDeviceName(&eDevs[i], EdgeXObjectName) + edgeDevice[deviceName] = eDevs[i] + } + + for i := range kDevs.Items { + deviceName := util.GetEdgeDeviceName(&kDevs.Items[i], EdgeXObjectName) + kubeDevice[deviceName] = kDevs.Items[i] + } + return edgeDevice, kubeDevice, nil +} + +// Get the list of devices that need to be added, deleted and updated +func (ds *DeviceSyncer) findDiffDevice( + edgeDevices map[string]iotv1alpha1.Device, kubeDevices map[string]iotv1alpha1.Device) ( + redundantEdgeDevices map[string]*iotv1alpha1.Device, redundantKubeDevices map[string]*iotv1alpha1.Device, syncedDevices map[string]*iotv1alpha1.Device) { + + redundantEdgeDevices = map[string]*iotv1alpha1.Device{} + redundantKubeDevices = map[string]*iotv1alpha1.Device{} + syncedDevices = map[string]*iotv1alpha1.Device{} + + for i := range edgeDevices { + ed := edgeDevices[i] + edName := util.GetEdgeDeviceName(&ed, EdgeXObjectName) + if _, exists := kubeDevices[edName]; !exists { + klog.V(5).Infof("found redundant edge device %s", edName) + redundantEdgeDevices[edName] = ds.completeCreateContent(&ed) + } else { + klog.V(5).Infof("found device %s to be synced", edName) + kd := kubeDevices[edName] + syncedDevices[edName] = ds.completeUpdateContent(&kd, &ed) + } + } + + for i := range kubeDevices { + kd := kubeDevices[i] + if !kd.Status.Synced { + continue + } + kdName := util.GetEdgeDeviceName(&kd, EdgeXObjectName) + if _, exists := edgeDevices[kdName]; !exists { + redundantKubeDevices[kdName] = &kd + } + } + return +} + +// syncEdgeToKube creates device on OpenYurt which are exists in edge platform but not in OpenYurt +func (ds *DeviceSyncer) syncEdgeToKube(edgeDevs map[string]*iotv1alpha1.Device) error { + for _, ed := range edgeDevs { + if err := ds.Client.Create(context.TODO(), ed); err != nil { + if apierrors.IsAlreadyExists(err) { + continue + } + klog.V(5).ErrorS(err, "fail to create device on OpenYurt", "DeviceName", strings.ToLower(ed.Name)) + return err + } + } + return nil +} + +// deleteDevices deletes redundant device on OpenYurt +func (ds *DeviceSyncer) deleteDevices(redundantKubeDevices map[string]*iotv1alpha1.Device) error { + for _, kd := range redundantKubeDevices { + if err := ds.Client.Delete(context.TODO(), kd); err != nil { + klog.V(5).ErrorS(err, "fail to delete the device on OpenYurt", + "DeviceName", kd.Name) + return err + } + } + return nil +} + +// updateDevicesStatus updates device status on OpenYurt +func (ds *DeviceSyncer) updateDevices(syncedDevices map[string]*iotv1alpha1.Device) error { + for n := range syncedDevices { + if err := ds.Client.Status().Update(context.TODO(), syncedDevices[n]); err != nil { + if apierrors.IsConflict(err) { + klog.V(5).InfoS("update Conflicts", "Device", syncedDevices[n].Name) + continue + } + return err + } + } + return nil +} + +// completeCreateContent completes the content of the device which will be created on OpenYurt +func (ds *DeviceSyncer) completeCreateContent(edgeDevice *iotv1alpha1.Device) *iotv1alpha1.Device { + createDevice := edgeDevice.DeepCopy() + createDevice.Spec.NodePool = ds.NodePool + createDevice.Name = strings.Join([]string{ds.NodePool, createDevice.Name}, "-") + createDevice.Namespace = ds.Namespace + createDevice.Spec.Managed = false + + return createDevice +} + +// completeUpdateContent completes the content of the device which will be updated on OpenYurt +func (ds *DeviceSyncer) completeUpdateContent(kubeDevice *iotv1alpha1.Device, edgeDevice *iotv1alpha1.Device) *iotv1alpha1.Device { + updatedDevice := kubeDevice.DeepCopy() + _, aps, _ := ds.deviceCli.ListPropertiesState(context.TODO(), updatedDevice, edgeCli.ListOptions{}) + // update device status + updatedDevice.Status.LastConnected = edgeDevice.Status.LastConnected + updatedDevice.Status.LastReported = edgeDevice.Status.LastReported + updatedDevice.Status.AdminState = edgeDevice.Status.AdminState + updatedDevice.Status.OperatingState = edgeDevice.Status.OperatingState + updatedDevice.Status.DeviceProperties = aps + return updatedDevice +} diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go b/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go new file mode 100644 index 00000000000..8585ec8ac9c --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go @@ -0,0 +1,165 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + + edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// DeviceProfileReconciler reconciles a DeviceProfile object +type DeviceProfileReconciler struct { + client.Client + Scheme *runtime.Scheme + edgeClient clients.DeviceProfileInterface + NodePool string +} + +//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceprofiles,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceprofiles/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceprofiles/finalizers,verbs=update + +// Reconcile make changes to a deviceprofile object in EdgeX based on it in Kubernetes +func (r *DeviceProfileReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var dp iotv1alpha1.DeviceProfile + if err := r.Get(ctx, req.NamespacedName, &dp); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + if dp.Spec.NodePool != r.NodePool { + return ctrl.Result{}, nil + } + klog.V(3).Infof("Reconciling the DeviceProfile: %s", dp.GetName()) + + // gets the actual name of deviceProfile on the edge platform from the Label of the deviceProfile + dpActualName := util.GetEdgeDeviceProfileName(&dp, EdgeXObjectName) + + // 1. Handle the deviceProfile deletion event + if err := r.reconcileDeleteDeviceProfile(ctx, &dp, dpActualName); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } else if !dp.ObjectMeta.DeletionTimestamp.IsZero() { + return ctrl.Result{}, nil + } + + if !dp.Status.Synced { + // 2. Synchronize OpenYurt deviceProfile to edge platform + if err := r.reconcileCreateDeviceProfile(ctx, &dp, dpActualName); err != nil { + if apierrors.IsConflict(err) { + return ctrl.Result{Requeue: true}, nil + } else { + return ctrl.Result{}, err + } + } + } + // 3. Handle the deviceProfile update event + // TODO + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *DeviceProfileReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIotCarrierOptions) error { + r.edgeClient = edgexclis.NewEdgexDeviceProfile(opts.CoreMetadataAddr) + r.NodePool = opts.Nodepool + + return ctrl.NewControllerManagedBy(mgr). + For(&iotv1alpha1.DeviceProfile{}). + WithEventFilter(genFirstUpdateFilter("deviceprofile")). + Complete(r) +} + +func (r *DeviceProfileReconciler) reconcileDeleteDeviceProfile(ctx context.Context, dp *iotv1alpha1.DeviceProfile, actualName string) error { + if dp.ObjectMeta.DeletionTimestamp.IsZero() { + if len(dp.GetFinalizers()) == 0 { + patchString := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": []string{iotv1alpha1.DeviceProfileFinalizer}, + }, + } + if patchData, err := json.Marshal(patchString); err != nil { + return err + } else { + if err = r.Patch(ctx, dp, client.RawPatch(types.MergePatchType, patchData)); err != nil { + return err + } + } + } + } else { + patchString := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": []string{}, + }, + } + // delete the deviceProfile in OpenYurt + if patchData, err := json.Marshal(patchString); err != nil { + return err + } else { + if err = r.Patch(ctx, dp, client.RawPatch(types.MergePatchType, patchData)); err != nil { + return err + } + } + + // delete the deviceProfile object on edge platform + err := r.edgeClient.Delete(context.TODO(), actualName, clients.DeleteOptions{}) + if err != nil && !clients.IsNotFoundErr(err) { + return err + } + } + return nil +} + +func (r *DeviceProfileReconciler) reconcileCreateDeviceProfile(ctx context.Context, dp *iotv1alpha1.DeviceProfile, actualName string) error { + klog.V(4).Infof("Checking if deviceProfile already exist on the edge platform: %s", dp.GetName()) + if edgeDp, err := r.edgeClient.Get(context.TODO(), actualName, clients.GetOptions{}); err != nil { + if !clients.IsNotFoundErr(err) { + klog.V(4).ErrorS(err, "fail to visit the edge platform") + return nil + } + } else { + // a. If object exists, the status of the deviceProfile on OpenYurt is updated + klog.V(4).Info("DeviceProfile already exists on edge platform") + dp.Status.Synced = true + dp.Status.EdgeId = edgeDp.Status.EdgeId + return r.Status().Update(ctx, dp) + } + + // b. If object does not exist, a request is sent to the edge platform to create a new deviceProfile + createDp, err := r.edgeClient.Create(context.Background(), dp, clients.CreateOptions{}) + if err != nil { + klog.V(4).ErrorS(err, "failed to create deviceProfile on edge platform") + return fmt.Errorf("failed to add deviceProfile to edge platform: %v", err) + } + klog.V(3).Infof("Successfully add DeviceProfile to edge platform, Name: %s, EdgeId: %s", createDp.GetName(), createDp.Status.EdgeId) + dp.Status.EdgeId = createDp.Status.EdgeId + dp.Status.Synced = true + return r.Status().Update(ctx, dp) +} diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_controller_test.go b/pkg/yurtiotcarrier/controllers/deviceprofile_controller_test.go new file mode 100644 index 00000000000..efddd1808ac --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/deviceprofile_controller_test.go @@ -0,0 +1,103 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// +kubebuilder:docs-gen:collapse=Imports + +var _ = Describe("Device controller", func() { + + // Define utility constants for object names and testing timeouts/durations and intervals. + const () + + Context("When updating DeviceProfile Status", func() { + It("Should trigger DeviceProfile instance", func() { + By("By creating a new DeviceProfile resource") + ctx := context.Background() + + rop := iotv1alpha1.ResourceOperation{ + DeviceResource: "EnableRandomization_Bool", + DefaultValue: "false", + } + + cmd := iotv1alpha1.DeviceCommand{ + Name: "WriteBoolValue", + IsHidden: false, + ReadWrite: "W", + ResourceOperations: []iotv1alpha1.ResourceOperation{rop}, + } + + resp := iotv1alpha1.ResourceProperties{ + ReadWrite: "W", + DefaultValue: "true", + ValueType: "Bool", + } + + res := iotv1alpha1.DeviceResource{ + Description: "used to decide whether to re-generate a random value", + Name: "EnableRandomization_Bool", + IsHidden: true, + Properties: resp, + } + + dp := &iotv1alpha1.DeviceProfile{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "device.openyurt.io/v1alpha1", + Kind: "DeviceProfile", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: ProfileName, + Namespace: CommonNamespace, + }, + Spec: iotv1alpha1.DeviceProfileSpec{ + Description: "test device profile", + NodePool: PoolName, + Manufacturer: "OpenYurt", + Model: Model, + DeviceCommands: []iotv1alpha1.DeviceCommand{cmd}, + DeviceResources: []iotv1alpha1.DeviceResource{res}, + }, + } + Expect(k8sClient.Create(ctx, dp)).Should(Succeed()) + + lookupKey := types.NamespacedName{Name: ProfileName, Namespace: CommonNamespace} + created := &iotv1alpha1.DeviceProfile{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, lookupKey, created) + return err == nil + }, timeout, interval).Should(BeTrue()) + time.Sleep(5 * time.Second) + Expect(created.Spec.Model).Should(Equal(Model)) + + Expect(k8sClient.Delete(ctx, created)).Should(Succeed()) + }) + }) + +}) diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go b/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go new file mode 100644 index 00000000000..d5e8805d25e --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go @@ -0,0 +1,214 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "strings" + "time" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + devcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type DeviceProfileSyncer struct { + // syncing period in seconds + syncPeriod time.Duration + // edge platform client + edgeClient devcli.DeviceProfileInterface + // Kubernetes client + client.Client + NodePool string + Namespace string +} + +// NewDeviceProfileSyncer initialize a New DeviceProfileSyncer +func NewDeviceProfileSyncer(client client.Client, opts *options.YurtIotCarrierOptions) (DeviceProfileSyncer, error) { + return DeviceProfileSyncer{ + syncPeriod: time.Duration(opts.EdgeSyncPeriod) * time.Second, + edgeClient: edgexclis.NewEdgexDeviceProfile(opts.CoreMetadataAddr), + Client: client, + NodePool: opts.Nodepool, + Namespace: opts.Namespace, + }, nil +} + +// NewDeviceProfileSyncerRunnable initialize a controller-runtime manager runnable +func (dps *DeviceProfileSyncer) NewDeviceProfileSyncerRunnable() ctrlmgr.RunnableFunc { + return func(ctx context.Context) error { + dps.Run(ctx.Done()) + return nil + } +} + +func (dps *DeviceProfileSyncer) Run(stop <-chan struct{}) { + klog.V(1).Info("[DeviceProfile] Starting the syncer...") + go func() { + for { + <-time.After(dps.syncPeriod) + klog.V(2).Info("[DeviceProfile] Start a round of synchronization.") + + // 1. get deviceProfiles on edge platform and OpenYurt + edgeDeviceProfiles, kubeDeviceProfiles, err := dps.getAllDeviceProfiles() + if err != nil { + klog.V(3).ErrorS(err, "fail to list the deviceProfiles") + continue + } + + // 2. find the deviceProfiles that need to be synchronized + redundantEdgeDeviceProfiles, redundantKubeDeviceProfiles, syncedDeviceProfiles := + dps.findDiffDeviceProfiles(edgeDeviceProfiles, kubeDeviceProfiles) + klog.V(2).Infof("[DeviceProfile] The number of objects waiting for synchronization { %s:%d, %s:%d, %s:%d }", + "Edge deviceProfiles should be added to OpenYurt", len(redundantEdgeDeviceProfiles), + "OpenYurt deviceProfiles that should be deleted", len(redundantKubeDeviceProfiles), + "DeviceProfiles that should be synchronized", len(syncedDeviceProfiles)) + + // 3. create deviceProfiles on OpenYurt which are exists in edge platform but not in OpenYurt + if err := dps.syncEdgeToKube(redundantEdgeDeviceProfiles); err != nil { + klog.V(3).ErrorS(err, "fail to create deviceProfiles on OpenYurt") + } + + // 4. delete redundant deviceProfiles on OpenYurt + if err := dps.deleteDeviceProfiles(redundantKubeDeviceProfiles); err != nil { + klog.V(3).ErrorS(err, "fail to delete redundant deviceProfiles on OpenYurt") + } + + // 5. update deviceProfiles on OpenYurt + // TODO + } + }() + + <-stop + klog.V(1).Info("[DeviceProfile] Stopping the syncer") +} + +// Get the existing DeviceProfile on the Edge platform, as well as OpenYurt existing DeviceProfile +// edgeDeviceProfiles:map[actualName]DeviceProfile +// kubeDeviceProfiles:map[actualName]DeviceProfile +func (dps *DeviceProfileSyncer) getAllDeviceProfiles() ( + map[string]iotv1alpha1.DeviceProfile, map[string]iotv1alpha1.DeviceProfile, error) { + + edgeDeviceProfiles := map[string]iotv1alpha1.DeviceProfile{} + kubeDeviceProfiles := map[string]iotv1alpha1.DeviceProfile{} + + // 1. list deviceProfiles on edge platform + eDps, err := dps.edgeClient.List(context.TODO(), devcli.ListOptions{}) + if err != nil { + klog.V(4).ErrorS(err, "fail to list the deviceProfiles on the edge platform") + return edgeDeviceProfiles, kubeDeviceProfiles, err + } + // 2. list deviceProfiles on OpenYurt (filter objects belonging to edgeServer) + var kDps iotv1alpha1.DeviceProfileList + listOptions := client.MatchingFields{util.IndexerPathForNodepool: dps.NodePool} + if err = dps.List(context.TODO(), &kDps, listOptions, client.InNamespace(dps.Namespace)); err != nil { + klog.V(4).ErrorS(err, "fail to list the deviceProfiles on the Kubernetes") + return edgeDeviceProfiles, kubeDeviceProfiles, err + } + for i := range eDps { + deviceProfilesName := util.GetEdgeDeviceProfileName(&eDps[i], EdgeXObjectName) + edgeDeviceProfiles[deviceProfilesName] = eDps[i] + } + + for i := range kDps.Items { + deviceProfilesName := util.GetEdgeDeviceProfileName(&kDps.Items[i], EdgeXObjectName) + kubeDeviceProfiles[deviceProfilesName] = kDps.Items[i] + } + return edgeDeviceProfiles, kubeDeviceProfiles, nil +} + +// Get the list of deviceProfiles that need to be added, deleted and updated +func (dps *DeviceProfileSyncer) findDiffDeviceProfiles( + edgeDeviceProfiles map[string]iotv1alpha1.DeviceProfile, kubeDeviceProfiles map[string]iotv1alpha1.DeviceProfile) ( + redundantEdgeDeviceProfiles map[string]*iotv1alpha1.DeviceProfile, redundantKubeDeviceProfiles map[string]*iotv1alpha1.DeviceProfile, syncedDeviceProfiles map[string]*iotv1alpha1.DeviceProfile) { + + redundantEdgeDeviceProfiles = map[string]*iotv1alpha1.DeviceProfile{} + redundantKubeDeviceProfiles = map[string]*iotv1alpha1.DeviceProfile{} + syncedDeviceProfiles = map[string]*iotv1alpha1.DeviceProfile{} + + for i := range edgeDeviceProfiles { + edp := edgeDeviceProfiles[i] + edpName := util.GetEdgeDeviceProfileName(&edp, EdgeXObjectName) + if _, exists := kubeDeviceProfiles[edpName]; !exists { + redundantEdgeDeviceProfiles[edpName] = dps.completeCreateContent(&edp) + } else { + kdp := kubeDeviceProfiles[edpName] + syncedDeviceProfiles[edpName] = dps.completeUpdateContent(&kdp, &edp) + } + } + + for i := range kubeDeviceProfiles { + kdp := kubeDeviceProfiles[i] + if !kdp.Status.Synced { + continue + } + kdpName := util.GetEdgeDeviceProfileName(&kdp, EdgeXObjectName) + if _, exists := edgeDeviceProfiles[kdpName]; !exists { + redundantKubeDeviceProfiles[kdpName] = &kdp + } + } + return +} + +// completeCreateContent completes the content of the deviceProfile which will be created on OpenYurt +func (dps *DeviceProfileSyncer) completeCreateContent(edgeDps *iotv1alpha1.DeviceProfile) *iotv1alpha1.DeviceProfile { + createDeviceProfile := edgeDps.DeepCopy() + createDeviceProfile.Namespace = dps.Namespace + createDeviceProfile.Name = strings.Join([]string{dps.NodePool, createDeviceProfile.Name}, "-") + createDeviceProfile.Spec.NodePool = dps.NodePool + return createDeviceProfile +} + +// completeUpdateContent completes the content of the deviceProfile which will be updated on OpenYurt +// TODO +func (dps *DeviceProfileSyncer) completeUpdateContent(kubeDps *iotv1alpha1.DeviceProfile, edgeDS *iotv1alpha1.DeviceProfile) *iotv1alpha1.DeviceProfile { + return kubeDps +} + +// syncEdgeToKube creates deviceProfiles on OpenYurt which are exists in edge platform but not in OpenYurt +func (dps *DeviceProfileSyncer) syncEdgeToKube(edgeDps map[string]*iotv1alpha1.DeviceProfile) error { + for _, edp := range edgeDps { + if err := dps.Client.Create(context.TODO(), edp); err != nil { + if apierrors.IsAlreadyExists(err) { + klog.V(5).Infof("DeviceProfile already exist on Kubernetes: %s", strings.ToLower(edp.Name)) + continue + } + klog.Infof("created deviceProfile failed: %s", strings.ToLower(edp.Name)) + return err + } + } + return nil +} + +// deleteDeviceProfiles deletes redundant deviceProfiles on OpenYurt +func (dps *DeviceProfileSyncer) deleteDeviceProfiles(redundantKubeDeviceProfiles map[string]*iotv1alpha1.DeviceProfile) error { + for _, kdp := range redundantKubeDeviceProfiles { + if err := dps.Client.Delete(context.TODO(), kdp); err != nil { + klog.V(5).ErrorS(err, "fail to delete the DeviceProfile on Kubernetes: %s ", + "DeviceProfile", kdp.Name) + return err + } + } + return nil +} diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_controller.go b/pkg/yurtiotcarrier/controllers/deviceservice_controller.go new file mode 100644 index 00000000000..58052e7007b --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/deviceservice_controller.go @@ -0,0 +1,220 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + util "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + corev1 "k8s.io/api/core/v1" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// DeviceServiceReconciler reconciles a DeviceService object +type DeviceServiceReconciler struct { + client.Client + Scheme *runtime.Scheme + deviceServiceCli clients.DeviceServiceInterface + NodePool string +} + +//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceservices,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceservices/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceservices/finalizers,verbs=update + +func (r *DeviceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var ds iotv1alpha1.DeviceService + if err := r.Get(ctx, req.NamespacedName, &ds); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // If objects doesn't belong to the edge platform to which the controller is connected, the controller does not handle events for that object + if ds.Spec.NodePool != r.NodePool { + return ctrl.Result{}, nil + } + klog.V(3).Infof("Reconciling the DeviceService: %s", ds.GetName()) + + deviceServiceStatus := ds.Status.DeepCopy() + // Update deviceService conditions + defer func() { + if !ds.Spec.Managed { + util.SetDeviceServiceCondition(deviceServiceStatus, util.NewDeviceServiceCondition(iotv1alpha1.DeviceServiceManagingCondition, corev1.ConditionFalse, iotv1alpha1.DeviceServiceManagingReason, "")) + } + + err := r.Status().Update(ctx, &ds) + if client.IgnoreNotFound(err) != nil { + if !apierrors.IsConflict(err) { + klog.V(4).ErrorS(err, "update deviceService conditions failed", "deviceService", ds.GetName()) + } + } + }() + + // 1. Handle the deviceService deletion event + if err := r.reconcileDeleteDeviceService(ctx, &ds); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } else if !ds.ObjectMeta.DeletionTimestamp.IsZero() { + return ctrl.Result{}, nil + } + + if !ds.Status.Synced { + // 2. Synchronize OpenYurt deviceService to edge platform + if err := r.reconcileCreateDeviceService(ctx, &ds, deviceServiceStatus); err != nil { + if apierrors.IsConflict(err) { + return ctrl.Result{Requeue: true}, nil + } else { + return ctrl.Result{}, err + } + } + } else if ds.Spec.Managed { + // 3. If the deviceService has been synchronized and is managed by the cloud, reconcile the deviceService fields + if err := r.reconcileUpdateDeviceService(ctx, &ds, deviceServiceStatus); err != nil { + if apierrors.IsConflict(err) { + return ctrl.Result{Requeue: true}, nil + } else { + return ctrl.Result{}, err + } + } + } + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *DeviceServiceReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIotCarrierOptions) error { + r.deviceServiceCli = edgexCli.NewEdgexDeviceServiceClient(opts.CoreMetadataAddr) + r.NodePool = opts.Nodepool + + return ctrl.NewControllerManagedBy(mgr). + For(&iotv1alpha1.DeviceService{}). + Complete(r) +} + +func (r *DeviceServiceReconciler) reconcileDeleteDeviceService(ctx context.Context, ds *iotv1alpha1.DeviceService) error { + // gets the actual name of deviceService on the edge platform from the Label of the device + edgeDeviceServiceName := util.GetEdgeDeviceServiceName(ds, EdgeXObjectName) + if ds.ObjectMeta.DeletionTimestamp.IsZero() { + if len(ds.GetFinalizers()) == 0 { + patchString := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": []string{iotv1alpha1.DeviceServiceFinalizer}, + }, + } + if patchData, err := json.Marshal(patchString); err != nil { + return err + } else { + if err = r.Patch(ctx, ds, client.RawPatch(types.MergePatchType, patchData)); err != nil { + return err + } + } + } + } else { + patchString := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": []string{}, + }, + } + // delete the deviceService in OpenYurt + if patchData, err := json.Marshal(patchString); err != nil { + return err + } else { + if err = r.Patch(ctx, ds, client.RawPatch(types.MergePatchType, patchData)); err != nil { + return err + } + } + + // delete the deviceService object on edge platform + err := r.deviceServiceCli.Delete(context.TODO(), edgeDeviceServiceName, clients.DeleteOptions{}) + if err != nil && !clients.IsNotFoundErr(err) { + return err + } + } + return nil +} + +func (r *DeviceServiceReconciler) reconcileCreateDeviceService(ctx context.Context, ds *iotv1alpha1.DeviceService, deviceServiceStatus *iotv1alpha1.DeviceServiceStatus) error { + // get the actual name of deviceService on the Edge platform from the Label of the device + edgeDeviceServiceName := util.GetEdgeDeviceServiceName(ds, EdgeXObjectName) + klog.V(4).Infof("Checking if deviceService already exist on the edge platform: %s", ds.GetName()) + // Checking if deviceService already exist on the edge platform + if edgeDs, err := r.deviceServiceCli.Get(context.TODO(), edgeDeviceServiceName, clients.GetOptions{}); err != nil { + if !clients.IsNotFoundErr(err) { + klog.V(4).ErrorS(err, "fail to visit the edge platform") + return nil + } else { + createdDs, err := r.deviceServiceCli.Create(context.TODO(), ds, clients.CreateOptions{}) + if err != nil { + klog.V(4).ErrorS(err, "failed to create deviceService on edge platform") + util.SetDeviceServiceCondition(deviceServiceStatus, util.NewDeviceServiceCondition(iotv1alpha1.DeviceServiceSyncedCondition, corev1.ConditionFalse, iotv1alpha1.DeviceServiceCreateSyncedReason, err.Error())) + return fmt.Errorf("fail to create DeviceService to edge platform: %v", err) + } + + klog.V(4).Infof("Successfully add DeviceService to Edge Platform, Name: %s, EdgeId: %s", ds.GetName(), createdDs.Status.EdgeId) + ds.Status.EdgeId = createdDs.Status.EdgeId + ds.Status.Synced = true + util.SetDeviceServiceCondition(deviceServiceStatus, util.NewDeviceServiceCondition(iotv1alpha1.DeviceServiceSyncedCondition, corev1.ConditionTrue, "", "")) + return r.Status().Update(ctx, ds) + } + } else { + // a. If object exists, the status of the device on OpenYurt is updated + klog.V(4).Infof("DeviceServiceName: %s, obj already exists on edge platform", ds.GetName()) + ds.Status.Synced = true + ds.Status.EdgeId = edgeDs.Status.EdgeId + return r.Status().Update(ctx, ds) + } +} + +func (r *DeviceServiceReconciler) reconcileUpdateDeviceService(ctx context.Context, ds *iotv1alpha1.DeviceService, deviceServiceStatus *iotv1alpha1.DeviceServiceStatus) error { + // 1. reconciling the AdminState field of deviceService + newDeviceServiceStatus := ds.Status.DeepCopy() + updateDeviceService := ds.DeepCopy() + + if ds.Spec.AdminState != "" && ds.Spec.AdminState != ds.Status.AdminState { + newDeviceServiceStatus.AdminState = ds.Spec.AdminState + } else { + updateDeviceService.Spec.AdminState = "" + } + + _, err := r.deviceServiceCli.Update(context.TODO(), updateDeviceService, clients.UpdateOptions{}) + if err != nil { + util.SetDeviceServiceCondition(deviceServiceStatus, util.NewDeviceServiceCondition(iotv1alpha1.DeviceServiceManagingCondition, corev1.ConditionFalse, iotv1alpha1.DeviceServiceUpdateStatusSyncedReason, err.Error())) + + return err + } + + // 2. update the device status on OpenYurt + ds.Status = *newDeviceServiceStatus + if err = r.Status().Update(ctx, ds); err != nil { + util.SetDeviceServiceCondition(deviceServiceStatus, util.NewDeviceServiceCondition(iotv1alpha1.DeviceServiceManagingCondition, corev1.ConditionFalse, iotv1alpha1.DeviceServiceUpdateStatusSyncedReason, err.Error())) + + return err + } + util.SetDeviceServiceCondition(deviceServiceStatus, util.NewDeviceServiceCondition(iotv1alpha1.DeviceServiceManagingCondition, corev1.ConditionTrue, "", "")) + return nil +} diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_controller_test.go b/pkg/yurtiotcarrier/controllers/deviceservice_controller_test.go new file mode 100644 index 00000000000..29c9af1f4e4 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/deviceservice_controller_test.go @@ -0,0 +1,75 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// +kubebuilder:docs-gen:collapse=Imports + +var _ = Describe("Device controller", func() { + + // Define utility constants for object names and testing timeouts/durations and intervals. + const () + + Context("When updating DeviceService Status", func() { + It("Should trigger DeviceService instance", func() { + By("By creating a new DeviceService resource") + ctx := context.Background() + + ds := &iotv1alpha1.DeviceService{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "device.openyurt.io/v1alpha1", + Kind: "DeviceService", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: ServiceName, + Namespace: CommonNamespace, + }, + Spec: iotv1alpha1.DeviceServiceSpec{ + Description: "test device service", + NodePool: PoolName, + AdminState: "UNLOCKED", + BaseAddress: "http://test-device:59900", + }, + } + Expect(k8sClient.Create(ctx, ds)).Should(Succeed()) + + lookupKey := types.NamespacedName{Name: ServiceName, Namespace: CommonNamespace} + created := &iotv1alpha1.DeviceService{} + + Eventually(func() bool { + err := k8sClient.Get(ctx, lookupKey, created) + return err == nil + }, timeout, interval).Should(BeTrue()) + time.Sleep(5 * time.Second) + Expect(created.Spec.NodePool).Should(Equal(PoolName)) + + Expect(k8sClient.Delete(ctx, created)).Should(Succeed()) + }) + }) + +}) diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go b/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go new file mode 100644 index 00000000000..d58ace0a04a --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go @@ -0,0 +1,237 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + "context" + "strings" + "time" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + iotcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type DeviceServiceSyncer struct { + // Kubernetes client + client.Client + // syncing period in seconds + syncPeriod time.Duration + deviceServiceCli iotcli.DeviceServiceInterface + NodePool string + Namespace string +} + +func NewDeviceServiceSyncer(client client.Client, opts *options.YurtIotCarrierOptions) (DeviceServiceSyncer, error) { + return DeviceServiceSyncer{ + syncPeriod: time.Duration(opts.EdgeSyncPeriod) * time.Second, + deviceServiceCli: edgexCli.NewEdgexDeviceServiceClient(opts.CoreMetadataAddr), + Client: client, + NodePool: opts.Nodepool, + Namespace: opts.Namespace, + }, nil +} + +func (ds *DeviceServiceSyncer) NewDeviceServiceSyncerRunnable() ctrlmgr.RunnableFunc { + return func(ctx context.Context) error { + ds.Run(ctx.Done()) + return nil + } +} + +func (ds *DeviceServiceSyncer) Run(stop <-chan struct{}) { + klog.V(1).Info("[DeviceService] Starting the syncer...") + go func() { + for { + <-time.After(ds.syncPeriod) + klog.V(2).Info("[DeviceService] Start a round of synchronization.") + // 1. get deviceServices on edge platform and OpenYurt + edgeDeviceServices, kubeDeviceServices, err := ds.getAllDeviceServices() + if err != nil { + klog.V(3).ErrorS(err, "fail to list the deviceServices") + continue + } + + // 2. find the deviceServices that need to be synchronized + redundantEdgeDeviceServices, redundantKubeDeviceServices, syncedDeviceServices := + ds.findDiffDeviceServices(edgeDeviceServices, kubeDeviceServices) + klog.V(2).Infof("[DeviceService] The number of objects waiting for synchronization { %s:%d, %s:%d, %s:%d }", + "Edge deviceServices should be added to OpenYurt", len(redundantEdgeDeviceServices), + "OpenYurt deviceServices that should be deleted", len(redundantKubeDeviceServices), + "DeviceServices that should be synchronized", len(syncedDeviceServices)) + + // 3. create deviceServices on OpenYurt which are exists in edge platform but not in OpenYurt + if err := ds.syncEdgeToKube(redundantEdgeDeviceServices); err != nil { + klog.V(3).ErrorS(err, "fail to create deviceServices on OpenYurt") + } + + // 4. delete redundant deviceServices on OpenYurt + if err := ds.deleteDeviceServices(redundantKubeDeviceServices); err != nil { + klog.V(3).ErrorS(err, "fail to delete redundant deviceServices on OpenYurt") + } + + // 5. update deviceService status on OpenYurt + if err := ds.updateDeviceServices(syncedDeviceServices); err != nil { + klog.V(3).ErrorS(err, "fail to update deviceServices") + } + klog.V(2).Info("[DeviceService] One round of synchronization is complete") + } + }() + + <-stop + klog.V(1).Info("[DeviceService] Stopping the syncer") +} + +// Get the existing DeviceService on the Edge platform, as well as OpenYurt existing DeviceService +// edgeDeviceServices:map[actualName]DeviceService +// kubeDeviceServices:map[actualName]DeviceService +func (ds *DeviceServiceSyncer) getAllDeviceServices() ( + map[string]iotv1alpha1.DeviceService, map[string]iotv1alpha1.DeviceService, error) { + + edgeDeviceServices := map[string]iotv1alpha1.DeviceService{} + kubeDeviceServices := map[string]iotv1alpha1.DeviceService{} + + // 1. list deviceServices on edge platform + eDevSs, err := ds.deviceServiceCli.List(context.TODO(), iotcli.ListOptions{}) + if err != nil { + klog.V(4).ErrorS(err, "fail to list the deviceServices object on the edge platform") + return edgeDeviceServices, kubeDeviceServices, err + } + // 2. list deviceServices on OpenYurt (filter objects belonging to edgeServer) + var kDevSs iotv1alpha1.DeviceServiceList + listOptions := client.MatchingFields{util.IndexerPathForNodepool: ds.NodePool} + if err = ds.List(context.TODO(), &kDevSs, listOptions, client.InNamespace(ds.Namespace)); err != nil { + klog.V(4).ErrorS(err, "fail to list the deviceServices object on the Kubernetes") + return edgeDeviceServices, kubeDeviceServices, err + } + for i := range eDevSs { + deviceServicesName := util.GetEdgeDeviceServiceName(&eDevSs[i], EdgeXObjectName) + edgeDeviceServices[deviceServicesName] = eDevSs[i] + } + + for i := range kDevSs.Items { + deviceServicesName := util.GetEdgeDeviceServiceName(&kDevSs.Items[i], EdgeXObjectName) + kubeDeviceServices[deviceServicesName] = kDevSs.Items[i] + } + return edgeDeviceServices, kubeDeviceServices, nil +} + +// Get the list of deviceServices that need to be added, deleted and updated +func (ds *DeviceServiceSyncer) findDiffDeviceServices( + edgeDeviceService map[string]iotv1alpha1.DeviceService, kubeDeviceService map[string]iotv1alpha1.DeviceService) ( + redundantEdgeDeviceServices map[string]*iotv1alpha1.DeviceService, redundantKubeDeviceServices map[string]*iotv1alpha1.DeviceService, syncedDeviceServices map[string]*iotv1alpha1.DeviceService) { + + redundantEdgeDeviceServices = map[string]*iotv1alpha1.DeviceService{} + redundantKubeDeviceServices = map[string]*iotv1alpha1.DeviceService{} + syncedDeviceServices = map[string]*iotv1alpha1.DeviceService{} + + for i := range edgeDeviceService { + eds := edgeDeviceService[i] + edName := util.GetEdgeDeviceServiceName(&eds, EdgeXObjectName) + if _, exists := kubeDeviceService[edName]; !exists { + redundantEdgeDeviceServices[edName] = ds.completeCreateContent(&eds) + } else { + kd := kubeDeviceService[edName] + syncedDeviceServices[edName] = ds.completeUpdateContent(&kd, &eds) + } + } + + for i := range kubeDeviceService { + kds := kubeDeviceService[i] + if !kds.Status.Synced { + continue + } + kdName := util.GetEdgeDeviceServiceName(&kds, EdgeXObjectName) + if _, exists := edgeDeviceService[kdName]; !exists { + redundantKubeDeviceServices[kdName] = &kds + } + } + return +} + +// syncEdgeToKube creates deviceServices on OpenYurt which are exists in edge platform but not in OpenYurt +func (ds *DeviceServiceSyncer) syncEdgeToKube(edgeDevs map[string]*iotv1alpha1.DeviceService) error { + for _, ed := range edgeDevs { + if err := ds.Client.Create(context.TODO(), ed); err != nil { + if apierrors.IsAlreadyExists(err) { + klog.V(5).InfoS("DeviceService already exist on Kubernetes", + "DeviceService", strings.ToLower(ed.Name)) + continue + } + klog.InfoS("created deviceService failed:", "DeviceService", strings.ToLower(ed.Name)) + return err + } + } + return nil +} + +// deleteDeviceServices deletes redundant deviceServices on OpenYurt +func (ds *DeviceServiceSyncer) deleteDeviceServices(redundantKubeDeviceServices map[string]*iotv1alpha1.DeviceService) error { + for _, kds := range redundantKubeDeviceServices { + if err := ds.Client.Delete(context.TODO(), kds); err != nil { + klog.V(5).ErrorS(err, "fail to delete the DeviceService on Kubernetes", + "DeviceService", kds.Name) + return err + } + } + return nil +} + +// updateDeviceServices updates deviceServices status on OpenYurt +func (ds *DeviceServiceSyncer) updateDeviceServices(syncedDeviceServices map[string]*iotv1alpha1.DeviceService) error { + for _, sd := range syncedDeviceServices { + if sd.ObjectMeta.ResourceVersion == "" { + continue + } + if err := ds.Client.Status().Update(context.TODO(), sd); err != nil { + if apierrors.IsConflict(err) { + klog.V(5).InfoS("update Conflicts", "DeviceService", sd.Name) + continue + } + klog.V(5).ErrorS(err, "fail to update the DeviceService on Kubernetes", + "DeviceService", sd.Name) + return err + } + } + return nil +} + +// completeCreateContent completes the content of the deviceService which will be created on OpenYurt +func (ds *DeviceServiceSyncer) completeCreateContent(edgeDS *iotv1alpha1.DeviceService) *iotv1alpha1.DeviceService { + createDevice := edgeDS.DeepCopy() + createDevice.Spec.NodePool = ds.NodePool + createDevice.Namespace = ds.Namespace + createDevice.Name = strings.Join([]string{ds.NodePool, edgeDS.Name}, "-") + createDevice.Spec.Managed = false + return createDevice +} + +// completeUpdateContent completes the content of the deviceService which will be updated on OpenYurt +func (ds *DeviceServiceSyncer) completeUpdateContent(kubeDS *iotv1alpha1.DeviceService, edgeDS *iotv1alpha1.DeviceService) *iotv1alpha1.DeviceService { + updatedDS := kubeDS.DeepCopy() + // update device status + updatedDS.Status.LastConnected = edgeDS.Status.LastConnected + updatedDS.Status.LastReported = edgeDS.Status.LastReported + updatedDS.Status.AdminState = edgeDS.Status.AdminState + return updatedDS +} diff --git a/pkg/yurtiotcarrier/controllers/predicate.go b/pkg/yurtiotcarrier/controllers/predicate.go new file mode 100644 index 00000000000..d67b48e9c47 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/predicate.go @@ -0,0 +1,47 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +import ( + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +func genFirstUpdateFilter(objKind string) predicate.Predicate { + return predicate.Funcs{ + // ignore the update event that is generated due to a + // new deviceprofile being added to the Edgex Foundry + UpdateFunc: func(e event.UpdateEvent) bool { + oldDp, ok := e.ObjectOld.(edgexCli.EdgeXObject) + if !ok { + klog.Infof("fail to assert object to deviceprofile, object kind is %s", objKind) + return false + } + newDp, ok := e.ObjectNew.(edgexCli.EdgeXObject) + if !ok { + klog.Infof("fail to assert object to deviceprofile, object kind is %s", objKind) + return false + } + if !oldDp.IsAddedToEdgeX() && newDp.IsAddedToEdgeX() { + return false + } + return true + }, + } +} diff --git a/pkg/yurtiotcarrier/controllers/util/conditions.go b/pkg/yurtiotcarrier/controllers/util/conditions.go new file mode 100644 index 00000000000..db892cc1459 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/util/conditions.go @@ -0,0 +1,120 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 util + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" +) + +// NewDeviceCondition creates a new Device condition. +func NewDeviceCondition(condType iotv1alpha1.DeviceConditionType, status corev1.ConditionStatus, reason, message string) *iotv1alpha1.DeviceCondition { + return &iotv1alpha1.DeviceCondition{ + Type: condType, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + } +} + +// GetDeviceCondition returns the condition with the provided type. +func GetDeviceCondition(status iotv1alpha1.DeviceStatus, condType iotv1alpha1.DeviceConditionType) *iotv1alpha1.DeviceCondition { + for i := range status.Conditions { + c := status.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} + +// SetDeviceCondition updates the Device to include the provided condition. If the condition that +// we are about to add already exists and has the same status, reason and message then we are not going to update. +func SetDeviceCondition(status *iotv1alpha1.DeviceStatus, condition *iotv1alpha1.DeviceCondition) { + currentCond := GetDeviceCondition(*status, condition.Type) + if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { + return + } + + if currentCond != nil && currentCond.Status == condition.Status { + condition.LastTransitionTime = currentCond.LastTransitionTime + } + newConditions := filterOutDeviceCondition(status.Conditions, condition.Type) + status.Conditions = append(newConditions, *condition) +} + +func filterOutDeviceCondition(conditions []iotv1alpha1.DeviceCondition, condType iotv1alpha1.DeviceConditionType) []iotv1alpha1.DeviceCondition { + var newConditions []iotv1alpha1.DeviceCondition + for _, c := range conditions { + if c.Type == condType { + continue + } + newConditions = append(newConditions, c) + } + return newConditions +} + +// NewDeviceServiceCondition creates a new DeviceService condition. +func NewDeviceServiceCondition(condType iotv1alpha1.DeviceServiceConditionType, status corev1.ConditionStatus, reason, message string) *iotv1alpha1.DeviceServiceCondition { + return &iotv1alpha1.DeviceServiceCondition{ + Type: condType, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + } +} + +// GetDeviceServiceCondition returns the condition with the provided type. +func GetDeviceServiceCondition(status iotv1alpha1.DeviceServiceStatus, condType iotv1alpha1.DeviceServiceConditionType) *iotv1alpha1.DeviceServiceCondition { + for i := range status.Conditions { + c := status.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} + +// SetDeviceServiceCondition updates the DeviceService to include the provided condition. If the condition that +// we are about to add already exists and has the same status, reason and message then we are not going to update. +func SetDeviceServiceCondition(status *iotv1alpha1.DeviceServiceStatus, condition *iotv1alpha1.DeviceServiceCondition) { + currentCond := GetDeviceServiceCondition(*status, condition.Type) + if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { + return + } + + if currentCond != nil && currentCond.Status == condition.Status { + condition.LastTransitionTime = currentCond.LastTransitionTime + } + newConditions := filterOutDeviceServiceCondition(status.Conditions, condition.Type) + status.Conditions = append(newConditions, *condition) +} + +func filterOutDeviceServiceCondition(conditions []iotv1alpha1.DeviceServiceCondition, condType iotv1alpha1.DeviceServiceConditionType) []iotv1alpha1.DeviceServiceCondition { + var newConditions []iotv1alpha1.DeviceServiceCondition + for _, c := range conditions { + if c.Type == condType { + continue + } + newConditions = append(newConditions, c) + } + return newConditions +} diff --git a/pkg/yurtiotcarrier/controllers/util/fieldindexer.go b/pkg/yurtiotcarrier/controllers/util/fieldindexer.go new file mode 100644 index 00000000000..fafc6f254f6 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/util/fieldindexer.go @@ -0,0 +1,62 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 util + +import ( + "context" + "sync" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + IndexerPathForNodepool = "spec.nodePool" +) + +var registerOnce sync.Once + +func RegisterFieldIndexers(fi client.FieldIndexer) error { + var err error + registerOnce.Do(func() { + // register the fieldIndexer for device + if err = fi.IndexField(context.TODO(), &iotv1alpha1.Device{}, IndexerPathForNodepool, func(rawObj client.Object) []string { + device := rawObj.(*iotv1alpha1.Device) + return []string{device.Spec.NodePool} + }); err != nil { + return + } + + // register the fieldIndexer for deviceService + if err = fi.IndexField(context.TODO(), &iotv1alpha1.DeviceService{}, IndexerPathForNodepool, func(rawObj client.Object) []string { + deviceService := rawObj.(*iotv1alpha1.DeviceService) + return []string{deviceService.Spec.NodePool} + }); err != nil { + return + } + + // register the fieldIndexer for deviceProfile + if err = fi.IndexField(context.TODO(), &iotv1alpha1.DeviceProfile{}, IndexerPathForNodepool, func(rawObj client.Object) []string { + profile := rawObj.(*iotv1alpha1.DeviceProfile) + return []string{profile.Spec.NodePool} + }); err != nil { + return + } + }) + return err +} diff --git a/pkg/yurtiotcarrier/controllers/util/string.go b/pkg/yurtiotcarrier/controllers/util/string.go new file mode 100644 index 00000000000..4e7607a4d56 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/util/string.go @@ -0,0 +1,30 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 util + +// IsInStringLst checks if 'str' is in the 'strLst' +func IsInStringLst(strLst []string, str string) bool { + if len(strLst) == 0 { + return false + } + for _, s := range strLst { + if str == s { + return true + } + } + return false +} diff --git a/pkg/yurtiotcarrier/controllers/util/string_test.go b/pkg/yurtiotcarrier/controllers/util/string_test.go new file mode 100644 index 00000000000..7f56226ad8d --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/util/string_test.go @@ -0,0 +1,70 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 util + +import ( + "testing" +) + +func TestIsInStringLst(t *testing.T) { + tests := []struct { + desc string + sl []string + s string + res bool + }{ + { + "test empty list", + []string{}, + "a", + false, + }, + { + "test not in list", + []string{"a", "b", "c"}, + "d", + false, + }, + { + "test not in list with one element", + []string{"a"}, + "b", + false, + }, + { + "test in list with one element", + []string{"aaa"}, + "aaa", + true, + }, + { + "test in list with one element", + []string{"aaa", "a", "bbb"}, + "a", + true, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + res := IsInStringLst(tt.sl, tt.s) + if res != tt.res { + t.Errorf("expect %v, but %v returned", tt.res, res) + } + }) + } +} diff --git a/pkg/yurtiotcarrier/controllers/util/tools.go b/pkg/yurtiotcarrier/controllers/util/tools.go new file mode 100644 index 00000000000..255d3d7f2f6 --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/util/tools.go @@ -0,0 +1,99 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 util + +import ( + "context" + "fmt" + "io/ioutil" + "strings" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +const ( + PODHOSTNAME = "/etc/hostname" + PODNAMESPACE = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" +) + +// GetNodePool get nodepool where device-controller run +func GetNodePool(cfg *rest.Config) (string, error) { + var nodePool string + client, err := kubernetes.NewForConfig(cfg) + if err != nil { + return nodePool, err + } + + bn, err := ioutil.ReadFile(PODHOSTNAME) + if err != nil { + return nodePool, fmt.Errorf("Read file %s fail: %v", PODHOSTNAME, err) + } + bns, err := ioutil.ReadFile(PODNAMESPACE) + if err != nil { + return nodePool, fmt.Errorf("Read file %s fail: %v", PODNAMESPACE, err) + } + name := strings.Replace(string(bn), "\n", "", -1) + namespace := string(bns) + + pod, err := client.CoreV1().Pods(namespace).Get(context.Background(), name, metav1.GetOptions{}) + if err != nil { + return nodePool, fmt.Errorf("not found pod %s/%s: %v", namespace, name, err) + } + node, err := client.CoreV1().Nodes().Get(context.Background(), pod.Spec.NodeName, metav1.GetOptions{}) + if err != nil { + return nodePool, fmt.Errorf("not found node %s: %v", pod.Spec.NodeName, err) + } + nodePool, ok := node.Labels["apps.openyurt.io/nodepool"] + if !ok { + return nodePool, fmt.Errorf("node %s doesn't add to a nodepool", node.GetName()) + } + return nodePool, err +} + +func GetEdgeDeviceServiceName(ds *iotv1alpha1.DeviceService, label string) string { + var actualDSName string + if _, ok := ds.ObjectMeta.Labels[label]; ok { + actualDSName = ds.ObjectMeta.Labels[label] + } else { + actualDSName = ds.GetName() + } + return actualDSName +} + +func GetEdgeDeviceName(d *iotv1alpha1.Device, label string) string { + var actualDeviceName string + if _, ok := d.ObjectMeta.Labels[label]; ok { + actualDeviceName = d.ObjectMeta.Labels[label] + } else { + actualDeviceName = d.GetName() + } + return actualDeviceName +} + +func GetEdgeDeviceProfileName(dp *iotv1alpha1.DeviceProfile, label string) string { + var actualDPName string + if _, ok := dp.ObjectMeta.Labels[label]; ok { + actualDPName = dp.ObjectMeta.Labels[label] + } else { + actualDPName = dp.GetName() + } + return actualDPName +} diff --git a/pkg/yurtiotcarrier/controllers/util/tools_test.go b/pkg/yurtiotcarrier/controllers/util/tools_test.go new file mode 100644 index 00000000000..d2b30c11e6a --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/util/tools_test.go @@ -0,0 +1,54 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 util + +import ( + "testing" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/stretchr/testify/assert" + "k8s.io/client-go/rest" +) + +func TestGetNodePool(t *testing.T) { + cfg := &rest.Config{} + res, err := GetNodePool(cfg) + if res != "" { + t.Errorf("expect nil on null config") + } + if err == nil { + t.Errorf("null config must cause error") + } +} + +func TestGetEdgeDeviceServiceName(t *testing.T) { + d := &iotv1alpha1.DeviceService{} + assert.Equal(t, GetEdgeDeviceServiceName(d, ""), "") + assert.Equal(t, GetEdgeDeviceServiceName(d, "a"), "") +} + +func TestGetEdgeDeviceName(t *testing.T) { + d := &iotv1alpha1.Device{} + assert.Equal(t, GetEdgeDeviceName(d, ""), "") + assert.Equal(t, GetEdgeDeviceName(d, "a"), "") +} + +func TestGetEdgeDeviceProfileName(t *testing.T) { + d := &iotv1alpha1.DeviceProfile{} + assert.Equal(t, GetEdgeDeviceProfileName(d, ""), "") + assert.Equal(t, GetEdgeDeviceProfileName(d, "a"), "") +} diff --git a/pkg/yurtiotcarrier/controllers/well_known_labels.go b/pkg/yurtiotcarrier/controllers/well_known_labels.go new file mode 100644 index 00000000000..1b0b9e5445a --- /dev/null +++ b/pkg/yurtiotcarrier/controllers/well_known_labels.go @@ -0,0 +1,21 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 controllers + +const ( + EdgeXObjectName = "device-controller/edgex-object.name" +) From 4a4388544c18095c886fd32c914c2bb761f56368 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Mon, 10 Jul 2023 18:07:56 +0800 Subject: [PATCH 2/7] enable PlatformAdmin to deploy yurt-device-controller Signed-off-by: wangxye <1031989637@qq.com> --- cmd/yurt-iot-carrier/app/core.go | 17 ++- go.mod | 3 - hack/make-rules/build.sh | 1 + pkg/apis/iot/v1alpha1/condition_const.go | 26 +++++ pkg/apis/iot/v1alpha1/device_types.go | 30 ++--- pkg/apis/iot/v1alpha1/deviceprofile_types.go | 16 +-- pkg/apis/iot/v1alpha1/deviceservice_types.go | 30 ++--- .../platformadmin/platformadmin_controller.go | 12 +- pkg/controller/platformadmin/utils/version.go | 2 +- .../clients/edgex-foundry/device_client.go | 6 +- .../edgex-foundry/device_client_test.go | 11 +- .../edgex-foundry/deviceprofile_client.go | 7 +- .../deviceprofile_client_test.go | 6 +- .../edgex-foundry/deviceservice_client.go | 7 +- .../deviceservice_client_test.go | 6 +- .../clients/edgex-foundry/util.go | 6 +- .../controllers/device_controller.go | 15 +-- .../controllers/device_controller_test.go | 78 ------------- .../controllers/device_syncer.go | 14 +-- .../controllers/deviceprofile_controller.go | 14 +-- .../deviceprofile_controller_test.go | 103 ------------------ .../controllers/deviceprofile_syncer.go | 10 +- .../controllers/deviceservice_controller.go | 14 +-- .../deviceservice_controller_test.go | 75 ------------- .../controllers/deviceservice_syncer.go | 10 +- pkg/yurtiotcarrier/controllers/predicate.go | 3 +- .../controllers/util/fieldindexer.go | 4 +- pkg/yurtiotcarrier/controllers/util/tools.go | 4 +- .../controllers/util/tools_test.go | 3 +- 29 files changed, 144 insertions(+), 389 deletions(-) delete mode 100644 pkg/yurtiotcarrier/controllers/device_controller_test.go delete mode 100644 pkg/yurtiotcarrier/controllers/deviceprofile_controller_test.go delete mode 100644 pkg/yurtiotcarrier/controllers/deviceservice_controller_test.go diff --git a/cmd/yurt-iot-carrier/app/core.go b/cmd/yurt-iot-carrier/app/core.go index baecf802e30..c2df21a37cc 100644 --- a/cmd/yurt-iot-carrier/app/core.go +++ b/cmd/yurt-iot-carrier/app/core.go @@ -21,21 +21,21 @@ import ( "fmt" "os" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" - "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" - + clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/klog/v2" "k8s.io/klog/v2/klogr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/pkg/apis" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" ) var ( @@ -43,6 +43,15 @@ var ( setupLog = ctrl.Log.WithName("setup") ) +func init() { + _ = clientgoscheme.AddToScheme(scheme) + + _ = apis.AddToScheme(clientgoscheme.Scheme) + _ = apis.AddToScheme(scheme) + + // +kubebuilder:scaffold:scheme +} + func NewCmdYurtIotCarrier(stopCh <-chan struct{}) *cobra.Command { yurtDeviceControllerOptions := options.NewYurtIotCarrierOptions() cmd := &cobra.Command{ diff --git a/go.mod b/go.mod index b875d1c12f9..53dcd2e2ad0 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/gorilla/mux v1.8.0 github.com/hashicorp/go-version v1.6.0 github.com/jarcoal/httpmock v1.3.0 - github.com/onsi/ginkgo v1.16.4 github.com/onsi/ginkgo/v2 v2.1.4 github.com/onsi/gomega v1.19.0 github.com/opencontainers/selinux v1.11.0 @@ -123,7 +122,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/nxadm/tail v1.4.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/runc v1.1.5 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect @@ -163,7 +161,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.22.2 // indirect k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect diff --git a/hack/make-rules/build.sh b/hack/make-rules/build.sh index 1422dd5df7d..10fd803d2b3 100755 --- a/hack/make-rules/build.sh +++ b/hack/make-rules/build.sh @@ -25,6 +25,7 @@ readonly YURT_ALL_TARGETS=( yurt-node-servant yurthub yurt-manager + yurt-iot-carrier ) # clean old binaries at GOOS and GOARCH diff --git a/pkg/apis/iot/v1alpha1/condition_const.go b/pkg/apis/iot/v1alpha1/condition_const.go index 7f1c8b5e84e..498c9c3e87e 100644 --- a/pkg/apis/iot/v1alpha1/condition_const.go +++ b/pkg/apis/iot/v1alpha1/condition_const.go @@ -35,4 +35,30 @@ const ( DeploymentProvisioningReason = "DeploymentProvisioning" DeploymentProvisioningFailedReason = "DeploymentProvisioningFailed" + + // DeviceSyncedCondition indicates that the device exists in both OpenYurt and edge platform + DeviceSyncedCondition DeviceConditionType = "DeviceSynced" + + DeviceManagingReason = "This device is not managed by openyurt" + + DeviceCreateSyncedReason = "Failed to create device on edge platform" + + // DeviceManagingCondition indicates that the device is being managed by cloud and its properties are being reconciled + DeviceManagingCondition DeviceConditionType = "DeviceManaging" + + DeviceVistedCoreMetadataSyncedReason = "Failed to visit the EdgeX core-metadata-service" + + DeviceUpdateStateReason = "Failed to update AdminState or OperatingState of device on edge platform" + + // DeviceServiceSyncedCondition indicates that the deviceService exists in both OpenYurt and edge platform + DeviceServiceSyncedCondition DeviceServiceConditionType = "DeviceServiceSynced" + + DeviceServiceManagingReason = "This deviceService is not managed by openyurt" + + // DeviceServiceManagingCondition indicates that the deviceService is being managed by cloud and its field are being reconciled + DeviceServiceManagingCondition DeviceServiceConditionType = "DeviceServiceManaging" + + DeviceServiceCreateSyncedReason = "Failed to add DeviceService to EdgeX" + + DeviceServiceUpdateStatusSyncedReason = "Failed to update DeviceService status" ) diff --git a/pkg/apis/iot/v1alpha1/device_types.go b/pkg/apis/iot/v1alpha1/device_types.go index 88bc09a948a..ea7c4c58158 100644 --- a/pkg/apis/iot/v1alpha1/device_types.go +++ b/pkg/apis/iot/v1alpha1/device_types.go @@ -22,19 +22,7 @@ import ( ) const ( - DeviceFinalizer = "v1alpha1.device.finalizer" - // DeviceSyncedCondition indicates that the device exists in both OpenYurt and edge platform - DeviceSyncedCondition DeviceConditionType = "DeviceSynced" - // DeviceManagingCondition indicates that the device is being managed by cloud and its properties are being reconciled - DeviceManagingCondition DeviceConditionType = "DeviceManaging" - - DeviceManagingReason = "This device is not managed by openyurt" - - DeviceCreateSyncedReason = "Failed to create device on edge platform" - - DeviceVistedCoreMetadataSyncedReason = "Failed to visit the EdgeX core-metadata-service" - - DeviceUpdateStateReason = "Failed to update AdminState or OperatingState of device on edge platform" + DeviceFinalizer = "iot.device.finalizer" ) // DeviceConditionType indicates valid conditions type of a Device. @@ -142,13 +130,15 @@ type DeviceCondition struct { Message string `json:"message,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:shortName=dev -//+kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of device" -//+kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of device" -//+kubebuilder:printcolumn:name="MANAGED",type="boolean",priority=1,JSONPath=".spec.managed",description="The managed status of device" -//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:shortName=dev +// +kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of device" +// +kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of device" +// +kubebuilder:printcolumn:name="MANAGED",type="boolean",priority=1,JSONPath=".spec.managed",description="The managed status of device" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" // Device is the Schema for the devices API type Device struct { diff --git a/pkg/apis/iot/v1alpha1/deviceprofile_types.go b/pkg/apis/iot/v1alpha1/deviceprofile_types.go index 4dcb01958d6..a35be26a1ad 100644 --- a/pkg/apis/iot/v1alpha1/deviceprofile_types.go +++ b/pkg/apis/iot/v1alpha1/deviceprofile_types.go @@ -21,7 +21,7 @@ import ( ) const ( - DeviceProfileFinalizer = "v1alpha1.deviceProfile.finalizer" + DeviceProfileFinalizer = "iot.deviceProfile.finalizer" ) type DeviceResource struct { @@ -83,12 +83,14 @@ type DeviceProfileStatus struct { Synced bool `json:"synced,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:shortName=dp -//+kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of deviceProfile" -//+kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of deviceProfile" -//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:shortName=dp +// +kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of deviceProfile" +// +kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of deviceProfile" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" // DeviceProfile represents the attributes and operational capabilities of a device. // It is a template for which there can be multiple matching devices within a given system. diff --git a/pkg/apis/iot/v1alpha1/deviceservice_types.go b/pkg/apis/iot/v1alpha1/deviceservice_types.go index e2a1eb89518..3dce06f1a75 100644 --- a/pkg/apis/iot/v1alpha1/deviceservice_types.go +++ b/pkg/apis/iot/v1alpha1/deviceservice_types.go @@ -22,20 +22,10 @@ import ( ) const ( - DeviceServiceFinalizer = "v1alpha1.deviceService.finalizer" - // DeviceServiceSyncedCondition indicates that the deviceService exists in both OpenYurt and edge platform - DeviceServiceSyncedCondition DeviceServiceConditionType = "DeviceServiceSynced" - // DeviceServiceManagingCondition indicates that the deviceService is being managed by cloud and its field are being reconciled - DeviceServiceManagingCondition DeviceServiceConditionType = "DeviceServiceManaging" - - DeviceServiceManagingReason = "This deviceService is not managed by openyurt" - - DeviceServiceCreateSyncedReason = "Failed to add DeviceService to EdgeX" - - DeviceServiceUpdateStatusSyncedReason = "Failed to update DeviceService status" + DeviceServiceFinalizer = "iot.deviceService.finalizer" ) -// DeviceServiceConditionType indicates valid conditions type of a Device. +// DeviceServiceConditionType indicates valid conditions type of a Device Service. type DeviceServiceConditionType string // DeviceServiceSpec defines the desired state of DeviceService @@ -90,13 +80,15 @@ type DeviceServiceCondition struct { Message string `json:"message,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:resource:shortName=dsvc -//+kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of deviceService" -//+kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of deviceService" -//+kubebuilder:printcolumn:name="MANAGED",type="boolean",priority=1,JSONPath=".spec.managed",description="The managed status of deviceService" -//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:shortName=dsvc +// +kubebuilder:printcolumn:name="NODEPOOL",type="string",JSONPath=".spec.nodePool",description="The nodepool of deviceService" +// +kubebuilder:printcolumn:name="SYNCED",type="boolean",JSONPath=".status.synced",description="The synced status of deviceService" +// +kubebuilder:printcolumn:name="MANAGED",type="boolean",priority=1,JSONPath=".spec.managed",description="The managed status of deviceService" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" // DeviceService is the Schema for the deviceservices API type DeviceService struct { diff --git a/pkg/controller/platformadmin/platformadmin_controller.go b/pkg/controller/platformadmin/platformadmin_controller.go index a453b367d07..2f9d0a40409 100644 --- a/pkg/controller/platformadmin/platformadmin_controller.go +++ b/pkg/controller/platformadmin/platformadmin_controller.go @@ -258,6 +258,10 @@ func (r *ReconcilePlatformAdmin) reconcileDelete(ctx context.Context, platformAd desiredComponents = append(desiredComponents, additionalComponents...) yurtIotCarrier, err := NewYurtIoTCarrierComponent(platformAdmin) + if err != nil { + klog.Errorf(Format("yurtIoTCarrierComponent error %v", err)) + return reconcile.Result{}, err + } desiredComponents = append(desiredComponents, yurtIotCarrier) //TODO: handle PlatformAdmin.Spec.Components @@ -389,6 +393,9 @@ func (r *ReconcilePlatformAdmin) reconcileComponent(ctx context.Context, platfor desireComponents = append(desireComponents, additionalComponents...) yurtIotCarrier, err := NewYurtIoTCarrierComponent(platformAdmin) + if err != nil { + return false, err + } desireComponents = append(desireComponents, yurtIotCarrier) //TODO: handle PlatformAdmin.Spec.Components @@ -802,12 +809,9 @@ func NewYurtIoTCarrierComponent(platformAdmin *iotv1alpha2.PlatformAdmin) (*conf map[string]interface{}{ "args": []interface{}{ "--health-probe-bind-address=:8081", - "--metrics-bind-address=127.0.0.1:80", + "--metrics-bind-address=127.0.0.1:8080", "--leader-elect=false", }, - // "command": []interface{}{ - // "./yurt-iot-carrier", - // }, "image": fmt.Sprintf("leoabyss/yurt-iot-carrier:%s", ver), "imagePullPolicy": "IfNotPresent", "livenessProbe": map[string]interface{}{ diff --git a/pkg/controller/platformadmin/utils/version.go b/pkg/controller/platformadmin/utils/version.go index c89e60d480a..ac733ab290e 100644 --- a/pkg/controller/platformadmin/utils/version.go +++ b/pkg/controller/platformadmin/utils/version.go @@ -22,7 +22,7 @@ import ( func DefaultVersion(platformAdmin *iotv1alpha2.PlatformAdmin) (string, string, error) { - version := "v1.3.0-6a8902b" + version := "v0.2" ns := platformAdmin.Namespace return version, ns, nil diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go b/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go index c549f0d0eb5..699d5cbf7b1 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go @@ -26,15 +26,15 @@ import ( "strings" "time" - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos" "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" edgex_resp "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" "github.com/go-resty/resty/v2" "golang.org/x/net/publicsuffix" "k8s.io/klog/v2" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" ) type EdgexDeviceClient struct { diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go b/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go index 6b564f1ff55..81ecab164d1 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go @@ -5,7 +5,7 @@ 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 + 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, @@ -20,13 +20,12 @@ import ( "encoding/json" "testing" - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - edgex_resp "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" ) const ( @@ -192,7 +191,7 @@ func Test_UpdatePropertyState(t *testing.T) { device := toKubeDevice(resp.Device) device.Spec.DeviceProperties = map[string]iotv1alpha1.DesiredPropertyState{ - "Float32": iotv1alpha1.DesiredPropertyState{ + "Float32": { Name: "Float32", DesiredValue: "66.66", }, diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go index 911f58bc00d..fced1283b9c 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go @@ -22,14 +22,13 @@ import ( "fmt" "net/http" - "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - devcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" - "github.com/go-resty/resty/v2" "k8s.io/klog/v2" + + "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + devcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" ) type EdgexDeviceProfile struct { diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go index 2ca074545ca..43fdf7648db 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go @@ -5,7 +5,7 @@ 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 + 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, @@ -20,11 +20,11 @@ import ( "encoding/json" "testing" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - edgex_resp "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" + + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" ) const ( diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go index 367fd68d633..bb4f4d68d56 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go @@ -22,14 +22,13 @@ import ( "fmt" "net/http" - "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" - "github.com/go-resty/resty/v2" "k8s.io/klog/v2" + + "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" ) type EdgexDeviceServiceClient struct { diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go index eafd9edfae6..d50d3159885 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go @@ -5,7 +5,7 @@ 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 + 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, @@ -21,10 +21,10 @@ import ( "testing" edgex_resp "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses" - "github.com/jarcoal/httpmock" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" "github.com/stretchr/testify/assert" + + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" ) const ( diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/util.go b/pkg/yurtiotcarrier/clients/edgex-foundry/util.go index 2214e5dab3d..63eed91ecbd 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/util.go +++ b/pkg/yurtiotcarrier/clients/edgex-foundry/util.go @@ -20,13 +20,13 @@ import ( "fmt" "strings" - "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common" "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/requests" "github.com/edgexfoundry/go-mod-core-contracts/v2/models" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" ) const ( diff --git a/pkg/yurtiotcarrier/controllers/device_controller.go b/pkg/yurtiotcarrier/controllers/device_controller.go index fb3d2216a93..f41b18e832e 100644 --- a/pkg/yurtiotcarrier/controllers/device_controller.go +++ b/pkg/yurtiotcarrier/controllers/device_controller.go @@ -22,22 +22,19 @@ import ( "fmt" "time" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" - - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - - edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - util "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + util "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" ) // DeviceReconciler reconciles a Device object diff --git a/pkg/yurtiotcarrier/controllers/device_controller_test.go b/pkg/yurtiotcarrier/controllers/device_controller_test.go deleted file mode 100644 index e0fcc00a037..00000000000 --- a/pkg/yurtiotcarrier/controllers/device_controller_test.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2023 The OpenYurt Authors. - -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 controllers - -import ( - "context" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -// +kubebuilder:docs-gen:collapse=Imports - -var _ = Describe("Device controller", func() { - - // Define utility constants for object names and testing timeouts/durations and intervals. - const () - - Context("When updating Device Status", func() { - It("Should trigger Device instance", func() { - By("By creating a new Device resource") - ctx := context.Background() - - dev := &iotv1alpha1.Device{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "device.openyurt.io/v1alpha1", - Kind: "Device", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: DeviceName, - Namespace: CommonNamespace, - }, - Spec: iotv1alpha1.DeviceSpec{ - Description: "unit test device", - AdminState: "UNLOCKED", - NodePool: PoolName, - Managed: true, - Notify: true, - Service: ServiceName, - }, - } - Expect(k8sClient.Create(ctx, dev)).Should(Succeed()) - - lookupKey := types.NamespacedName{Name: DeviceName, Namespace: CommonNamespace} - created := &iotv1alpha1.Device{} - - Eventually(func() bool { - err := k8sClient.Get(ctx, lookupKey, created) - return err == nil - }, timeout, interval).Should(BeTrue()) - time.Sleep(5 * time.Second) - Expect(created.Spec.NodePool).Should(Equal(PoolName)) - - Expect(k8sClient.Delete(ctx, created)).Should(Succeed()) - }) - }) - -}) diff --git a/pkg/yurtiotcarrier/controllers/device_syncer.go b/pkg/yurtiotcarrier/controllers/device_syncer.go index a07860b2ed4..82ee3bbfadd 100644 --- a/pkg/yurtiotcarrier/controllers/device_syncer.go +++ b/pkg/yurtiotcarrier/controllers/device_syncer.go @@ -21,18 +21,16 @@ import ( "strings" "time" - edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - efCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" - - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + efCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" ) type DeviceSyncer struct { diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go b/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go index 8585ec8ac9c..6ce5cf7702c 100644 --- a/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go +++ b/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go @@ -21,20 +21,18 @@ import ( "encoding/json" "fmt" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - - edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" - apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" ) // DeviceProfileReconciler reconciles a DeviceProfile object diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_controller_test.go b/pkg/yurtiotcarrier/controllers/deviceprofile_controller_test.go deleted file mode 100644 index efddd1808ac..00000000000 --- a/pkg/yurtiotcarrier/controllers/deviceprofile_controller_test.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2023 The OpenYurt Authors. - -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 controllers - -import ( - "context" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -// +kubebuilder:docs-gen:collapse=Imports - -var _ = Describe("Device controller", func() { - - // Define utility constants for object names and testing timeouts/durations and intervals. - const () - - Context("When updating DeviceProfile Status", func() { - It("Should trigger DeviceProfile instance", func() { - By("By creating a new DeviceProfile resource") - ctx := context.Background() - - rop := iotv1alpha1.ResourceOperation{ - DeviceResource: "EnableRandomization_Bool", - DefaultValue: "false", - } - - cmd := iotv1alpha1.DeviceCommand{ - Name: "WriteBoolValue", - IsHidden: false, - ReadWrite: "W", - ResourceOperations: []iotv1alpha1.ResourceOperation{rop}, - } - - resp := iotv1alpha1.ResourceProperties{ - ReadWrite: "W", - DefaultValue: "true", - ValueType: "Bool", - } - - res := iotv1alpha1.DeviceResource{ - Description: "used to decide whether to re-generate a random value", - Name: "EnableRandomization_Bool", - IsHidden: true, - Properties: resp, - } - - dp := &iotv1alpha1.DeviceProfile{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "device.openyurt.io/v1alpha1", - Kind: "DeviceProfile", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: ProfileName, - Namespace: CommonNamespace, - }, - Spec: iotv1alpha1.DeviceProfileSpec{ - Description: "test device profile", - NodePool: PoolName, - Manufacturer: "OpenYurt", - Model: Model, - DeviceCommands: []iotv1alpha1.DeviceCommand{cmd}, - DeviceResources: []iotv1alpha1.DeviceResource{res}, - }, - } - Expect(k8sClient.Create(ctx, dp)).Should(Succeed()) - - lookupKey := types.NamespacedName{Name: ProfileName, Namespace: CommonNamespace} - created := &iotv1alpha1.DeviceProfile{} - - Eventually(func() bool { - err := k8sClient.Get(ctx, lookupKey, created) - return err == nil - }, timeout, interval).Should(BeTrue()) - time.Sleep(5 * time.Second) - Expect(created.Spec.Model).Should(Equal(Model)) - - Expect(k8sClient.Delete(ctx, created)).Should(Succeed()) - }) - }) - -}) diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go b/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go index d5e8805d25e..e5383f397da 100644 --- a/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go +++ b/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go @@ -21,16 +21,16 @@ import ( "strings" "time" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" devcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" - ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" ) type DeviceProfileSyncer struct { diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_controller.go b/pkg/yurtiotcarrier/controllers/deviceservice_controller.go index 58052e7007b..f57d2020761 100644 --- a/pkg/yurtiotcarrier/controllers/deviceservice_controller.go +++ b/pkg/yurtiotcarrier/controllers/deviceservice_controller.go @@ -21,21 +21,19 @@ import ( "encoding/json" "fmt" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - util "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + util "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" ) // DeviceServiceReconciler reconciles a DeviceService object diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_controller_test.go b/pkg/yurtiotcarrier/controllers/deviceservice_controller_test.go deleted file mode 100644 index 29c9af1f4e4..00000000000 --- a/pkg/yurtiotcarrier/controllers/deviceservice_controller_test.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2023 The OpenYurt Authors. - -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 controllers - -import ( - "context" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -// +kubebuilder:docs-gen:collapse=Imports - -var _ = Describe("Device controller", func() { - - // Define utility constants for object names and testing timeouts/durations and intervals. - const () - - Context("When updating DeviceService Status", func() { - It("Should trigger DeviceService instance", func() { - By("By creating a new DeviceService resource") - ctx := context.Background() - - ds := &iotv1alpha1.DeviceService{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "device.openyurt.io/v1alpha1", - Kind: "DeviceService", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: ServiceName, - Namespace: CommonNamespace, - }, - Spec: iotv1alpha1.DeviceServiceSpec{ - Description: "test device service", - NodePool: PoolName, - AdminState: "UNLOCKED", - BaseAddress: "http://test-device:59900", - }, - } - Expect(k8sClient.Create(ctx, ds)).Should(Succeed()) - - lookupKey := types.NamespacedName{Name: ServiceName, Namespace: CommonNamespace} - created := &iotv1alpha1.DeviceService{} - - Eventually(func() bool { - err := k8sClient.Get(ctx, lookupKey, created) - return err == nil - }, timeout, interval).Should(BeTrue()) - time.Sleep(5 * time.Second) - Expect(created.Spec.NodePool).Should(Equal(PoolName)) - - Expect(k8sClient.Delete(ctx, created)).Should(Succeed()) - }) - }) - -}) diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go b/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go index d58ace0a04a..a5699de97d6 100644 --- a/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go +++ b/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go @@ -20,16 +20,16 @@ import ( "strings" "time" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" + "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" iotcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" - ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" ) type DeviceServiceSyncer struct { diff --git a/pkg/yurtiotcarrier/controllers/predicate.go b/pkg/yurtiotcarrier/controllers/predicate.go index d67b48e9c47..5078421e182 100644 --- a/pkg/yurtiotcarrier/controllers/predicate.go +++ b/pkg/yurtiotcarrier/controllers/predicate.go @@ -17,10 +17,11 @@ limitations under the License. package controllers import ( - edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" + + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" ) func genFirstUpdateFilter(objKind string) predicate.Predicate { diff --git a/pkg/yurtiotcarrier/controllers/util/fieldindexer.go b/pkg/yurtiotcarrier/controllers/util/fieldindexer.go index fafc6f254f6..8245d343274 100644 --- a/pkg/yurtiotcarrier/controllers/util/fieldindexer.go +++ b/pkg/yurtiotcarrier/controllers/util/fieldindexer.go @@ -20,9 +20,9 @@ import ( "context" "sync" - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "sigs.k8s.io/controller-runtime/pkg/client" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" ) const ( diff --git a/pkg/yurtiotcarrier/controllers/util/tools.go b/pkg/yurtiotcarrier/controllers/util/tools.go index 255d3d7f2f6..7f1a8d6fd30 100644 --- a/pkg/yurtiotcarrier/controllers/util/tools.go +++ b/pkg/yurtiotcarrier/controllers/util/tools.go @@ -22,11 +22,11 @@ import ( "io/ioutil" "strings" - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" ) const ( diff --git a/pkg/yurtiotcarrier/controllers/util/tools_test.go b/pkg/yurtiotcarrier/controllers/util/tools_test.go index d2b30c11e6a..fde94c4fffa 100644 --- a/pkg/yurtiotcarrier/controllers/util/tools_test.go +++ b/pkg/yurtiotcarrier/controllers/util/tools_test.go @@ -19,9 +19,10 @@ package util import ( "testing" - iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" "github.com/stretchr/testify/assert" "k8s.io/client-go/rest" + + iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" ) func TestGetNodePool(t *testing.T) { From 44220199f0788165a8c5037aab934966936ccbbf Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Mon, 10 Jul 2023 21:57:40 +0800 Subject: [PATCH 3/7] add ci for yurt-device-controller Signed-off-by: wangxye <1031989637@qq.com> --- .github/workflows/trivy-scan.yml | 2 +- Makefile | 2 ++ .../release/Dockerfile.yurt-iot-carrier | 14 ++++++++++++++ hack/make-rules/local-up-openyurt.sh | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 hack/dockerfiles/release/Dockerfile.yurt-iot-carrier diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index eb2b54867c6..26cffbe58b8 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - target: [ yurthub, node-servant, yurt-manager ] + target: [ yurthub, node-servant, yurt-manager, yurt-iot-carrier ] steps: - uses: actions/checkout@v3 with: diff --git a/Makefile b/Makefile index 49fe17030f4..0e8c6cfb6f4 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,8 @@ docker-push-yurt-tunnel-server: docker-buildx-builder docker-push-yurt-tunnel-agent: docker-buildx-builder docker buildx build --no-cache --push ${DOCKER_BUILD_ARGS} --platform ${TARGET_PLATFORMS} -f hack/dockerfiles/release/Dockerfile.yurt-tunnel-agent . -t ${IMAGE_REPO}/yurt-tunnel-agent:${GIT_VERSION} +docker-push-yurt-iot-carrier: docker-buildx-builder + docker buildx build --no-cache --push ${DOCKER_BUILD_ARGS} --platform ${TARGET_PLATFORMS} -f hack/dockerfiles/release/Dockerfile.yurt-iot-carrier . -t ${IMAGE_REPO}/yurt-iot-carrier:${GIT_VERSION} generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. # hack/make-rule/generate_openapi.sh // TODO by kadisi diff --git a/hack/dockerfiles/release/Dockerfile.yurt-iot-carrier b/hack/dockerfiles/release/Dockerfile.yurt-iot-carrier new file mode 100644 index 00000000000..56ad8303a80 --- /dev/null +++ b/hack/dockerfiles/release/Dockerfile.yurt-iot-carrier @@ -0,0 +1,14 @@ +# multi-arch image building for yurt-iot-carrier + +FROM --platform=${BUILDPLATFORM} golang:1.18 as builder +ADD . /build +ARG TARGETOS TARGETARCH GIT_VERSION GOPROXY MIRROR_REPO +WORKDIR /build/ +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} GIT_VERSION=${GIT_VERSION} make build WHAT=cmd/yurt-iot-carrier + +FROM --platform=${TARGETPLATFORM} alpine:3.17 +ARG TARGETOS TARGETARCH MIRROR_REPO +RUN if [ ! -z "${MIRROR_REPO+x}" ]; then sed -i "s/dl-cdn.alpinelinux.org/${MIRROR_REPO}/g" /etc/apk/repositories; fi && \ + apk add ca-certificates bash libc6-compat iptables ip6tables && update-ca-certificates && rm /var/cache/apk/* +COPY --from=builder /build/_output/local/bin/${TARGETOS}/${TARGETARCH}/yurt-iot-carrier /usr/local/bin/yurt-iot-carrier +ENTRYPOINT ["/usr/local/bin/yurt-iot-carrier"] \ No newline at end of file diff --git a/hack/make-rules/local-up-openyurt.sh b/hack/make-rules/local-up-openyurt.sh index 5181eec46f8..73a4d29e77e 100755 --- a/hack/make-rules/local-up-openyurt.sh +++ b/hack/make-rules/local-up-openyurt.sh @@ -56,6 +56,7 @@ readonly REQUIRED_IMAGES=( openyurt/node-servant openyurt/yurt-manager openyurt/yurthub + openyurt/yurt-iot-carrier ) readonly LOCAL_ARCH=$(go env GOHOSTARCH) From fd0d04abd63c836f146aed7b4aee101c401dc474 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Tue, 11 Jul 2023 17:32:36 +0800 Subject: [PATCH 4/7] enables yurt-device-controller to be deployed in any namespace Signed-off-by: wangxye <1031989637@qq.com> --- ...nition_platformadmins.iot.openyurt.io.yaml | 8493 ----------------- ...ml => iot.openyurt.io_deviceprofiles.yaml} | 0 ...t.io.yaml => iot.openyurt.io_devices.yaml} | 0 ...ml => iot.openyurt.io_deviceservices.yaml} | 0 .../yurt-manager-auto-generated.yaml | 36 +- .../templates/yurt-manager-iot.yaml | 88 + hack/make-rules/kustomize_to_chart.sh | 4 + .../platformadmin/platformadmin_controller.go | 137 +- pkg/controller/platformadmin/utils/version.go | 2 + .../controllers/device_controller.go | 6 +- .../controllers/deviceprofile_controller.go | 6 +- .../controllers/deviceservice_controller.go | 6 +- 12 files changed, 189 insertions(+), 8589 deletions(-) delete mode 100644 charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml rename charts/yurt-manager/crds/{apiextensions.k8s.io_v1_customresourcedefinition_deviceprofiles.iot.openyurt.io.yaml => iot.openyurt.io_deviceprofiles.yaml} (100%) rename charts/yurt-manager/crds/{apiextensions.k8s.io_v1_customresourcedefinition_devices.iot.openyurt.io.yaml => iot.openyurt.io_devices.yaml} (100%) rename charts/yurt-manager/crds/{apiextensions.k8s.io_v1_customresourcedefinition_deviceservices.iot.openyurt.io.yaml => iot.openyurt.io_deviceservices.yaml} (100%) create mode 100644 charts/yurt-manager/templates/yurt-manager-iot.yaml diff --git a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml b/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml deleted file mode 100644 index 9b71d04091b..00000000000 --- a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml +++ /dev/null @@ -1,8493 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: platformadmins.iot.openyurt.io -spec: - group: iot.openyurt.io - names: - categories: - - all - kind: PlatformAdmin - listKind: PlatformAdminList - plural: platformadmins - shortNames: - - pa - singular: platformadmin - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The platform ready status - jsonPath: .status.ready - name: READY - type: boolean - - description: The Service Replica. - jsonPath: .status.serviceReplicas - name: Service - type: integer - - description: The Ready Service Replica. - jsonPath: .status.serviceReadyReplicas - name: ReadyService - type: integer - - description: The Deployment Replica. - jsonPath: .status.deploymentReplicas - name: Deployment - type: integer - - description: The Ready Deployment Replica. - jsonPath: .status.deploymentReadyReplicas - name: ReadyDeployment - type: integer - deprecated: true - deprecationWarning: iot.openyurt.io/v1alpha1 PlatformAdmin will be deprecated - in future; use iot.openyurt.io/v1alpha2 PlatformAdmin; v1alpha1 PlatformAdmin.Spec.ServiceType - only support ClusterIP - name: v1alpha1 - schema: - openAPIV3Schema: - description: PlatformAdmin is the Schema for the samples API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: PlatformAdminSpec defines the desired state of PlatformAdmin - properties: - additionalDeployments: - items: - description: DeploymentTemplateSpec defines the pool template of - Deployment. - properties: - metadata: - type: object - spec: - description: DeploymentSpec is the specification of the desired - behavior of the Deployment. - properties: - minReadySeconds: - description: Minimum number of seconds for which a newly - created pod should be ready without any of its container - crashing, for it to be considered available. Defaults - to 0 (pod will be considered available as soon as it is - ready) - format: int32 - type: integer - paused: - description: Indicates that the deployment is paused. - type: boolean - progressDeadlineSeconds: - description: The maximum time in seconds for a deployment - to make progress before it is considered to be failed. - The deployment controller will continue to process failed - deployments and a condition with a ProgressDeadlineExceeded - reason will be surfaced in the deployment status. Note - that progress will not be estimated during the time a - deployment is paused. Defaults to 600s. - format: int32 - type: integer - replicas: - description: Number of desired pods. This is a pointer to - distinguish between explicit zero and not specified. Defaults - to 1. - format: int32 - type: integer - revisionHistoryLimit: - description: The number of old ReplicaSets to retain to - allow rollback. This is a pointer to distinguish between - explicit zero and not specified. Defaults to 10. - format: int32 - type: integer - selector: - description: Label selector for pods. Existing ReplicaSets - whose pods are selected by this will be the ones affected - by this deployment. It must match the pod template's labels. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists - or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. - type: object - type: object - strategy: - description: The deployment strategy to use to replace existing - pods with new ones. - properties: - rollingUpdate: - description: 'Rolling update config params. Present - only if DeploymentStrategyType = RollingUpdate. --- - TODO: Update this to follow our convention for oneOf, - whatever we decide it to be.' - properties: - maxSurge: - anyOf: - - type: integer - - type: string - description: 'The maximum number of pods that can - be scheduled above the desired number of pods. - Value can be an absolute number (ex: 5) or a percentage - of desired pods (ex: 10%). This can not be 0 if - MaxUnavailable is 0. Absolute number is calculated - from percentage by rounding up. Defaults to 25%. - Example: when this is set to 30%, the new ReplicaSet - can be scaled up immediately when the rolling - update starts, such that the total number of old - and new pods do not exceed 130% of desired pods. - Once old pods have been killed, new ReplicaSet - can be scaled up further, ensuring that total - number of pods running at any time during the - update is at most 130% of desired pods.' - x-kubernetes-int-or-string: true - maxUnavailable: - anyOf: - - type: integer - - type: string - description: 'The maximum number of pods that can - be unavailable during the update. Value can be - an absolute number (ex: 5) or a percentage of - desired pods (ex: 10%). Absolute number is calculated - from percentage by rounding down. This can not - be 0 if MaxSurge is 0. Defaults to 25%. Example: - when this is set to 30%, the old ReplicaSet can - be scaled down to 70% of desired pods immediately - when the rolling update starts. Once new pods - are ready, old ReplicaSet can be scaled down further, - followed by scaling up the new ReplicaSet, ensuring - that the total number of pods available at all - times during the update is at least 70% of desired - pods.' - x-kubernetes-int-or-string: true - type: object - type: - description: Type of deployment. Can be "Recreate" or - "RollingUpdate". Default is RollingUpdate. - type: string - type: object - template: - description: Template describes the pods that will be created. - properties: - metadata: - description: 'Standard object''s metadata. More info: - https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' - type: object - spec: - description: 'Specification of the desired behavior - of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' - properties: - activeDeadlineSeconds: - description: Optional duration in seconds the pod - may be active on the node relative to StartTime - before the system will actively try to mark it - failed and kill associated containers. Value must - be a positive integer. - format: int64 - type: integer - affinity: - description: If specified, the pod's scheduling - constraints - properties: - nodeAffinity: - description: Describes node affinity scheduling - rules for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to - schedule pods to nodes that satisfy the - affinity expressions specified by this - field, but it may choose a node that violates - one or more of the expressions. The node - that is most preferred is the one with - the greatest sum of weights, i.e. for - each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a - sum by iterating through the elements - of this field and adding "weight" to the - sum if the node matches the corresponding - matchExpressions; the node(s) with the - highest sum are the most preferred. - items: - description: An empty preferred scheduling - term matches all objects with implicit - weight 0 (i.e. it's a no-op). A null - preferred scheduling term matches no - objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, - associated with the corresponding - weight. - properties: - matchExpressions: - description: A list of node selector - requirements by node's labels. - items: - description: A node selector - requirement is a selector - that contains values, a key, - and an operator that relates - the key and values. - properties: - key: - description: The label key - that the selector applies - to. - type: string - operator: - description: Represents - a key's relationship to - a set of values. Valid - operators are In, NotIn, - Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of - string values. If the - operator is In or NotIn, - the values array must - be non-empty. If the operator - is Exists or DoesNotExist, - the values array must - be empty. If the operator - is Gt or Lt, the values - array must have a single - element, which will be - interpreted as an integer. - This array is replaced - during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector - requirements by node's fields. - items: - description: A node selector - requirement is a selector - that contains values, a key, - and an operator that relates - the key and values. - properties: - key: - description: The label key - that the selector applies - to. - type: string - operator: - description: Represents - a key's relationship to - a set of values. Valid - operators are In, NotIn, - Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of - string values. If the - operator is In or NotIn, - the values array must - be non-empty. If the operator - is Exists or DoesNotExist, - the values array must - be empty. If the operator - is Gt or Lt, the values - array must have a single - element, which will be - interpreted as an integer. - This array is replaced - during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - description: Weight associated with - matching the corresponding nodeSelectorTerm, - in the range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements - specified by this field are not met at - scheduling time, the pod will not be scheduled - onto the node. If the affinity requirements - specified by this field cease to be met - at some point during pod execution (e.g. - due to an update), the system may or may - not try to eventually evict the pod from - its node. - properties: - nodeSelectorTerms: - description: Required. A list of node - selector terms. The terms are ORed. - items: - description: A null or empty node - selector term matches no objects. - The requirements of them are ANDed. - The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector - requirements by node's labels. - items: - description: A node selector - requirement is a selector - that contains values, a key, - and an operator that relates - the key and values. - properties: - key: - description: The label key - that the selector applies - to. - type: string - operator: - description: Represents - a key's relationship to - a set of values. Valid - operators are In, NotIn, - Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of - string values. If the - operator is In or NotIn, - the values array must - be non-empty. If the operator - is Exists or DoesNotExist, - the values array must - be empty. If the operator - is Gt or Lt, the values - array must have a single - element, which will be - interpreted as an integer. - This array is replaced - during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector - requirements by node's fields. - items: - description: A node selector - requirement is a selector - that contains values, a key, - and an operator that relates - the key and values. - properties: - key: - description: The label key - that the selector applies - to. - type: string - operator: - description: Represents - a key's relationship to - a set of values. Valid - operators are In, NotIn, - Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of - string values. If the - operator is In or NotIn, - the values array must - be non-empty. If the operator - is Exists or DoesNotExist, - the values array must - be empty. If the operator - is Gt or Lt, the values - array must have a single - element, which will be - interpreted as an integer. - This array is replaced - during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - description: Describes pod affinity scheduling - rules (e.g. co-locate this pod in the same - node, zone, etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to - schedule pods to nodes that satisfy the - affinity expressions specified by this - field, but it may choose a node that violates - one or more of the expressions. The node - that is most preferred is the one with - the greatest sum of weights, i.e. for - each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a - sum by iterating through the elements - of this field and adding "weight" to the - sum if the node has pods which matches - the corresponding podAffinityTerm; the - node(s) with the highest sum are the most - preferred. - items: - description: The weights of all of the - matched WeightedPodAffinityTerm fields - are added per-node to find the most - preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity - term, associated with the corresponding - weight. - properties: - labelSelector: - description: A label query over - a set of resources, in this - case pods. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, - a key, and an operator - that relates the key and - values. - properties: - key: - description: key is - the label key that - the selector applies - to. - type: string - operator: - description: operator - represents a key's - relationship to a - set of values. Valid - operators are In, - NotIn, Exists and - DoesNotExist. - type: string - values: - description: values - is an array of string - values. If the operator - is In or NotIn, the - values array must - be non-empty. If the - operator is Exists - or DoesNotExist, the - values array must - be empty. This array - is replaced during - a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is - a map of {key,value} pairs. - A single {key,value} in - the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", - the operator is "In", and - the values array contains - only "value". The requirements - are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over - the set of namespaces that the - term applies to. The term is - applied to the union of the - namespaces selected by this - field and the ones listed in - the namespaces field. null selector - and null or empty namespaces - list means "this pod's namespace". - An empty selector ({}) matches - all namespaces. This field is - beta-level and is only honored - when PodAffinityNamespaceSelector - feature is enabled. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, - a key, and an operator - that relates the key and - values. - properties: - key: - description: key is - the label key that - the selector applies - to. - type: string - operator: - description: operator - represents a key's - relationship to a - set of values. Valid - operators are In, - NotIn, Exists and - DoesNotExist. - type: string - values: - description: values - is an array of string - values. If the operator - is In or NotIn, the - values array must - be non-empty. If the - operator is Exists - or DoesNotExist, the - values array must - be empty. This array - is replaced during - a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is - a map of {key,value} pairs. - A single {key,value} in - the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", - the operator is "In", and - the values array contains - only "value". The requirements - are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies - a static list of namespace names - that the term applies to. The - term is applied to the union - of the namespaces listed in - this field and the ones selected - by namespaceSelector. null or - empty namespaces list and null - namespaceSelector means "this - pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be - co-located (affinity) or not - co-located (anti-affinity) with - the pods matching the labelSelector - in the specified namespaces, - where co-located is defined - as running on a node whose value - of the label with key topologyKey - matches that of any node on - which any of the selected pods - is running. Empty topologyKey - is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with - matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements - specified by this field are not met at - scheduling time, the pod will not be scheduled - onto the node. If the affinity requirements - specified by this field cease to be met - at some point during pod execution (e.g. - due to a pod label update), the system - may or may not try to eventually evict - the pod from its node. When there are - multiple elements, the lists of nodes - corresponding to each podAffinityTerm - are intersected, i.e. all terms must be - satisfied. - items: - description: Defines a set of pods (namely - those matching the labelSelector relative - to the given namespace(s)) that this - pod should be co-located (affinity) - or not co-located (anti-affinity) with, - where co-located is defined as running - on a node whose value of the label with - key matches that of any - node on which a pod of the set of pods - is running - properties: - labelSelector: - description: A label query over a - set of resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, a key, - and an operator that relates - the key and values. - properties: - key: - description: key is the - label key that the selector - applies to. - type: string - operator: - description: operator represents - a key's relationship to - a set of values. Valid - operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an - array of string values. - If the operator is In - or NotIn, the values array - must be non-empty. If - the operator is Exists - or DoesNotExist, the values - array must be empty. This - array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a - map of {key,value} pairs. A - single {key,value} in the matchLabels - map is equivalent to an element - of matchExpressions, whose key - field is "key", the operator - is "In", and the values array - contains only "value". The requirements - are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the - set of namespaces that the term - applies to. The term is applied - to the union of the namespaces selected - by this field and the ones listed - in the namespaces field. null selector - and null or empty namespaces list - means "this pod's namespace". An - empty selector ({}) matches all - namespaces. This field is beta-level - and is only honored when PodAffinityNamespaceSelector - feature is enabled. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, a key, - and an operator that relates - the key and values. - properties: - key: - description: key is the - label key that the selector - applies to. - type: string - operator: - description: operator represents - a key's relationship to - a set of values. Valid - operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an - array of string values. - If the operator is In - or NotIn, the values array - must be non-empty. If - the operator is Exists - or DoesNotExist, the values - array must be empty. This - array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a - map of {key,value} pairs. A - single {key,value} in the matchLabels - map is equivalent to an element - of matchExpressions, whose key - field is "key", the operator - is "In", and the values array - contains only "value". The requirements - are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies - a static list of namespace names - that the term applies to. The term - is applied to the union of the namespaces - listed in this field and the ones - selected by namespaceSelector. null - or empty namespaces list and null - namespaceSelector means "this pod's - namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where - co-located is defined as running - on a node whose value of the label - with key topologyKey matches that - of any node on which any of the - selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling - rules (e.g. avoid putting this pod in the - same node, zone, etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to - schedule pods to nodes that satisfy the - anti-affinity expressions specified by - this field, but it may choose a node that - violates one or more of the expressions. - The node that is most preferred is the - one with the greatest sum of weights, - i.e. for each node that meets all of the - scheduling requirements (resource request, - requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and - adding "weight" to the sum if the node - has pods which matches the corresponding - podAffinityTerm; the node(s) with the - highest sum are the most preferred. - items: - description: The weights of all of the - matched WeightedPodAffinityTerm fields - are added per-node to find the most - preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity - term, associated with the corresponding - weight. - properties: - labelSelector: - description: A label query over - a set of resources, in this - case pods. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, - a key, and an operator - that relates the key and - values. - properties: - key: - description: key is - the label key that - the selector applies - to. - type: string - operator: - description: operator - represents a key's - relationship to a - set of values. Valid - operators are In, - NotIn, Exists and - DoesNotExist. - type: string - values: - description: values - is an array of string - values. If the operator - is In or NotIn, the - values array must - be non-empty. If the - operator is Exists - or DoesNotExist, the - values array must - be empty. This array - is replaced during - a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is - a map of {key,value} pairs. - A single {key,value} in - the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", - the operator is "In", and - the values array contains - only "value". The requirements - are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over - the set of namespaces that the - term applies to. The term is - applied to the union of the - namespaces selected by this - field and the ones listed in - the namespaces field. null selector - and null or empty namespaces - list means "this pod's namespace". - An empty selector ({}) matches - all namespaces. This field is - beta-level and is only honored - when PodAffinityNamespaceSelector - feature is enabled. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, - a key, and an operator - that relates the key and - values. - properties: - key: - description: key is - the label key that - the selector applies - to. - type: string - operator: - description: operator - represents a key's - relationship to a - set of values. Valid - operators are In, - NotIn, Exists and - DoesNotExist. - type: string - values: - description: values - is an array of string - values. If the operator - is In or NotIn, the - values array must - be non-empty. If the - operator is Exists - or DoesNotExist, the - values array must - be empty. This array - is replaced during - a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is - a map of {key,value} pairs. - A single {key,value} in - the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", - the operator is "In", and - the values array contains - only "value". The requirements - are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies - a static list of namespace names - that the term applies to. The - term is applied to the union - of the namespaces listed in - this field and the ones selected - by namespaceSelector. null or - empty namespaces list and null - namespaceSelector means "this - pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be - co-located (affinity) or not - co-located (anti-affinity) with - the pods matching the labelSelector - in the specified namespaces, - where co-located is defined - as running on a node whose value - of the label with key topologyKey - matches that of any node on - which any of the selected pods - is running. Empty topologyKey - is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with - matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements - specified by this field are not met at - scheduling time, the pod will not be scheduled - onto the node. If the anti-affinity requirements - specified by this field cease to be met - at some point during pod execution (e.g. - due to a pod label update), the system - may or may not try to eventually evict - the pod from its node. When there are - multiple elements, the lists of nodes - corresponding to each podAffinityTerm - are intersected, i.e. all terms must be - satisfied. - items: - description: Defines a set of pods (namely - those matching the labelSelector relative - to the given namespace(s)) that this - pod should be co-located (affinity) - or not co-located (anti-affinity) with, - where co-located is defined as running - on a node whose value of the label with - key matches that of any - node on which a pod of the set of pods - is running - properties: - labelSelector: - description: A label query over a - set of resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, a key, - and an operator that relates - the key and values. - properties: - key: - description: key is the - label key that the selector - applies to. - type: string - operator: - description: operator represents - a key's relationship to - a set of values. Valid - operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an - array of string values. - If the operator is In - or NotIn, the values array - must be non-empty. If - the operator is Exists - or DoesNotExist, the values - array must be empty. This - array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a - map of {key,value} pairs. A - single {key,value} in the matchLabels - map is equivalent to an element - of matchExpressions, whose key - field is "key", the operator - is "In", and the values array - contains only "value". The requirements - are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the - set of namespaces that the term - applies to. The term is applied - to the union of the namespaces selected - by this field and the ones listed - in the namespaces field. null selector - and null or empty namespaces list - means "this pod's namespace". An - empty selector ({}) matches all - namespaces. This field is beta-level - and is only honored when PodAffinityNamespaceSelector - feature is enabled. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, a key, - and an operator that relates - the key and values. - properties: - key: - description: key is the - label key that the selector - applies to. - type: string - operator: - description: operator represents - a key's relationship to - a set of values. Valid - operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an - array of string values. - If the operator is In - or NotIn, the values array - must be non-empty. If - the operator is Exists - or DoesNotExist, the values - array must be empty. This - array is replaced during - a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a - map of {key,value} pairs. A - single {key,value} in the matchLabels - map is equivalent to an element - of matchExpressions, whose key - field is "key", the operator - is "In", and the values array - contains only "value". The requirements - are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies - a static list of namespace names - that the term applies to. The term - is applied to the union of the namespaces - listed in this field and the ones - selected by namespaceSelector. null - or empty namespaces list and null - namespaceSelector means "this pod's - namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where - co-located is defined as running - on a node whose value of the label - with key topologyKey matches that - of any node on which any of the - selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - automountServiceAccountToken: - description: AutomountServiceAccountToken indicates - whether a service account token should be automatically - mounted. - type: boolean - containers: - description: List of containers belonging to the - pod. Containers cannot currently be added or removed. - There must be at least one container in a Pod. - Cannot be updated. - items: - description: A single application container that - you want to run within a pod. - properties: - args: - description: 'Arguments to the entrypoint. - The docker image''s CMD is used if this - is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. - If a variable cannot be resolved, the reference - in the input string will be unchanged. Double - $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal - "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed - within a shell. The docker image''s ENTRYPOINT - is used if this is not provided. Variable - references $(VAR_NAME) are expanded using - the container''s environment. If a variable - cannot be resolved, the reference in the - input string will be unchanged. Double $$ - are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal - "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables - to set in the container. Cannot be updated. - items: - description: EnvVar represents an environment - variable present in a Container. - properties: - name: - description: Name of the environment - variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) - are expanded using the previously - defined environment variables in the - container and any service environment - variables. If a variable cannot be - resolved, the reference in the input - string will be unchanged. Double $$ - are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: - i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped - references will never be expanded, - regardless of whether the variable - exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment - variable's value. Cannot be used if - value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a - ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the ConfigMap or its key must - be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of - the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is written - in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory - and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output - format of the exposed resources, - defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a - secret in the pod's namespace - properties: - key: - description: The key of the - secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the Secret or its key must - be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment - variables in the container. The keys defined - within a source must be a C_IDENTIFIER. - All invalid keys will be reported as an - event when the container is starting. When - a key exists in multiple sources, the value - associated with the last source will take - precedence. Values defined by an Env with - a duplicate key will take precedence. Cannot - be updated. - items: - description: EnvFromSource represents the - source of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select - from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - ConfigMap must be defined - type: boolean - type: object - prefix: - description: An optional identifier - to prepend to each key in the ConfigMap. - Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - Secret must be defined - type: boolean - type: object - type: object - type: array - image: - description: 'Docker image name. More info: - https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level - config management to default or override - container images in workload controllers - like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, - Never, IfNotPresent. Defaults to Always - if :latest tag is specified, or IfNotPresent - otherwise. Cannot be updated. More info: - https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management system - should take in response to container lifecycle - events. Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately - after a container is created. If the - handler fails, the container is terminated - and restarted according to its restart - policy. Other management of the container - blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The - command is simply exec'd, it - is not run inside a shell, so - traditional shell instructions - ('|', etc) won't work. To use - a shell, you need to explicitly - call out to that shell. Exit - status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to - set in the request. HTTP allows - repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on - the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately - before a container is terminated due - to an API request or management event - such as liveness/startup probe failure, - preemption, resource contention, etc. - The handler is not called if the container - crashes or exits. The reason for termination - is passed to the handler. The Pod''s - termination grace period countdown begins - before the PreStop hooked is executed. - Regardless of the outcome of the handler, - the container will eventually terminate - within the Pod''s termination grace - period. Other management of the container - blocks until the hook completes or until - the termination grace period is reached. - More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The - command is simply exec'd, it - is not run inside a shell, so - traditional shell instructions - ('|', etc) won't work. To use - a shell, you need to explicitly - call out to that shell. Exit - status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to - set in the request. HTTP allows - repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on - the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container - liveness. Container will be restarted if - the probe fails. Cannot be updated. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified - as a DNS_LABEL. Each container in a pod - must have a unique name (DNS_LABEL). Cannot - be updated. - type: string - ports: - description: List of ports to expose from - the container. Exposing a port here gives - the system additional information about - the network connections a container uses, - but is primarily informational. Not specifying - a port here DOES NOT prevent that port from - being exposed. Any port which is listening - on the default "0.0.0.0" address inside - a container will be accessible from the - network. Cannot be updated. - items: - description: ContainerPort represents a - network port in a single container. - properties: - containerPort: - description: Number of port to expose - on the pod's IP address. This must - be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the - external port to. - type: string - hostPort: - description: Number of port to expose - on the host. If specified, this must - be a valid port number, 0 < x < 65536. - If HostNetwork is specified, this - must match ContainerPort. Most containers - do not need this. - format: int32 - type: integer - name: - description: If specified, this must - be an IANA_SVC_NAME and unique within - the pod. Each named port in a pod - must have a unique name. Name for - the port that can be referred to by - services. - type: string - protocol: - default: TCP - description: Protocol for port. Must - be UDP, TCP, or SCTP. Defaults to - "TCP". - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container - service readiness. Container will be removed - from service endpoints if the probe fails. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resources: - description: 'Compute Resources required by - this container. Cannot be updated. More - info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. - If Requests is omitted for a container, - it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined - value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'SecurityContext defines the - security options the container should be - run with. If set, the fields of SecurityContext - override the equivalent fields of PodSecurityContext. - More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation - controls whether a process can gain - more privileges than its parent process. - This bool directly controls if the no_new_privs - flag will be set on the container process. - AllowPrivilegeEscalation is true always - when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN' - type: boolean - capabilities: - description: The capabilities to add/drop - when running containers. Defaults to - the default set of capabilities granted - by the container runtime. - properties: - add: - description: Added capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged - mode. Processes in privileged containers - are essentially equivalent to root on - the host. Defaults to false. - type: boolean - procMount: - description: procMount denotes the type - of proc mount to use for the containers. - The default is DefaultProcMount which - uses the container runtime defaults - for readonly paths and masked paths. - This requires the ProcMountType feature - flag to be enabled. - type: string - readOnlyRootFilesystem: - description: Whether this container has - a read-only root filesystem. Default - is false. - type: boolean - runAsGroup: - description: The GID to run the entrypoint - of the container process. Uses runtime - default if unset. May also be set in - PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container - must run as a non-root user. If true, - the Kubelet will validate the image - at runtime to ensure that it does not - run as UID 0 (root) and fail to start - the container if it does. If unset or - false, no such validation will be performed. - May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint - of the container process. Defaults to - user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be - applied to the container. If unspecified, - the container runtime will allocate - a random SELinux context for each container. May - also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - properties: - level: - description: Level is SELinux level - label that applies to the container. - type: string - role: - description: Role is a SELinux role - label that applies to the container. - type: string - type: - description: Type is a SELinux type - label that applies to the container. - type: string - user: - description: User is a SELinux user - label that applies to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use - by this container. If seccomp options - are provided at both the pod & container - level, the container options override - the pod options. - properties: - localhostProfile: - description: localhostProfile indicates - a profile defined in a file on the - node should be used. The profile - must be preconfigured on the node - to work. Must be a descending path, - relative to the kubelet's configured - seccomp profile location. Must only - be set if type is "Localhost". - type: string - type: - description: "type indicates which - kind of seccomp profile will be - applied. Valid options are: \n Localhost - - a profile defined in a file on - the node should be used. RuntimeDefault - - the container runtime default - profile should be used. Unconfined - - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings - applied to all containers. If unspecified, - the options from the PodSecurityContext - will be used. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is - where the GMSA admission webhook - (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA - credential spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName - is the name of the GMSA credential - spec to use. - type: string - hostProcess: - description: HostProcess determines - if a container should be run as - a 'Host Process' container. This - field is alpha-level and will only - be honored by components that enable - the WindowsHostProcessContainers - feature flag. Setting this field - without the feature flag will result - in errors when validating the Pod. - All of a Pod's containers must have - the same effective HostProcess value - (it is not allowed to have a mix - of HostProcess containers and non-HostProcess - containers). In addition, if HostProcess - is true then HostNetwork must also - be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows - to run the entrypoint of the container - process. Defaults to the user specified - in image metadata if unspecified. - May also be set in PodSecurityContext. - If set in both SecurityContext and - PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that - the Pod has successfully initialized. If - specified, no other probes are executed - until this completes successfully. If this - probe fails, the Pod will be restarted, - just as if the livenessProbe failed. This - can be used to provide different probe parameters - at the beginning of a Pod''s lifecycle, - when it might take a long time to load data - or warm a cache, than during steady-state - operation. This cannot be updated. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should - allocate a buffer for stdin in the container - runtime. If this is not set, reads from - stdin in the container will always result - in EOF. Default is false. - type: boolean - stdinOnce: - description: Whether the container runtime - should close the stdin channel after it - has been opened by a single attach. When - stdin is true the stdin stream will remain - open across multiple attach sessions. If - stdinOnce is set to true, stdin is opened - on container start, is empty until the first - client attaches to stdin, and then remains - open and accepts data until the client disconnects, - at which time stdin is closed and remains - closed until the container is restarted. - If this flag is false, a container processes - that reads from stdin will never receive - an EOF. Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the - file to which the container''s termination - message will be written is mounted into - the container''s filesystem. Message written - is intended to be brief final status, such - as an assertion failure message. Will be - truncated by the node if greater than 4096 - bytes. The total message length across all - containers will be limited to 12kb. Defaults - to /dev/termination-log. Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination - message should be populated. File will use - the contents of terminationMessagePath to - populate the container status message on - both success and failure. FallbackToLogsOnError - will use the last chunk of container log - output if the termination message file is - empty and the container exited with an error. - The log output is limited to 2048 bytes - or 80 lines, whichever is smaller. Defaults - to File. Cannot be updated. - type: string - tty: - description: Whether this container should - allocate a TTY for itself, also requires - 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of - block devices to be used by the container. - items: - description: volumeDevice describes a mapping - of a raw block device within a container. - properties: - devicePath: - description: devicePath is the path - inside of the container that the device - will be mapped to. - type: string - name: - description: name must match the name - of a persistentVolumeClaim in the - pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the - container's filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting - of a Volume within a container. - properties: - mountPath: - description: Path within the container - at which the volume should be mounted. Must - not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines - how mounts are propagated from the - host to container and the other way - around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name - of a Volume. - type: string - readOnly: - description: Mounted read-only if true, - read-write otherwise (false or unspecified). - Defaults to false. - type: boolean - subPath: - description: Path within the volume - from which the container's volume - should be mounted. Defaults to "" - (volume's root). - type: string - subPathExpr: - description: Expanded path within the - volume from which the container's - volume should be mounted. Behaves - similarly to SubPath but environment - variable references $(VAR_NAME) are - expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr - and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. - If not specified, the container runtime's - default will be used, which might be configured - in the container image. Cannot be updated. - type: string - required: - - name - type: object - type: array - dnsConfig: - description: Specifies the DNS parameters of a pod. - Parameters specified here will be merged to the - generated DNS configuration based on DNSPolicy. - properties: - nameservers: - description: A list of DNS name server IP addresses. - This will be appended to the base nameservers - generated from DNSPolicy. Duplicated nameservers - will be removed. - items: - type: string - type: array - options: - description: A list of DNS resolver options. - This will be merged with the base options - generated from DNSPolicy. Duplicated entries - will be removed. Resolution options given - in Options will override those that appear - in the base DNSPolicy. - items: - description: PodDNSConfigOption defines DNS - resolver options of a pod. - properties: - name: - description: Required. - type: string - value: - type: string - type: object - type: array - searches: - description: A list of DNS search domains for - host-name lookup. This will be appended to - the base search paths generated from DNSPolicy. - Duplicated search paths will be removed. - items: - type: string - type: array - type: object - dnsPolicy: - description: Set DNS policy for the pod. Defaults - to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet', - 'ClusterFirst', 'Default' or 'None'. DNS parameters - given in DNSConfig will be merged with the policy - selected with DNSPolicy. To have DNS options set - along with hostNetwork, you have to specify DNS - policy explicitly to 'ClusterFirstWithHostNet'. - type: string - enableServiceLinks: - description: 'EnableServiceLinks indicates whether - information about services should be injected - into pod''s environment variables, matching the - syntax of Docker links. Optional: Defaults to - true.' - type: boolean - ephemeralContainers: - description: List of ephemeral containers run in - this pod. Ephemeral containers may be run in an - existing pod to perform user-initiated actions - such as debugging. This list cannot be specified - when creating a pod, and it cannot be modified - by updating the pod spec. In order to add an ephemeral - container to an existing pod, use the pod's ephemeralcontainers - subresource. This field is alpha-level and is - only honored by servers that enable the EphemeralContainers - feature. - items: - description: An EphemeralContainer is a container - that may be added temporarily to an existing - pod for user-initiated activities such as debugging. - Ephemeral containers have no resource or scheduling - guarantees, and they will not be restarted when - they exit or when a pod is removed or restarted. - If an ephemeral container causes a pod to exceed - its resource allocation, the pod may be evicted. - Ephemeral containers may not be added by directly - updating the pod spec. They must be added via - the pod's ephemeralcontainers subresource, and - they will appear in the pod spec once added. - This is an alpha feature enabled by the EphemeralContainers - feature flag. - properties: - args: - description: 'Arguments to the entrypoint. - The docker image''s CMD is used if this - is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. - If a variable cannot be resolved, the reference - in the input string will be unchanged. Double - $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal - "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed - within a shell. The docker image''s ENTRYPOINT - is used if this is not provided. Variable - references $(VAR_NAME) are expanded using - the container''s environment. If a variable - cannot be resolved, the reference in the - input string will be unchanged. Double $$ - are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal - "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables - to set in the container. Cannot be updated. - items: - description: EnvVar represents an environment - variable present in a Container. - properties: - name: - description: Name of the environment - variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) - are expanded using the previously - defined environment variables in the - container and any service environment - variables. If a variable cannot be - resolved, the reference in the input - string will be unchanged. Double $$ - are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: - i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped - references will never be expanded, - regardless of whether the variable - exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment - variable's value. Cannot be used if - value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a - ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the ConfigMap or its key must - be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of - the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is written - in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory - and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output - format of the exposed resources, - defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a - secret in the pod's namespace - properties: - key: - description: The key of the - secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the Secret or its key must - be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment - variables in the container. The keys defined - within a source must be a C_IDENTIFIER. - All invalid keys will be reported as an - event when the container is starting. When - a key exists in multiple sources, the value - associated with the last source will take - precedence. Values defined by an Env with - a duplicate key will take precedence. Cannot - be updated. - items: - description: EnvFromSource represents the - source of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select - from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - ConfigMap must be defined - type: boolean - type: object - prefix: - description: An optional identifier - to prepend to each key in the ConfigMap. - Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - Secret must be defined - type: boolean - type: object - type: object - type: array - image: - description: 'Docker image name. More info: - https://kubernetes.io/docs/concepts/containers/images' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, - Never, IfNotPresent. Defaults to Always - if :latest tag is specified, or IfNotPresent - otherwise. Cannot be updated. More info: - https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Lifecycle is not allowed for - ephemeral containers. - properties: - postStart: - description: 'PostStart is called immediately - after a container is created. If the - handler fails, the container is terminated - and restarted according to its restart - policy. Other management of the container - blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The - command is simply exec'd, it - is not run inside a shell, so - traditional shell instructions - ('|', etc) won't work. To use - a shell, you need to explicitly - call out to that shell. Exit - status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to - set in the request. HTTP allows - repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on - the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately - before a container is terminated due - to an API request or management event - such as liveness/startup probe failure, - preemption, resource contention, etc. - The handler is not called if the container - crashes or exits. The reason for termination - is passed to the handler. The Pod''s - termination grace period countdown begins - before the PreStop hooked is executed. - Regardless of the outcome of the handler, - the container will eventually terminate - within the Pod''s termination grace - period. Other management of the container - blocks until the hook completes or until - the termination grace period is reached. - More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The - command is simply exec'd, it - is not run inside a shell, so - traditional shell instructions - ('|', etc) won't work. To use - a shell, you need to explicitly - call out to that shell. Exit - status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to - set in the request. HTTP allows - repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on - the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: Probes are not allowed for ephemeral - containers. - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the ephemeral container - specified as a DNS_LABEL. This name must - be unique among all containers, init containers - and ephemeral containers. - type: string - ports: - description: Ports are not allowed for ephemeral - containers. - items: - description: ContainerPort represents a - network port in a single container. - properties: - containerPort: - description: Number of port to expose - on the pod's IP address. This must - be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the - external port to. - type: string - hostPort: - description: Number of port to expose - on the host. If specified, this must - be a valid port number, 0 < x < 65536. - If HostNetwork is specified, this - must match ContainerPort. Most containers - do not need this. - format: int32 - type: integer - name: - description: If specified, this must - be an IANA_SVC_NAME and unique within - the pod. Each named port in a pod - must have a unique name. Name for - the port that can be referred to by - services. - type: string - protocol: - default: TCP - description: Protocol for port. Must - be UDP, TCP, or SCTP. Defaults to - "TCP". - type: string - required: - - containerPort - type: object - type: array - readinessProbe: - description: Probes are not allowed for ephemeral - containers. - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resources: - description: Resources are not allowed for - ephemeral containers. Ephemeral containers - use spare resources already allocated to - the pod. - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. - If Requests is omitted for a container, - it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined - value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'Optional: SecurityContext defines - the security options the ephemeral container - should be run with. If set, the fields of - SecurityContext override the equivalent - fields of PodSecurityContext.' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation - controls whether a process can gain - more privileges than its parent process. - This bool directly controls if the no_new_privs - flag will be set on the container process. - AllowPrivilegeEscalation is true always - when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN' - type: boolean - capabilities: - description: The capabilities to add/drop - when running containers. Defaults to - the default set of capabilities granted - by the container runtime. - properties: - add: - description: Added capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged - mode. Processes in privileged containers - are essentially equivalent to root on - the host. Defaults to false. - type: boolean - procMount: - description: procMount denotes the type - of proc mount to use for the containers. - The default is DefaultProcMount which - uses the container runtime defaults - for readonly paths and masked paths. - This requires the ProcMountType feature - flag to be enabled. - type: string - readOnlyRootFilesystem: - description: Whether this container has - a read-only root filesystem. Default - is false. - type: boolean - runAsGroup: - description: The GID to run the entrypoint - of the container process. Uses runtime - default if unset. May also be set in - PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container - must run as a non-root user. If true, - the Kubelet will validate the image - at runtime to ensure that it does not - run as UID 0 (root) and fail to start - the container if it does. If unset or - false, no such validation will be performed. - May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint - of the container process. Defaults to - user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be - applied to the container. If unspecified, - the container runtime will allocate - a random SELinux context for each container. May - also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - properties: - level: - description: Level is SELinux level - label that applies to the container. - type: string - role: - description: Role is a SELinux role - label that applies to the container. - type: string - type: - description: Type is a SELinux type - label that applies to the container. - type: string - user: - description: User is a SELinux user - label that applies to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use - by this container. If seccomp options - are provided at both the pod & container - level, the container options override - the pod options. - properties: - localhostProfile: - description: localhostProfile indicates - a profile defined in a file on the - node should be used. The profile - must be preconfigured on the node - to work. Must be a descending path, - relative to the kubelet's configured - seccomp profile location. Must only - be set if type is "Localhost". - type: string - type: - description: "type indicates which - kind of seccomp profile will be - applied. Valid options are: \n Localhost - - a profile defined in a file on - the node should be used. RuntimeDefault - - the container runtime default - profile should be used. Unconfined - - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings - applied to all containers. If unspecified, - the options from the PodSecurityContext - will be used. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is - where the GMSA admission webhook - (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA - credential spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName - is the name of the GMSA credential - spec to use. - type: string - hostProcess: - description: HostProcess determines - if a container should be run as - a 'Host Process' container. This - field is alpha-level and will only - be honored by components that enable - the WindowsHostProcessContainers - feature flag. Setting this field - without the feature flag will result - in errors when validating the Pod. - All of a Pod's containers must have - the same effective HostProcess value - (it is not allowed to have a mix - of HostProcess containers and non-HostProcess - containers). In addition, if HostProcess - is true then HostNetwork must also - be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows - to run the entrypoint of the container - process. Defaults to the user specified - in image metadata if unspecified. - May also be set in PodSecurityContext. - If set in both SecurityContext and - PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: string - type: object - type: object - startupProbe: - description: Probes are not allowed for ephemeral - containers. - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should - allocate a buffer for stdin in the container - runtime. If this is not set, reads from - stdin in the container will always result - in EOF. Default is false. - type: boolean - stdinOnce: - description: Whether the container runtime - should close the stdin channel after it - has been opened by a single attach. When - stdin is true the stdin stream will remain - open across multiple attach sessions. If - stdinOnce is set to true, stdin is opened - on container start, is empty until the first - client attaches to stdin, and then remains - open and accepts data until the client disconnects, - at which time stdin is closed and remains - closed until the container is restarted. - If this flag is false, a container processes - that reads from stdin will never receive - an EOF. Default is false - type: boolean - targetContainerName: - description: If set, the name of the container - from PodSpec that this ephemeral container - targets. The ephemeral container will be - run in the namespaces (IPC, PID, etc) of - this container. If not set then the ephemeral - container is run in whatever namespaces - are shared for the pod. Note that the container - runtime must support this feature. - type: string - terminationMessagePath: - description: 'Optional: Path at which the - file to which the container''s termination - message will be written is mounted into - the container''s filesystem. Message written - is intended to be brief final status, such - as an assertion failure message. Will be - truncated by the node if greater than 4096 - bytes. The total message length across all - containers will be limited to 12kb. Defaults - to /dev/termination-log. Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination - message should be populated. File will use - the contents of terminationMessagePath to - populate the container status message on - both success and failure. FallbackToLogsOnError - will use the last chunk of container log - output if the termination message file is - empty and the container exited with an error. - The log output is limited to 2048 bytes - or 80 lines, whichever is smaller. Defaults - to File. Cannot be updated. - type: string - tty: - description: Whether this container should - allocate a TTY for itself, also requires - 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of - block devices to be used by the container. - items: - description: volumeDevice describes a mapping - of a raw block device within a container. - properties: - devicePath: - description: devicePath is the path - inside of the container that the device - will be mapped to. - type: string - name: - description: name must match the name - of a persistentVolumeClaim in the - pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the - container's filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting - of a Volume within a container. - properties: - mountPath: - description: Path within the container - at which the volume should be mounted. Must - not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines - how mounts are propagated from the - host to container and the other way - around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name - of a Volume. - type: string - readOnly: - description: Mounted read-only if true, - read-write otherwise (false or unspecified). - Defaults to false. - type: boolean - subPath: - description: Path within the volume - from which the container's volume - should be mounted. Defaults to "" - (volume's root). - type: string - subPathExpr: - description: Expanded path within the - volume from which the container's - volume should be mounted. Behaves - similarly to SubPath but environment - variable references $(VAR_NAME) are - expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr - and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. - If not specified, the container runtime's - default will be used, which might be configured - in the container image. Cannot be updated. - type: string - required: - - name - type: object - type: array - hostAliases: - description: HostAliases is an optional list of - hosts and IPs that will be injected into the pod's - hosts file if specified. This is only valid for - non-hostNetwork pods. - items: - description: HostAlias holds the mapping between - IP and hostnames that will be injected as an - entry in the pod's hosts file. - properties: - hostnames: - description: Hostnames for the above IP address. - items: - type: string - type: array - ip: - description: IP address of the host file entry. - type: string - type: object - type: array - hostIPC: - description: 'Use the host''s ipc namespace. Optional: - Default to false.' - type: boolean - hostNetwork: - description: Host networking requested for this - pod. Use the host's network namespace. If this - option is set, the ports that will be used must - be specified. Default to false. - type: boolean - hostPID: - description: 'Use the host''s pid namespace. Optional: - Default to false.' - type: boolean - hostname: - description: Specifies the hostname of the Pod If - not specified, the pod's hostname will be set - to a system-defined value. - type: string - imagePullSecrets: - description: 'ImagePullSecrets is an optional list - of references to secrets in the same namespace - to use for pulling any of the images used by this - PodSpec. If specified, these secrets will be passed - to individual puller implementations for them - to use. For example, in the case of docker, only - DockerConfig type secrets are honored. More info: - https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' - items: - description: LocalObjectReference contains enough - information to let you locate the referenced - object inside the same namespace. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - type: array - initContainers: - description: 'List of initialization containers - belonging to the pod. Init containers are executed - in order prior to containers being started. If - any init container fails, the pod is considered - to have failed and is handled according to its - restartPolicy. The name for an init container - or normal container must be unique among all containers. - Init containers may not have Lifecycle actions, - Readiness probes, Liveness probes, or Startup - probes. The resourceRequirements of an init container - are taken into account during scheduling by finding - the highest request/limit for each resource type, - and then using the max of of that value or the - sum of the normal containers. Limits are applied - to init containers in a similar fashion. Init - containers cannot currently be added or removed. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' - items: - description: A single application container that - you want to run within a pod. - properties: - args: - description: 'Arguments to the entrypoint. - The docker image''s CMD is used if this - is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. - If a variable cannot be resolved, the reference - in the input string will be unchanged. Double - $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal - "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed - within a shell. The docker image''s ENTRYPOINT - is used if this is not provided. Variable - references $(VAR_NAME) are expanded using - the container''s environment. If a variable - cannot be resolved, the reference in the - input string will be unchanged. Double $$ - are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal - "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables - to set in the container. Cannot be updated. - items: - description: EnvVar represents an environment - variable present in a Container. - properties: - name: - description: Name of the environment - variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) - are expanded using the previously - defined environment variables in the - container and any service environment - variables. If a variable cannot be - resolved, the reference in the input - string will be unchanged. Double $$ - are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: - i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped - references will never be expanded, - regardless of whether the variable - exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment - variable's value. Cannot be used if - value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a - ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the ConfigMap or its key must - be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of - the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is written - in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory - and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output - format of the exposed resources, - defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a - secret in the pod's namespace - properties: - key: - description: The key of the - secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the Secret or its key must - be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment - variables in the container. The keys defined - within a source must be a C_IDENTIFIER. - All invalid keys will be reported as an - event when the container is starting. When - a key exists in multiple sources, the value - associated with the last source will take - precedence. Values defined by an Env with - a duplicate key will take precedence. Cannot - be updated. - items: - description: EnvFromSource represents the - source of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select - from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - ConfigMap must be defined - type: boolean - type: object - prefix: - description: An optional identifier - to prepend to each key in the ConfigMap. - Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - Secret must be defined - type: boolean - type: object - type: object - type: array - image: - description: 'Docker image name. More info: - https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level - config management to default or override - container images in workload controllers - like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, - Never, IfNotPresent. Defaults to Always - if :latest tag is specified, or IfNotPresent - otherwise. Cannot be updated. More info: - https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management system - should take in response to container lifecycle - events. Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately - after a container is created. If the - handler fails, the container is terminated - and restarted according to its restart - policy. Other management of the container - blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The - command is simply exec'd, it - is not run inside a shell, so - traditional shell instructions - ('|', etc) won't work. To use - a shell, you need to explicitly - call out to that shell. Exit - status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to - set in the request. HTTP allows - repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on - the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately - before a container is terminated due - to an API request or management event - such as liveness/startup probe failure, - preemption, resource contention, etc. - The handler is not called if the container - crashes or exits. The reason for termination - is passed to the handler. The Pod''s - termination grace period countdown begins - before the PreStop hooked is executed. - Regardless of the outcome of the handler, - the container will eventually terminate - within the Pod''s termination grace - period. Other management of the container - blocks until the hook completes or until - the termination grace period is reached. - More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The - command is simply exec'd, it - is not run inside a shell, so - traditional shell instructions - ('|', etc) won't work. To use - a shell, you need to explicitly - call out to that shell. Exit - status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to - set in the request. HTTP allows - repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on - the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of - the port to access on the container. - Number must be in the range - 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container - liveness. Container will be restarted if - the probe fails. Cannot be updated. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified - as a DNS_LABEL. Each container in a pod - must have a unique name (DNS_LABEL). Cannot - be updated. - type: string - ports: - description: List of ports to expose from - the container. Exposing a port here gives - the system additional information about - the network connections a container uses, - but is primarily informational. Not specifying - a port here DOES NOT prevent that port from - being exposed. Any port which is listening - on the default "0.0.0.0" address inside - a container will be accessible from the - network. Cannot be updated. - items: - description: ContainerPort represents a - network port in a single container. - properties: - containerPort: - description: Number of port to expose - on the pod's IP address. This must - be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the - external port to. - type: string - hostPort: - description: Number of port to expose - on the host. If specified, this must - be a valid port number, 0 < x < 65536. - If HostNetwork is specified, this - must match ContainerPort. Most containers - do not need this. - format: int32 - type: integer - name: - description: If specified, this must - be an IANA_SVC_NAME and unique within - the pod. Each named port in a pod - must have a unique name. Name for - the port that can be referred to by - services. - type: string - protocol: - default: TCP - description: Protocol for port. Must - be UDP, TCP, or SCTP. Defaults to - "TCP". - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container - service readiness. Container will be removed - from service endpoints if the probe fails. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resources: - description: 'Compute Resources required by - this container. Cannot be updated. More - info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. - If Requests is omitted for a container, - it defaults to Limits if that is explicitly - specified, otherwise to an implementation-defined - value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'SecurityContext defines the - security options the container should be - run with. If set, the fields of SecurityContext - override the equivalent fields of PodSecurityContext. - More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation - controls whether a process can gain - more privileges than its parent process. - This bool directly controls if the no_new_privs - flag will be set on the container process. - AllowPrivilegeEscalation is true always - when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN' - type: boolean - capabilities: - description: The capabilities to add/drop - when running containers. Defaults to - the default set of capabilities granted - by the container runtime. - properties: - add: - description: Added capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged - mode. Processes in privileged containers - are essentially equivalent to root on - the host. Defaults to false. - type: boolean - procMount: - description: procMount denotes the type - of proc mount to use for the containers. - The default is DefaultProcMount which - uses the container runtime defaults - for readonly paths and masked paths. - This requires the ProcMountType feature - flag to be enabled. - type: string - readOnlyRootFilesystem: - description: Whether this container has - a read-only root filesystem. Default - is false. - type: boolean - runAsGroup: - description: The GID to run the entrypoint - of the container process. Uses runtime - default if unset. May also be set in - PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container - must run as a non-root user. If true, - the Kubelet will validate the image - at runtime to ensure that it does not - run as UID 0 (root) and fail to start - the container if it does. If unset or - false, no such validation will be performed. - May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint - of the container process. Defaults to - user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be - applied to the container. If unspecified, - the container runtime will allocate - a random SELinux context for each container. May - also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - properties: - level: - description: Level is SELinux level - label that applies to the container. - type: string - role: - description: Role is a SELinux role - label that applies to the container. - type: string - type: - description: Type is a SELinux type - label that applies to the container. - type: string - user: - description: User is a SELinux user - label that applies to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use - by this container. If seccomp options - are provided at both the pod & container - level, the container options override - the pod options. - properties: - localhostProfile: - description: localhostProfile indicates - a profile defined in a file on the - node should be used. The profile - must be preconfigured on the node - to work. Must be a descending path, - relative to the kubelet's configured - seccomp profile location. Must only - be set if type is "Localhost". - type: string - type: - description: "type indicates which - kind of seccomp profile will be - applied. Valid options are: \n Localhost - - a profile defined in a file on - the node should be used. RuntimeDefault - - the container runtime default - profile should be used. Unconfined - - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings - applied to all containers. If unspecified, - the options from the PodSecurityContext - will be used. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is - where the GMSA admission webhook - (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA - credential spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName - is the name of the GMSA credential - spec to use. - type: string - hostProcess: - description: HostProcess determines - if a container should be run as - a 'Host Process' container. This - field is alpha-level and will only - be honored by components that enable - the WindowsHostProcessContainers - feature flag. Setting this field - without the feature flag will result - in errors when validating the Pod. - All of a Pod's containers must have - the same effective HostProcess value - (it is not allowed to have a mix - of HostProcess containers and non-HostProcess - containers). In addition, if HostProcess - is true then HostNetwork must also - be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows - to run the entrypoint of the container - process. Defaults to the user specified - in image metadata if unspecified. - May also be set in PodSecurityContext. - If set in both SecurityContext and - PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that - the Pod has successfully initialized. If - specified, no other probes are executed - until this completes successfully. If this - probe fails, the Pod will be restarted, - just as if the livenessProbe failed. This - can be used to provide different probe parameters - at the beginning of a Pod''s lifecycle, - when it might take a long time to load data - or warm a cache, than during steady-state - operation. This cannot be updated. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies - the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it - is not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to - explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to - perform the probe. Default to 10 seconds. - Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not - yet supported TODO: implement a realistic - TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the pod - IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time when - the processes are forcibly halted with - a kill signal. Set this value longer - than the expected cleanup time for your - process. If this value is nil, the pod's - terminationGracePeriodSeconds will be - used. Otherwise, this value overrides - the value provided by the pod spec. - Value must be non-negative integer. - The value zero indicates stop immediately - via the kill signal (no opportunity - to shut down). This is a beta field - and requires enabling ProbeTerminationGracePeriod - feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should - allocate a buffer for stdin in the container - runtime. If this is not set, reads from - stdin in the container will always result - in EOF. Default is false. - type: boolean - stdinOnce: - description: Whether the container runtime - should close the stdin channel after it - has been opened by a single attach. When - stdin is true the stdin stream will remain - open across multiple attach sessions. If - stdinOnce is set to true, stdin is opened - on container start, is empty until the first - client attaches to stdin, and then remains - open and accepts data until the client disconnects, - at which time stdin is closed and remains - closed until the container is restarted. - If this flag is false, a container processes - that reads from stdin will never receive - an EOF. Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the - file to which the container''s termination - message will be written is mounted into - the container''s filesystem. Message written - is intended to be brief final status, such - as an assertion failure message. Will be - truncated by the node if greater than 4096 - bytes. The total message length across all - containers will be limited to 12kb. Defaults - to /dev/termination-log. Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination - message should be populated. File will use - the contents of terminationMessagePath to - populate the container status message on - both success and failure. FallbackToLogsOnError - will use the last chunk of container log - output if the termination message file is - empty and the container exited with an error. - The log output is limited to 2048 bytes - or 80 lines, whichever is smaller. Defaults - to File. Cannot be updated. - type: string - tty: - description: Whether this container should - allocate a TTY for itself, also requires - 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of - block devices to be used by the container. - items: - description: volumeDevice describes a mapping - of a raw block device within a container. - properties: - devicePath: - description: devicePath is the path - inside of the container that the device - will be mapped to. - type: string - name: - description: name must match the name - of a persistentVolumeClaim in the - pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the - container's filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting - of a Volume within a container. - properties: - mountPath: - description: Path within the container - at which the volume should be mounted. Must - not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines - how mounts are propagated from the - host to container and the other way - around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name - of a Volume. - type: string - readOnly: - description: Mounted read-only if true, - read-write otherwise (false or unspecified). - Defaults to false. - type: boolean - subPath: - description: Path within the volume - from which the container's volume - should be mounted. Defaults to "" - (volume's root). - type: string - subPathExpr: - description: Expanded path within the - volume from which the container's - volume should be mounted. Behaves - similarly to SubPath but environment - variable references $(VAR_NAME) are - expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr - and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. - If not specified, the container runtime's - default will be used, which might be configured - in the container image. Cannot be updated. - type: string - required: - - name - type: object - type: array - nodeName: - description: NodeName is a request to schedule this - pod onto a specific node. If it is non-empty, - the scheduler simply schedules this pod onto that - node, assuming that it fits resource requirements. - type: string - nodeSelector: - additionalProperties: - type: string - description: 'NodeSelector is a selector which must - be true for the pod to fit on a node. Selector - which must match a node''s labels for the pod - to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' - type: object - x-kubernetes-map-type: atomic - overhead: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Overhead represents the resource overhead - associated with running a pod for a given RuntimeClass. - This field will be autopopulated at admission - time by the RuntimeClass admission controller. - If the RuntimeClass admission controller is enabled, - overhead must not be set in Pod create requests. - The RuntimeClass admission controller will reject - Pod create requests which have the overhead already - set. If RuntimeClass is configured and selected - in the PodSpec, Overhead will be set to the value - defined in the corresponding RuntimeClass, otherwise - it will remain unset and treated as zero. More - info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md - This field is beta-level as of Kubernetes v1.18, - and is only honored by servers that enable the - PodOverhead feature.' - type: object - preemptionPolicy: - description: PreemptionPolicy is the Policy for - preempting pods with lower priority. One of Never, - PreemptLowerPriority. Defaults to PreemptLowerPriority - if unset. This field is beta-level, gated by the - NonPreemptingPriority feature-gate. - type: string - priority: - description: The priority value. Various system - components use this field to find the priority - of the pod. When Priority Admission Controller - is enabled, it prevents users from setting this - field. The admission controller populates this - field from PriorityClassName. The higher the value, - the higher the priority. - format: int32 - type: integer - priorityClassName: - description: If specified, indicates the pod's priority. - "system-node-critical" and "system-cluster-critical" - are two special keywords which indicate the highest - priorities with the former being the highest priority. - Any other name must be defined by creating a PriorityClass - object with that name. If not specified, the pod - priority will be default or zero if there is no - default. - type: string - readinessGates: - description: 'If specified, all readiness gates - will be evaluated for pod readiness. A pod is - ready when all its containers are ready AND all - conditions specified in the readiness gates have - status equal to "True" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates' - items: - description: PodReadinessGate contains the reference - to a pod condition - properties: - conditionType: - description: ConditionType refers to a condition - in the pod's condition list with matching - type. - type: string - required: - - conditionType - type: object - type: array - restartPolicy: - description: 'Restart policy for all containers - within the pod. One of Always, OnFailure, Never. - Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' - type: string - runtimeClassName: - description: 'RuntimeClassName refers to a RuntimeClass - object in the node.k8s.io group, which should - be used to run this pod. If no RuntimeClass resource - matches the named class, the pod will not be run. - If unset or empty, the "legacy" RuntimeClass will - be used, which is an implicit class with an empty - definition that uses the default runtime handler. - More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class - This is a beta feature as of Kubernetes v1.14.' - type: string - schedulerName: - description: If specified, the pod will be dispatched - by specified scheduler. If not specified, the - pod will be dispatched by default scheduler. - type: string - securityContext: - description: 'SecurityContext holds pod-level security - attributes and common container settings. Optional: - Defaults to empty. See type description for default - values of each field.' - properties: - fsGroup: - description: "A special supplemental group that - applies to all containers in a pod. Some volume - types allow the Kubelet to change the ownership - of that volume to be owned by the pod: \n - 1. The owning GID will be the FSGroup 2. The - setgid bit is set (new files created in the - volume will be owned by FSGroup) 3. The permission - bits are OR'd with rw-rw---- \n If unset, - the Kubelet will not modify the ownership - and permissions of any volume." - format: int64 - type: integer - fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior - of changing ownership and permission of the - volume before being exposed inside Pod. This - field will only apply to volume types which - support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume - types such as: secret, configmaps and emptydir. - Valid values are "OnRootMismatch" and "Always". - If not specified, "Always" is used.' - type: string - runAsGroup: - description: The GID to run the entrypoint of - the container process. Uses runtime default - if unset. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence for that container. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must - run as a non-root user. If true, the Kubelet - will validate the image at runtime to ensure - that it does not run as UID 0 (root) and fail - to start the container if it does. If unset - or false, no such validation will be performed. - May also be set in SecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of - the container process. Defaults to user specified - in image metadata if unspecified. May also - be set in SecurityContext. If set in both - SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence - for that container. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied - to all containers. If unspecified, the container - runtime will allocate a random SELinux context - for each container. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence for that container. - properties: - level: - description: Level is SELinux level label - that applies to the container. - type: string - role: - description: Role is a SELinux role label - that applies to the container. - type: string - type: - description: Type is a SELinux type label - that applies to the container. - type: string - user: - description: User is a SELinux user label - that applies to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by the - containers in this pod. - properties: - localhostProfile: - description: localhostProfile indicates - a profile defined in a file on the node - should be used. The profile must be preconfigured - on the node to work. Must be a descending - path, relative to the kubelet's configured - seccomp profile location. Must only be - set if type is "Localhost". - type: string - type: - description: "type indicates which kind - of seccomp profile will be applied. Valid - options are: \n Localhost - a profile - defined in a file on the node should be - used. RuntimeDefault - the container runtime - default profile should be used. Unconfined - - no profile should be applied." - type: string - required: - - type - type: object - supplementalGroups: - description: A list of groups applied to the - first process run in each container, in addition - to the container's primary GID. If unspecified, - no groups will be added to any container. - items: - format: int64 - type: integer - type: array - sysctls: - description: Sysctls hold a list of namespaced - sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail - to launch. - items: - description: Sysctl defines a kernel parameter - to be set - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - description: The Windows specific settings applied - to all containers. If unspecified, the options - within a container's SecurityContext will - be used. If set in both SecurityContext and - PodSecurityContext, the value specified in - SecurityContext takes precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where - the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential - spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the - name of the GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a - container should be run as a 'Host Process' - container. This field is alpha-level and - will only be honored by components that - enable the WindowsHostProcessContainers - feature flag. Setting this field without - the feature flag will result in errors - when validating the Pod. All of a Pod's - containers must have the same effective - HostProcess value (it is not allowed to - have a mix of HostProcess containers and - non-HostProcess containers). In addition, - if HostProcess is true then HostNetwork - must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to - run the entrypoint of the container process. - Defaults to the user specified in image - metadata if unspecified. May also be set - in PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - serviceAccount: - description: 'DeprecatedServiceAccount is a depreciated - alias for ServiceAccountName. Deprecated: Use - serviceAccountName instead.' - type: string - serviceAccountName: - description: 'ServiceAccountName is the name of - the ServiceAccount to use to run this pod. More - info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' - type: string - setHostnameAsFQDN: - description: If true the pod's hostname will be - configured as the pod's FQDN, rather than the - leaf name (the default). In Linux containers, - this means setting the FQDN in the hostname field - of the kernel (the nodename field of struct utsname). - In Windows containers, this means setting the - registry value of hostname for the registry key - HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters - to FQDN. If a pod does not have FQDN, this has - no effect. Default to false. - type: boolean - shareProcessNamespace: - description: 'Share a single process namespace between - all of the containers in a pod. When this is set - containers will be able to view and signal processes - from other containers in the same pod, and the - first process in each container will not be assigned - PID 1. HostPID and ShareProcessNamespace cannot - both be set. Optional: Default to false.' - type: boolean - subdomain: - description: If specified, the fully qualified Pod - hostname will be "...svc.". If not specified, - the pod will not have a domainname at all. - type: string - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod - needs to terminate gracefully. May be decreased - in delete request. Value must be non-negative - integer. The value zero indicates stop immediately - via the kill signal (no opportunity to shut down). - If this value is nil, the default grace period - will be used instead. The grace period is the - duration in seconds after the processes running - in the pod are sent a termination signal and the - time when the processes are forcibly halted with - a kill signal. Set this value longer than the - expected cleanup time for your process. Defaults - to 30 seconds. - format: int64 - type: integer - tolerations: - description: If specified, the pod's tolerations. - items: - description: The pod this Toleration is attached - to tolerates any taint that matches the triple - using the matching operator - . - properties: - effect: - description: Effect indicates the taint effect - to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, - PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the - toleration applies to. Empty means match - all taint keys. If the key is empty, operator - must be Exists; this combination means to - match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists - and Equal. Defaults to Equal. Exists is - equivalent to wildcard for value, so that - a pod can tolerate all taints of a particular - category. - type: string - tolerationSeconds: - description: TolerationSeconds represents - the period of time the toleration (which - must be of effect NoExecute, otherwise this - field is ignored) tolerates the taint. By - default, it is not set, which means tolerate - the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict - immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the - toleration matches to. If the operator is - Exists, the value should be empty, otherwise - just a regular string. - type: string - type: object - type: array - topologySpreadConstraints: - description: TopologySpreadConstraints describes - how a group of pods ought to spread across topology - domains. Scheduler will schedule pods in a way - which abides by the constraints. All topologySpreadConstraints - are ANDed. - items: - description: TopologySpreadConstraint specifies - how to spread matching pods among the given - topology. - properties: - labelSelector: - description: LabelSelector is used to find - matching pods. Pods that match this label - selector are counted to determine the number - of pods in their corresponding topology - domain. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - maxSkew: - description: 'MaxSkew describes the degree - to which pods may be unevenly distributed. - When `whenUnsatisfiable=DoNotSchedule`, - it is the maximum permitted difference between - the number of matching pods in the target - topology and the global minimum. For example, - in a 3-zone cluster, MaxSkew is set to 1, - and pods with the same labelSelector spread - as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | - - if MaxSkew is 1, incoming pod can only - be scheduled to zone3 to become 1/1/1; scheduling - it onto zone1(zone2) would make the ActualSkew(2-0) - on zone1(zone2) violate MaxSkew(1). - if - MaxSkew is 2, incoming pod can be scheduled - onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, - it is used to give higher precedence to - topologies that satisfy it. It''s a required - field. Default value is 1 and 0 is not allowed.' - format: int32 - type: integer - topologyKey: - description: TopologyKey is the key of node - labels. Nodes that have a label with this - key and identical values are considered - to be in the same topology. We consider - each as a "bucket", and try - to put balanced number of pods into each - bucket. It's a required field. - type: string - whenUnsatisfiable: - description: 'WhenUnsatisfiable indicates - how to deal with a pod if it doesn''t satisfy - the spread constraint. - DoNotSchedule (default) - tells the scheduler not to schedule it. - - ScheduleAnyway tells the scheduler to - schedule the pod in any location, but - giving higher precedence to topologies that - would help reduce the skew. A constraint - is considered "Unsatisfiable" for an incoming - pod if and only if every possible node assigment - for that pod would violate "MaxSkew" on - some topology. For example, in a 3-zone - cluster, MaxSkew is set to 1, and pods with - the same labelSelector spread as 3/1/1: - | zone1 | zone2 | zone3 | | P P P | P | P | - If WhenUnsatisfiable is set to DoNotSchedule, - incoming pod can only be scheduled to zone2(zone3) - to become 3/2/1(3/1/2) as ActualSkew(2-1) - on zone2(zone3) satisfies MaxSkew(1). In - other words, the cluster can still be imbalanced, - but scheduler won''t make it *more* imbalanced. - It''s a required field.' - type: string - required: - - maxSkew - - topologyKey - - whenUnsatisfiable - type: object - type: array - x-kubernetes-list-map-keys: - - topologyKey - - whenUnsatisfiable - x-kubernetes-list-type: map - volumes: - description: 'List of volumes that can be mounted - by containers belonging to the pod. More info: - https://kubernetes.io/docs/concepts/storage/volumes' - items: - description: Volume represents a named volume - in a pod that may be accessed by any container - in the pod. - properties: - awsElasticBlockStore: - description: 'AWSElasticBlockStore represents - an AWS Disk resource that is attached to - a kubelet''s host machine and then exposed - to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - properties: - fsType: - description: 'Filesystem type of the volume - that you want to mount. Tip: Ensure - that the filesystem type is supported - by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - partition: - description: 'The partition in the volume - that you want to mount. If omitted, - the default is to mount by volume name. - Examples: For volume /dev/sda1, you - specify the partition as "1". Similarly, - the volume partition for /dev/sda is - "0" (or you can leave the property empty).' - format: int32 - type: integer - readOnly: - description: 'Specify "true" to force - and set the ReadOnly property in VolumeMounts - to "true". If omitted, the default is - "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: boolean - volumeID: - description: 'Unique ID of the persistent - disk resource in AWS (Amazon EBS volume). - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: string - required: - - volumeID - type: object - azureDisk: - description: AzureDisk represents an Azure - Data Disk mount on the host and bind mount - to the pod. - properties: - cachingMode: - description: 'Host Caching mode: None, - Read Only, Read Write.' - type: string - diskName: - description: The Name of the data disk - in the blob storage - type: string - diskURI: - description: The URI the data disk in - the blob storage - type: string - fsType: - description: Filesystem type to mount. - Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. - type: string - kind: - description: 'Expected values Shared: - multiple blob disks per storage account Dedicated: - single blob disk per storage account Managed: - azure managed data disk (only in managed - availability set). defaults to shared' - type: string - readOnly: - description: Defaults to false (read/write). - ReadOnly here will force the ReadOnly - setting in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: AzureFile represents an Azure - File Service mount on the host and bind - mount to the pod. - properties: - readOnly: - description: Defaults to false (read/write). - ReadOnly here will force the ReadOnly - setting in VolumeMounts. - type: boolean - secretName: - description: the name of secret that contains - Azure Storage Account Name and Key - type: string - shareName: - description: Share Name - type: string - required: - - secretName - - shareName - type: object - cephfs: - description: CephFS represents a Ceph FS mount - on the host that shares a pod's lifetime - properties: - monitors: - description: 'Required: Monitors is a - collection of Ceph monitors More info: - https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - items: - type: string - type: array - path: - description: 'Optional: Used as the mounted - root, rather than the full Ceph tree, - default is /' - type: string - readOnly: - description: 'Optional: Defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: boolean - secretFile: - description: 'Optional: SecretFile is - the path to key ring for User, default - is /etc/ceph/user.secret More info: - https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - secretRef: - description: 'Optional: SecretRef is reference - to the authentication secret for User, - default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - user: - description: 'Optional: User is the rados - user name, default is admin More info: - https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - required: - - monitors - type: object - cinder: - description: 'Cinder represents a cinder volume - attached and mounted on kubelets host machine. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - properties: - fsType: - description: 'Filesystem type to mount. - Must be a filesystem type supported - by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: - https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - readOnly: - description: 'Optional: Defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: boolean - secretRef: - description: 'Optional: points to a secret - object containing parameters used to - connect to OpenStack.' - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - volumeID: - description: 'volume id used to identify - the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - required: - - volumeID - type: object - configMap: - description: ConfigMap represents a configMap - that should populate this volume - properties: - defaultMode: - description: 'Optional: mode bits used - to set permissions on created files - by default. Must be an octal value between - 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. - Directories within the path are not - affected by this setting. This might - be in conflict with other options that - affect the file mode, like fsGroup, - and the result can be other mode bits - set.' - format: int32 - type: integer - items: - description: If unspecified, each key-value - pair in the Data field of the referenced - ConfigMap will be projected into the - volume as a file whose name is the key - and content is the value. If specified, - the listed keys will be projected into - the specified paths, and unlisted keys - will not be present. If a key is specified - which is not present in the ConfigMap, - the volume setup will error unless it - is marked optional. Paths must be relative - and may not contain the '..' path or - start with '..'. - items: - description: Maps a string key to a - path within a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits - used to set permissions on this - file. Must be an octal value between - 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts - both octal and decimal values, - JSON requires decimal values for - mode bits. If not specified, the - volume defaultMode will be used. - This might be in conflict with - other options that affect the - file mode, like fsGroup, and the - result can be other mode bits - set.' - format: int32 - type: integer - path: - description: The relative path of - the file to map the key to. May - not be an absolute path. May not - contain the path element '..'. - May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its keys must be defined - type: boolean - type: object - csi: - description: CSI (Container Storage Interface) - represents ephemeral storage that is handled - by certain external CSI drivers (Beta feature). - properties: - driver: - description: Driver is the name of the - CSI driver that handles this volume. - Consult with your admin for the correct - name as registered in the cluster. - type: string - fsType: - description: Filesystem type to mount. - Ex. "ext4", "xfs", "ntfs". If not provided, - the empty value is passed to the associated - CSI driver which will determine the - default filesystem to apply. - type: string - nodePublishSecretRef: - description: NodePublishSecretRef is a - reference to the secret object containing - sensitive information to pass to the - CSI driver to complete the CSI NodePublishVolume - and NodeUnpublishVolume calls. This - field is optional, and may be empty - if no secret is required. If the secret - object contains more than one secret, - all secret references are passed. - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - readOnly: - description: Specifies a read-only configuration - for the volume. Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: VolumeAttributes stores driver-specific - properties that are passed to the CSI - driver. Consult your driver's documentation - for supported values. - type: object - required: - - driver - type: object - downwardAPI: - description: DownwardAPI represents downward - API about the pod that should populate this - volume - properties: - defaultMode: - description: 'Optional: mode bits to use - on created files by default. Must be - a Optional: mode bits used to set permissions - on created files by default. Must be - an octal value between 0000 and 0777 - or a decimal value between 0 and 511. - YAML accepts both octal and decimal - values, JSON requires decimal values - for mode bits. Defaults to 0644. Directories - within the path are not affected by - this setting. This might be in conflict - with other options that affect the file - mode, like fsGroup, and the result can - be other mode bits set.' - format: int32 - type: integer - items: - description: Items is a list of downward - API volume file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects - a field of the pod: only annotations, - labels, name and namespace are - supported.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is written - in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits - used to set permissions on this - file, must be an octal value between - 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts - both octal and decimal values, - JSON requires decimal values for - mode bits. If not specified, the - volume defaultMode will be used. - This might be in conflict with - other options that affect the - file mode, like fsGroup, and the - result can be other mode bits - set.' - format: int32 - type: integer - path: - description: 'Required: Path is the - relative path name of the file - to be created. Must not be absolute - or contain the ''..'' path. Must - be utf-8 encoded. The first item - of the relative path must not - start with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, requests.cpu and - requests.memory) are currently - supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output - format of the exposed resources, - defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - description: 'EmptyDir represents a temporary - directory that shares a pod''s lifetime. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - properties: - medium: - description: 'What type of storage medium - should back this directory. The default - is "" which means to use the node''s - default medium. Must be an empty string - (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'Total amount of local storage - required for this EmptyDir volume. The - size limit is also applicable for memory - medium. The maximum usage on memory - medium EmptyDir would be the minimum - value between the SizeLimit specified - here and the sum of memory limits of - all containers in a pod. The default - is nil which means that the limit is - undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: "Ephemeral represents a volume - that is handled by a cluster storage driver. - The volume's lifecycle is tied to the pod - that defines it - it will be created before - the pod starts, and deleted when the pod - is removed. \n Use this if: a) the volume - is only needed while the pod runs, b) features - of normal volumes like restoring from snapshot - or capacity tracking are needed, c) the - storage driver is specified through a storage - class, and d) the storage driver supports - dynamic volume provisioning through a - PersistentVolumeClaim (see EphemeralVolumeSource - for more information on the connection - between this volume type and PersistentVolumeClaim). - \n Use PersistentVolumeClaim or one of the - vendor-specific APIs for volumes that persist - for longer than the lifecycle of an individual - pod. \n Use CSI for light-weight local ephemeral - volumes if the CSI driver is meant to be - used that way - see the documentation of - the driver for more information." - properties: - volumeClaimTemplate: - description: "Will be used to create a - stand-alone PVC to provision the volume. - The pod in which this EphemeralVolumeSource - is embedded will be the owner of the - PVC, i.e. the PVC will be deleted together - with the pod. The name of the PVC will - be `-` where - `` is the name from the - `PodSpec.Volumes` array entry. Pod validation - will reject the pod if the concatenated - name is not valid for a PVC (for example, - too long). \n An existing PVC with that - name that is not owned by the pod will - *not* be used for the pod to avoid using - an unrelated volume by mistake. Starting - the pod is then blocked until the unrelated - PVC is removed. If such a pre-created - PVC is meant to be used by the pod, - the PVC has to updated with an owner - reference to the pod once the pod exists. - Normally this should not be necessary, - but it may be useful when manually reconstructing - a broken cluster. \n This field is read-only - and no changes will be made by Kubernetes - to the PVC after it has been created." - properties: - metadata: - description: May contain labels and - annotations that will be copied - into the PVC when creating it. No - other fields are allowed and will - be rejected during validation. - type: object - spec: - description: The specification for - the PersistentVolumeClaim. The entire - content is copied unchanged into - the PVC that gets created from this - template. The same fields as in - a PersistentVolumeClaim are also - valid here. - properties: - accessModes: - description: 'AccessModes contains - the desired access modes the - volume should have. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'This field can be - used to specify either: * An - existing VolumeSnapshot object - (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external - controller can support the specified - data source, it will create - a new volume based on the contents - of the specified data source. - If the AnyVolumeDataSource feature - gate is enabled, this field - will always have the same contents - as the DataSourceRef field.' - properties: - apiGroup: - description: APIGroup is the - group for the resource being - referenced. If APIGroup - is not specified, the specified - Kind must be in the core - API group. For any other - third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type - of resource being referenced - type: string - name: - description: Name is the name - of resource being referenced - type: string - required: - - kind - - name - type: object - dataSourceRef: - description: 'Specifies the object - from which to populate the volume - with data, if a non-empty volume - is desired. This may be any - local object from a non-empty - API group (non core object) - or a PersistentVolumeClaim object. - When this field is specified, - volume binding will only succeed - if the type of the specified - object matches some installed - volume populator or dynamic - provisioner. This field will - replace the functionality of - the DataSource field and as - such if both fields are non-empty, - they must have the same value. - For backwards compatibility, - both fields (DataSource and - DataSourceRef) will be set to - the same value automatically - if one of them is empty and - the other is non-empty. There - are two important differences - between DataSource and DataSourceRef: - * While DataSource only allows - two specific types of objects, - DataSourceRef allows any non-core - object, as well as PersistentVolumeClaim - objects.' - properties: - apiGroup: - description: APIGroup is the - group for the resource being - referenced. If APIGroup - is not specified, the specified - Kind must be in the core - API group. For any other - third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type - of resource being referenced - type: string - name: - description: Name is the name - of resource being referenced - type: string - required: - - kind - - name - type: object - resources: - description: 'Resources represents - the minimum resources the volume - should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes - the maximum amount of compute - resources allowed. More - info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes - the minimum amount of compute - resources required. If Requests - is omitted for a container, - it defaults to Limits if - that is explicitly specified, - otherwise to an implementation-defined - value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: A label query over - volumes to consider for binding. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: A label selector - requirement is a selector - that contains values, - a key, and an operator - that relates the key and - values. - properties: - key: - description: key is - the label key that - the selector applies - to. - type: string - operator: - description: operator - represents a key's - relationship to a - set of values. Valid - operators are In, - NotIn, Exists and - DoesNotExist. - type: string - values: - description: values - is an array of string - values. If the operator - is In or NotIn, the - values array must - be non-empty. If the - operator is Exists - or DoesNotExist, the - values array must - be empty. This array - is replaced during - a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is - a map of {key,value} pairs. - A single {key,value} in - the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", - the operator is "In", and - the values array contains - only "value". The requirements - are ANDed. - type: object - type: object - storageClassName: - description: 'Name of the StorageClass - required by the claim. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines - what type of volume is required - by the claim. Value of Filesystem - is implied when not included - in claim spec. - type: string - volumeName: - description: VolumeName is the - binding reference to the PersistentVolume - backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - fc: - description: FC represents a Fibre Channel - resource that is attached to a kubelet's - host machine and then exposed to the pod. - properties: - fsType: - description: 'Filesystem type to mount. - Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. TODO: how - do we prevent errors in the filesystem - from compromising the machine' - type: string - lun: - description: 'Optional: FC target lun - number' - format: int32 - type: integer - readOnly: - description: 'Optional: Defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts.' - type: boolean - targetWWNs: - description: 'Optional: FC target worldwide - names (WWNs)' - items: - type: string - type: array - wwids: - description: 'Optional: FC volume world - wide identifiers (wwids) Either wwids - or combination of targetWWNs and lun - must be set, but not both simultaneously.' - items: - type: string - type: array - type: object - flexVolume: - description: FlexVolume represents a generic - volume resource that is provisioned/attached - using an exec based plugin. - properties: - driver: - description: Driver is the name of the - driver to use for this volume. - type: string - fsType: - description: Filesystem type to mount. - Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". The default filesystem - depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'Optional: Extra command - options if any.' - type: object - readOnly: - description: 'Optional: Defaults to false - (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts.' - type: boolean - secretRef: - description: 'Optional: SecretRef is reference - to the secret object containing sensitive - information to pass to the plugin scripts. - This may be empty if no secret object - is specified. If the secret object contains - more than one secret, all secrets are - passed to the plugin scripts.' - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - required: - - driver - type: object - flocker: - description: Flocker represents a Flocker - volume attached to a kubelet's host machine. - This depends on the Flocker control service - being running - properties: - datasetName: - description: Name of the dataset stored - as metadata -> name on the dataset for - Flocker should be considered as deprecated - type: string - datasetUUID: - description: UUID of the dataset. This - is unique identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: 'GCEPersistentDisk represents - a GCE Disk resource that is attached to - a kubelet''s host machine and then exposed - to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - properties: - fsType: - description: 'Filesystem type of the volume - that you want to mount. Tip: Ensure - that the filesystem type is supported - by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - partition: - description: 'The partition in the volume - that you want to mount. If omitted, - the default is to mount by volume name. - Examples: For volume /dev/sda1, you - specify the partition as "1". Similarly, - the volume partition for /dev/sda is - "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 - type: integer - pdName: - description: 'Unique name of the PD resource - in GCE. Used to identify the disk in - GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: string - readOnly: - description: 'ReadOnly here will force - the ReadOnly setting in VolumeMounts. - Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: boolean - required: - - pdName - type: object - gitRepo: - description: 'GitRepo represents a git repository - at a particular revision. DEPRECATED: GitRepo - is deprecated. To provision a container - with a git repo, mount an EmptyDir into - an InitContainer that clones the repo using - git, then mount the EmptyDir into the Pod''s - container.' - properties: - directory: - description: Target directory name. Must - not contain or start with '..'. If - '.' is supplied, the volume directory - will be the git repository. Otherwise, - if specified, the volume will contain - the git repository in the subdirectory - with the given name. - type: string - repository: - description: Repository URL - type: string - revision: - description: Commit hash for the specified - revision. - type: string - required: - - repository - type: object - glusterfs: - description: 'Glusterfs represents a Glusterfs - mount on the host that shares a pod''s lifetime. - More info: https://examples.k8s.io/volumes/glusterfs/README.md' - properties: - endpoints: - description: 'EndpointsName is the endpoint - name that details Glusterfs topology. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - path: - description: 'Path is the Glusterfs volume - path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - readOnly: - description: 'ReadOnly here will force - the Glusterfs volume to be mounted with - read-only permissions. Defaults to false. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: 'HostPath represents a pre-existing - file or directory on the host machine that - is directly exposed to the container. This - is generally used for system agents or other - privileged things that are allowed to see - the host machine. Most containers will NOT - need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who - can use host directory mounts and who can/can - not mount host directories as read/write.' - properties: - path: - description: 'Path of the directory on - the host. If the path is a symlink, - it will follow the link to the real - path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'Type for HostPath Volume - Defaults to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - iscsi: - description: 'ISCSI represents an ISCSI Disk - resource that is attached to a kubelet''s - host machine and then exposed to the pod. - More info: https://examples.k8s.io/volumes/iscsi/README.md' - properties: - chapAuthDiscovery: - description: whether support iSCSI Discovery - CHAP authentication - type: boolean - chapAuthSession: - description: whether support iSCSI Session - CHAP authentication - type: boolean - fsType: - description: 'Filesystem type of the volume - that you want to mount. Tip: Ensure - that the filesystem type is supported - by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - initiatorName: - description: Custom iSCSI Initiator Name. - If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface - : will be - created for the connection. - type: string - iqn: - description: Target iSCSI Qualified Name. - type: string - iscsiInterface: - description: iSCSI Interface Name that - uses an iSCSI transport. Defaults to - 'default' (tcp). - type: string - lun: - description: iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: iSCSI Target Portal List. - The portal is either an IP or ip_addr:port - if the port is other than default (typically - TCP ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: ReadOnly here will force - the ReadOnly setting in VolumeMounts. - Defaults to false. - type: boolean - secretRef: - description: CHAP Secret for iSCSI target - and initiator authentication - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - targetPortal: - description: iSCSI Target Portal. The - Portal is either an IP or ip_addr:port - if the port is other than default (typically - TCP ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: 'Volume''s name. Must be a DNS_LABEL - and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - nfs: - description: 'NFS represents an NFS mount - on the host that shares a pod''s lifetime - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - properties: - path: - description: 'Path that is exported by - the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - readOnly: - description: 'ReadOnly here will force - the NFS export to be mounted with read-only - permissions. Defaults to false. More - info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: boolean - server: - description: 'Server is the hostname or - IP address of the NFS server. More info: - https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: 'PersistentVolumeClaimVolumeSource - represents a reference to a PersistentVolumeClaim - in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - claimName: - description: 'ClaimName is the name of - a PersistentVolumeClaim in the same - namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - type: string - readOnly: - description: Will force the ReadOnly setting - in VolumeMounts. Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: PhotonPersistentDisk represents - a PhotonController persistent disk attached - and mounted on kubelets host machine - properties: - fsType: - description: Filesystem type to mount. - Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. - type: string - pdID: - description: ID that identifies Photon - Controller persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: PortworxVolume represents a portworx - volume attached and mounted on kubelets - host machine - properties: - fsType: - description: FSType represents the filesystem - type to mount Must be a filesystem type - supported by the host operating system. - Ex. "ext4", "xfs". Implicitly inferred - to be "ext4" if unspecified. - type: string - readOnly: - description: Defaults to false (read/write). - ReadOnly here will force the ReadOnly - setting in VolumeMounts. - type: boolean - volumeID: - description: VolumeID uniquely identifies - a Portworx volume - type: string - required: - - volumeID - type: object - projected: - description: Items for all in one resources - secrets, configmaps, and downward API - properties: - defaultMode: - description: Mode bits used to set permissions - on created files by default. Must be - an octal value between 0000 and 0777 - or a decimal value between 0 and 511. - YAML accepts both octal and decimal - values, JSON requires decimal values - for mode bits. Directories within the - path are not affected by this setting. - This might be in conflict with other - options that affect the file mode, like - fsGroup, and the result can be other - mode bits set. - format: int32 - type: integer - sources: - description: list of volume projections - items: - description: Projection that may be - projected along with other supported - volume types - properties: - configMap: - description: information about the - configMap data to project - properties: - items: - description: If unspecified, - each key-value pair in the - Data field of the referenced - ConfigMap will be projected - into the volume as a file - whose name is the key and - content is the value. If specified, - the listed keys will be projected - into the specified paths, - and unlisted keys will not - be present. If a key is specified - which is not present in the - ConfigMap, the volume setup - will error unless it is marked - optional. Paths must be relative - and may not contain the '..' - path or start with '..'. - items: - description: Maps a string - key to a path within a volume. - properties: - key: - description: The key to - project. - type: string - mode: - description: 'Optional: - mode bits used to set - permissions on this - file. Must be an octal - value between 0000 and - 0777 or a decimal value - between 0 and 511. YAML - accepts both octal and - decimal values, JSON - requires decimal values - for mode bits. If not - specified, the volume - defaultMode will be - used. This might be - in conflict with other - options that affect - the file mode, like - fsGroup, and the result - can be other mode bits - set.' - format: int32 - type: integer - path: - description: The relative - path of the file to - map the key to. May - not be an absolute path. - May not contain the - path element '..'. May - not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the ConfigMap or its keys - must be defined - type: boolean - type: object - downwardAPI: - description: information about the - downwardAPI data to project - properties: - items: - description: Items is a list - of DownwardAPIVolume file - items: - description: DownwardAPIVolumeFile - represents information to - create the file containing - the pod field - properties: - fieldRef: - description: 'Required: - Selects a field of the - pod: only annotations, - labels, name and namespace - are supported.' - properties: - apiVersion: - description: Version - of the schema the - FieldPath is written - in terms of, defaults - to "v1". - type: string - fieldPath: - description: Path - of the field to - select in the specified - API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: - mode bits used to set - permissions on this - file, must be an octal - value between 0000 and - 0777 or a decimal value - between 0 and 511. YAML - accepts both octal and - decimal values, JSON - requires decimal values - for mode bits. If not - specified, the volume - defaultMode will be - used. This might be - in conflict with other - options that affect - the file mode, like - fsGroup, and the result - can be other mode bits - set.' - format: int32 - type: integer - path: - description: 'Required: - Path is the relative - path name of the file - to be created. Must - not be absolute or contain - the ''..'' path. Must - be utf-8 encoded. The - first item of the relative - path must not start - with ''..''' - type: string - resourceFieldRef: - description: 'Selects - a resource of the container: - only resources limits - and requests (limits.cpu, - limits.memory, requests.cpu - and requests.memory) - are currently supported.' - properties: - containerName: - description: 'Container - name: required for - volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies - the output format - of the exposed resources, - defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: - resource to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - description: information about the - secret data to project - properties: - items: - description: If unspecified, - each key-value pair in the - Data field of the referenced - Secret will be projected into - the volume as a file whose - name is the key and content - is the value. If specified, - the listed keys will be projected - into the specified paths, - and unlisted keys will not - be present. If a key is specified - which is not present in the - Secret, the volume setup will - error unless it is marked - optional. Paths must be relative - and may not contain the '..' - path or start with '..'. - items: - description: Maps a string - key to a path within a volume. - properties: - key: - description: The key to - project. - type: string - mode: - description: 'Optional: - mode bits used to set - permissions on this - file. Must be an octal - value between 0000 and - 0777 or a decimal value - between 0 and 511. YAML - accepts both octal and - decimal values, JSON - requires decimal values - for mode bits. If not - specified, the volume - defaultMode will be - used. This might be - in conflict with other - options that affect - the file mode, like - fsGroup, and the result - can be other mode bits - set.' - format: int32 - type: integer - path: - description: The relative - path of the file to - map the key to. May - not be an absolute path. - May not contain the - path element '..'. May - not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the Secret or its key must - be defined - type: boolean - type: object - serviceAccountToken: - description: information about the - serviceAccountToken data to project - properties: - audience: - description: Audience is the - intended audience of the token. - A recipient of a token must - identify itself with an identifier - specified in the audience - of the token, and otherwise - should reject the token. The - audience defaults to the identifier - of the apiserver. - type: string - expirationSeconds: - description: ExpirationSeconds - is the requested duration - of validity of the service - account token. As the token - approaches expiration, the - kubelet volume plugin will - proactively rotate the service - account token. The kubelet - will start trying to rotate - the token if the token is - older than 80 percent of its - time to live or if the token - is older than 24 hours.Defaults - to 1 hour and must be at least - 10 minutes. - format: int64 - type: integer - path: - description: Path is the path - relative to the mount point - of the file to project the - token into. - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - description: Quobyte represents a Quobyte - mount on the host that shares a pod's lifetime - properties: - group: - description: Group to map volume access - to Default is no group - type: string - readOnly: - description: ReadOnly here will force - the Quobyte volume to be mounted with - read-only permissions. Defaults to false. - type: boolean - registry: - description: Registry represents a single - or multiple Quobyte Registry services - specified as a string as host:port pair - (multiple entries are separated with - commas) which acts as the central registry - for volumes - type: string - tenant: - description: Tenant owning the given Quobyte - volume in the Backend Used with dynamically - provisioned Quobyte volumes, value is - set by the plugin - type: string - user: - description: User to map volume access - to Defaults to serivceaccount user - type: string - volume: - description: Volume is a string that references - an already created Quobyte volume by - name. - type: string - required: - - registry - - volume - type: object - rbd: - description: 'RBD represents a Rados Block - Device mount on the host that shares a pod''s - lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' - properties: - fsType: - description: 'Filesystem type of the volume - that you want to mount. Tip: Ensure - that the filesystem type is supported - by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred - to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - image: - description: 'The rados image name. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - keyring: - description: 'Keyring is the path to key - ring for RBDUser. Default is /etc/ceph/keyring. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - monitors: - description: 'A collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - items: - type: string - type: array - pool: - description: 'The rados pool name. Default - is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - readOnly: - description: 'ReadOnly here will force - the ReadOnly setting in VolumeMounts. - Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: boolean - secretRef: - description: 'SecretRef is name of the - authentication secret for RBDUser. If - provided overrides keyring. Default - is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - user: - description: 'The rados user name. Default - is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - required: - - image - - monitors - type: object - scaleIO: - description: ScaleIO represents a ScaleIO - persistent volume attached and mounted on - Kubernetes nodes. - properties: - fsType: - description: Filesystem type to mount. - Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Default is "xfs". - type: string - gateway: - description: The host address of the ScaleIO - API Gateway. - type: string - protectionDomain: - description: The name of the ScaleIO Protection - Domain for the configured storage. - type: string - readOnly: - description: Defaults to false (read/write). - ReadOnly here will force the ReadOnly - setting in VolumeMounts. - type: boolean - secretRef: - description: SecretRef references to the - secret for ScaleIO user and other sensitive - information. If this is not provided, - Login operation will fail. - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - sslEnabled: - description: Flag to enable/disable SSL - communication with Gateway, default - false - type: boolean - storageMode: - description: Indicates whether the storage - for a volume should be ThickProvisioned - or ThinProvisioned. Default is ThinProvisioned. - type: string - storagePool: - description: The ScaleIO Storage Pool - associated with the protection domain. - type: string - system: - description: The name of the storage system - as configured in ScaleIO. - type: string - volumeName: - description: The name of a volume already - created in the ScaleIO system that is - associated with this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: 'Secret represents a secret that - should populate this volume. More info: - https://kubernetes.io/docs/concepts/storage/volumes#secret' - properties: - defaultMode: - description: 'Optional: mode bits used - to set permissions on created files - by default. Must be an octal value between - 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and - decimal values, JSON requires decimal - values for mode bits. Defaults to 0644. - Directories within the path are not - affected by this setting. This might - be in conflict with other options that - affect the file mode, like fsGroup, - and the result can be other mode bits - set.' - format: int32 - type: integer - items: - description: If unspecified, each key-value - pair in the Data field of the referenced - Secret will be projected into the volume - as a file whose name is the key and - content is the value. If specified, - the listed keys will be projected into - the specified paths, and unlisted keys - will not be present. If a key is specified - which is not present in the Secret, - the volume setup will error unless it - is marked optional. Paths must be relative - and may not contain the '..' path or - start with '..'. - items: - description: Maps a string key to a - path within a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits - used to set permissions on this - file. Must be an octal value between - 0000 and 0777 or a decimal value - between 0 and 511. YAML accepts - both octal and decimal values, - JSON requires decimal values for - mode bits. If not specified, the - volume defaultMode will be used. - This might be in conflict with - other options that affect the - file mode, like fsGroup, and the - result can be other mode bits - set.' - format: int32 - type: integer - path: - description: The relative path of - the file to map the key to. May - not be an absolute path. May not - contain the path element '..'. - May not start with the string - '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: Specify whether the Secret - or its keys must be defined - type: boolean - secretName: - description: 'Name of the secret in the - pod''s namespace to use. More info: - https://kubernetes.io/docs/concepts/storage/volumes#secret' - type: string - type: object - storageos: - description: StorageOS represents a StorageOS - volume attached and mounted on Kubernetes - nodes. - properties: - fsType: - description: Filesystem type to mount. - Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. - type: string - readOnly: - description: Defaults to false (read/write). - ReadOnly here will force the ReadOnly - setting in VolumeMounts. - type: boolean - secretRef: - description: SecretRef specifies the secret - to use for obtaining the StorageOS API - credentials. If not specified, default - values will be attempted. - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - volumeName: - description: VolumeName is the human-readable - name of the StorageOS volume. Volume - names are only unique within a namespace. - type: string - volumeNamespace: - description: VolumeNamespace specifies - the scope of the volume within StorageOS. If - no namespace is specified then the Pod's - namespace will be used. This allows - the Kubernetes name scoping to be mirrored - within StorageOS for tighter integration. - Set VolumeName to any name to override - the default behaviour. Set to "default" - if you are not using namespaces within - StorageOS. Namespaces that do not pre-exist - within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: VsphereVolume represents a vSphere - volume attached and mounted on kubelets - host machine - properties: - fsType: - description: Filesystem type to mount. - Must be a filesystem type supported - by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. - type: string - storagePolicyID: - description: Storage Policy Based Management - (SPBM) profile ID associated with the - StoragePolicyName. - type: string - storagePolicyName: - description: Storage Policy Based Management - (SPBM) profile name. - type: string - volumePath: - description: Path that identifies vSphere - volume vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - required: - - containers - type: object - type: object - required: - - selector - - template - type: object - required: - - spec - type: object - type: array - additionalServices: - items: - description: DeploymentTemplateSpec defines the pool template of - Deployment. - properties: - metadata: - type: object - spec: - description: ServiceSpec describes the attributes that a user - creates on a service. - properties: - allocateLoadBalancerNodePorts: - description: allocateLoadBalancerNodePorts defines if NodePorts - will be automatically allocated for services with type - LoadBalancer. Default is "true". It may be set to "false" - if the cluster load-balancer does not rely on NodePorts. If - the caller requests specific NodePorts (by specifying - a value), those requests will be respected, regardless - of this field. This field may only be set for services - with type LoadBalancer and will be cleared if the type - is changed to any other type. This field is beta-level - and is only honored by servers that enable the ServiceLBNodePortControl - feature. - type: boolean - clusterIP: - description: clusterIP is the IP address of the service - and is usually assigned randomly. If an address is specified - manually, is in-range (as per system configuration), and - is not in use, it will be allocated to the service; otherwise - creation of the service will fail. This field may not - be changed through updates unless the type field is also - being changed to ExternalName (which requires this field - to be blank) or the type field is being changed from ExternalName - (in which case this field may optionally be specified, - as describe above). Valid values are "None", empty string - (""), or a valid IP address. Setting this to "None" makes - a "headless service" (no virtual IP), which is useful - when direct endpoint connections are preferred and proxying - is not required. Only applies to types ClusterIP, NodePort, - and LoadBalancer. If this field is specified when creating - a Service of type ExternalName, creation will fail. This - field will be wiped when updating a Service to type ExternalName. - type: string - clusterIPs: - description: ClusterIPs is a list of IP addresses assigned - to this service, and are usually assigned randomly. If - an address is specified manually, is in-range (as per - system configuration), and is not in use, it will be allocated - to the service; otherwise creation of the service will - fail. This field may not be changed through updates unless - the type field is also being changed to ExternalName (which - requires this field to be empty) or the type field is - being changed from ExternalName (in which case this field - may optionally be specified, as describe above). Valid - values are "None", empty string (""), or a valid IP address. Setting - this to "None" makes a "headless service" (no virtual - IP), which is useful when direct endpoint connections - are preferred and proxying is not required. Only applies - to types ClusterIP, NodePort, and LoadBalancer. If this - field is specified when creating a Service of type ExternalName, - creation will fail. - items: - type: string - type: array - x-kubernetes-list-type: atomic - externalIPs: - description: externalIPs is a list of IP addresses for which - nodes in the cluster will also accept traffic for this - service. These IPs are not managed by Kubernetes. The - user is responsible for ensuring that traffic arrives - at a node with this IP. A common example is external - load-balancers that are not part of the Kubernetes system. - items: - type: string - type: array - externalName: - description: externalName is the external reference that - discovery mechanisms will return as an alias for this - service (e.g. a DNS CNAME record). No proxying will be - involved. Must be a lowercase RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) - and requires `type` to be "ExternalName". - type: string - externalTrafficPolicy: - description: externalTrafficPolicy denotes if this Service - desires to route external traffic to node-local or cluster-wide - endpoints. "Local" preserves the client source IP and - avoids a second hop for LoadBalancer and Nodeport type - services, but risks potentially imbalanced traffic spreading. - "Cluster" obscures the client source IP and may cause - a second hop to another node, but should have good overall - load-spreading. - type: string - healthCheckNodePort: - description: healthCheckNodePort specifies the healthcheck - nodePort for the service. This only applies when type - is set to LoadBalancer and externalTrafficPolicy is set - to Local. If a value is specified, is in-range, and is - not in use, it will be used. If not specified, a value - will be automatically allocated. External systems (e.g. - load-balancers) can use this port to determine if a given - node holds endpoints for this service or not. If this - field is specified when creating a Service which does - not need it, creation will fail. This field will be wiped - when updating a Service to no longer need it (e.g. changing - type). - format: int32 - type: integer - internalTrafficPolicy: - description: InternalTrafficPolicy specifies if the cluster - internal traffic should be routed to all endpoints or - node-local endpoints only. "Cluster" routes internal traffic - to a Service to all endpoints. "Local" routes traffic - to node-local endpoints only, traffic is dropped if no - node-local endpoints are ready. The default value is "Cluster". - type: string - ipFamilies: - description: "IPFamilies is a list of IP families (e.g. - IPv4, IPv6) assigned to this service, and is gated by - the \"IPv6DualStack\" feature gate. This field is usually - assigned automatically based on cluster configuration - and the ipFamilyPolicy field. If this field is specified - manually, the requested family is available in the cluster, - and ipFamilyPolicy allows it, it will be used; otherwise - creation of the service will fail. This field is conditionally - mutable: it allows for adding or removing a secondary - IP family, but it does not allow changing the primary - IP family of the Service. Valid values are \"IPv4\" and - \"IPv6\". This field only applies to Services of types - ClusterIP, NodePort, and LoadBalancer, and does apply - to \"headless\" services. This field will be wiped when - updating a Service to type ExternalName. \n This field - may hold a maximum of two entries (dual-stack families, - in either order). These families must correspond to the - values of the clusterIPs field, if specified." - items: - description: IPFamily represents the IP Family (IPv4 or - IPv6). This type is used to express the family of an - IP expressed by a type (e.g. service.spec.ipFamilies). - type: string - type: array - x-kubernetes-list-type: atomic - ipFamilyPolicy: - description: IPFamilyPolicy represents the dual-stack-ness - requested or required by this Service, and is gated by - the "IPv6DualStack" feature gate. If there is no value - provided, then this field will be set to SingleStack. - Services can be "SingleStack" (a single IP family), "PreferDualStack" - (two IP families on dual-stack configured clusters or - a single IP family on single-stack clusters), or "RequireDualStack" - (two IP families on dual-stack configured clusters, otherwise - fail). The ipFamilies and clusterIPs fields depend on - the value of this field. This field will be wiped when - updating a service to type ExternalName. - type: string - loadBalancerClass: - description: loadBalancerClass is the class of the load - balancer implementation this Service belongs to. If specified, - the value of this field must be a label-style identifier, - with an optional prefix, e.g. "internal-vip" or "example.com/internal-vip". - Unprefixed names are reserved for end-users. This field - can only be set when the Service type is 'LoadBalancer'. - If not set, the default load balancer implementation is - used, today this is typically done through the cloud provider - integration, but should apply for any default implementation. - If set, it is assumed that a load balancer implementation - is watching for Services with a matching class. Any default - load balancer implementation (e.g. cloud providers) should - ignore Services that set this field. This field can only - be set when creating or updating a Service to type 'LoadBalancer'. - Once set, it can not be changed. This field will be wiped - when a service is updated to a non 'LoadBalancer' type. - type: string - loadBalancerIP: - description: 'Only applies to Service Type: LoadBalancer - LoadBalancer will get created with the IP specified in - this field. This feature depends on whether the underlying - cloud-provider supports specifying the loadBalancerIP - when a load balancer is created. This field will be ignored - if the cloud-provider does not support the feature.' - type: string - loadBalancerSourceRanges: - description: 'If specified and supported by the platform, - this will restrict traffic through the cloud-provider - load-balancer will be restricted to the specified client - IPs. This field will be ignored if the cloud-provider - does not support the feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/' - items: - type: string - type: array - ports: - description: 'The list of ports that are exposed by this - service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' - items: - description: ServicePort contains information on service's - port. - properties: - appProtocol: - description: The application protocol for this port. - This field follows standard Kubernetes label syntax. - Un-prefixed names are reserved for IANA standard - service names (as per RFC-6335 and http://www.iana.org/assignments/service-names). - Non-standard protocols should use prefixed names - such as mycompany.com/my-custom-protocol. - type: string - name: - description: The name of this port within the service. - This must be a DNS_LABEL. All ports within a ServiceSpec - must have unique names. When considering the endpoints - for a Service, this must match the 'name' field - in the EndpointPort. Optional if only one ServicePort - is defined on this service. - type: string - nodePort: - description: 'The port on each node on which this - service is exposed when type is NodePort or LoadBalancer. Usually - assigned by the system. If a value is specified, - in-range, and not in use it will be used, otherwise - the operation will fail. If not specified, a port - will be allocated if this Service requires one. If - this field is specified when creating a Service - which does not need it, creation will fail. This - field will be wiped when updating a Service to no - longer need it (e.g. changing type from NodePort - to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' - format: int32 - type: integer - port: - description: The port that will be exposed by this - service. - format: int32 - type: integer - protocol: - default: TCP - description: The IP protocol for this port. Supports - "TCP", "UDP", and "SCTP". Default is TCP. - type: string - targetPort: - anyOf: - - type: integer - - type: string - description: 'Number or name of the port to access - on the pods targeted by the service. Number must - be in the range 1 to 65535. Name must be an IANA_SVC_NAME. - If this is a string, it will be looked up as a named - port in the target Pod''s container ports. If this - is not specified, the value of the ''port'' field - is used (an identity map). This field is ignored - for services with clusterIP=None, and should be - omitted or set equal to the ''port'' field. More - info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - x-kubernetes-list-map-keys: - - port - - protocol - x-kubernetes-list-type: map - publishNotReadyAddresses: - description: publishNotReadyAddresses indicates that any - agent which deals with endpoints for this Service should - disregard any indications of ready/not-ready. The primary - use case for setting this field is for a StatefulSet's - Headless Service to propagate SRV DNS records for its - Pods for the purpose of peer discovery. The Kubernetes - controllers that generate Endpoints and EndpointSlice - resources for Services interpret this to mean that all - endpoints are considered "ready" even if the Pods themselves - are not. Agents which consume only Kubernetes generated - endpoints through the Endpoints or EndpointSlice resources - can safely assume this behavior. - type: boolean - selector: - additionalProperties: - type: string - description: 'Route service traffic to pods with label keys - and values matching this selector. If empty or not present, - the service is assumed to have an external process managing - its endpoints, which Kubernetes will not modify. Only - applies to types ClusterIP, NodePort, and LoadBalancer. - Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' - type: object - x-kubernetes-map-type: atomic - sessionAffinity: - description: 'Supports "ClientIP" and "None". Used to maintain - session affinity. Enable client IP based session affinity. - Must be ClientIP or None. Defaults to None. More info: - https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' - type: string - sessionAffinityConfig: - description: sessionAffinityConfig contains the configurations - of session affinity. - properties: - clientIP: - description: clientIP contains the configurations of - Client IP based session affinity. - properties: - timeoutSeconds: - description: timeoutSeconds specifies the seconds - of ClientIP type session sticky time. The value - must be >0 && <=86400(for 1 day) if ServiceAffinity - == "ClientIP". Default value is 10800(for 3 hours). - format: int32 - type: integer - type: object - type: object - type: - description: 'type determines how the Service is exposed. - Defaults to ClusterIP. Valid options are ExternalName, - ClusterIP, NodePort, and LoadBalancer. "ClusterIP" allocates - a cluster-internal IP address for load-balancing to endpoints. - Endpoints are determined by the selector or if that is - not specified, by manual construction of an Endpoints - object or EndpointSlice objects. If clusterIP is "None", - no virtual IP is allocated and the endpoints are published - as a set of endpoints rather than a virtual IP. "NodePort" - builds on ClusterIP and allocates a port on every node - which routes to the same endpoints as the clusterIP. "LoadBalancer" - builds on NodePort and creates an external load-balancer - (if supported in the current cloud) which routes to the - same endpoints as the clusterIP. "ExternalName" aliases - this service to the specified externalName. Several other - fields do not apply to ExternalName services. More info: - https://kubernetes.' - type: string - type: object - required: - - spec - type: object - type: array - imageRegistry: - type: string - poolName: - type: string - serviceType: - description: Service Type string describes ingress methods for a service - type: string - version: - type: string - type: object - status: - description: PlatformAdminStatus defines the observed state of PlatformAdmin - properties: - conditions: - description: Current PlatformAdmin state - items: - description: PlatformAdminCondition describes current state of a - PlatformAdmin. - properties: - lastTransitionTime: - description: Last time the condition transitioned from one status - to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of in place set condition. - type: string - type: object - type: array - deploymentReadyReplicas: - format: int32 - type: integer - deploymentReplicas: - format: int32 - type: integer - initialized: - type: boolean - ready: - type: boolean - serviceReadyReplicas: - format: int32 - type: integer - serviceReplicas: - format: int32 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - description: The platformadmin ready status - jsonPath: .status.ready - name: READY - type: boolean - - description: The Ready Component. - jsonPath: .status.readyComponentNum - name: ReadyComponentNum - type: integer - - description: The Unready Component. - jsonPath: .status.unreadyComponentNum - name: UnreadyComponentNum - type: integer - name: v1alpha2 - schema: - openAPIV3Schema: - description: PlatformAdmin is the Schema for the samples API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: PlatformAdminSpec defines the desired state of PlatformAdmin - properties: - components: - items: - description: Component defines the components of EdgeX - properties: - image: - type: string - name: - type: string - required: - - name - type: object - type: array - imageRegistry: - type: string - platform: - type: string - poolName: - type: string - security: - type: boolean - version: - type: string - type: object - status: - description: PlatformAdminStatus defines the observed state of PlatformAdmin - properties: - conditions: - description: Current PlatformAdmin state - items: - description: PlatformAdminCondition describes current state of a - PlatformAdmin. - properties: - lastTransitionTime: - description: Last time the condition transitioned from one status - to another. - format: date-time - type: string - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of in place set condition. - type: string - type: object - type: array - initialized: - type: boolean - ready: - type: boolean - readyComponentNum: - format: int32 - type: integer - unreadyComponentNum: - format: int32 - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceprofiles.iot.openyurt.io.yaml b/charts/yurt-manager/crds/iot.openyurt.io_deviceprofiles.yaml similarity index 100% rename from charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceprofiles.iot.openyurt.io.yaml rename to charts/yurt-manager/crds/iot.openyurt.io_deviceprofiles.yaml diff --git a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_devices.iot.openyurt.io.yaml b/charts/yurt-manager/crds/iot.openyurt.io_devices.yaml similarity index 100% rename from charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_devices.iot.openyurt.io.yaml rename to charts/yurt-manager/crds/iot.openyurt.io_devices.yaml diff --git a/charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceservices.iot.openyurt.io.yaml b/charts/yurt-manager/crds/iot.openyurt.io_deviceservices.yaml similarity index 100% rename from charts/yurt-manager/crds/apiextensions.k8s.io_v1_customresourcedefinition_deviceservices.iot.openyurt.io.yaml rename to charts/yurt-manager/crds/iot.openyurt.io_deviceservices.yaml diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index 78365265389..72e35edad21 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -374,7 +374,16 @@ rules: - list - watch - apiGroups: - - device.openyurt.io + - discovery.k8s.io + resources: + - endpointslices + verbs: + - get + - list + - patch + - watch +- apiGroups: + - iot.openyurt.io resources: - deviceprofiles verbs: @@ -386,13 +395,13 @@ rules: - update - watch - apiGroups: - - device.openyurt.io + - iot.openyurt.io resources: - deviceprofiles/finalizers verbs: - update - apiGroups: - - device.openyurt.io + - iot.openyurt.io resources: - deviceprofiles/status verbs: @@ -400,7 +409,7 @@ rules: - patch - update - apiGroups: - - device.openyurt.io + - iot.openyurt.io resources: - devices verbs: @@ -412,13 +421,13 @@ rules: - update - watch - apiGroups: - - device.openyurt.io + - iot.openyurt.io resources: - devices/finalizers verbs: - update - apiGroups: - - device.openyurt.io + - iot.openyurt.io resources: - devices/status verbs: @@ -426,7 +435,7 @@ rules: - patch - update - apiGroups: - - device.openyurt.io + - iot.openyurt.io resources: - deviceservices verbs: @@ -438,28 +447,19 @@ rules: - update - watch - apiGroups: - - device.openyurt.io + - iot.openyurt.io resources: - deviceservices/finalizers verbs: - update - apiGroups: - - device.openyurt.io + - iot.openyurt.io resources: - deviceservices/status verbs: - get - patch - update -- apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - get - - list - - patch - - watch - apiGroups: - iot.openyurt.io resources: diff --git a/charts/yurt-manager/templates/yurt-manager-iot.yaml b/charts/yurt-manager/templates/yurt-manager-iot.yaml new file mode 100644 index 00000000000..a835d4499d1 --- /dev/null +++ b/charts/yurt-manager/templates/yurt-manager-iot.yaml @@ -0,0 +1,88 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: yurt-iot-carrier +rules: + - apiGroups: + - "" + resources: + - events + verbs: + - get + - apiGroups: + - iot.openyurt.io + resources: + - devices + - deviceservices + - deviceprofiles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - iot.openyurt.io + resources: + - devices/status + - deviceprofiles/status + - deviceservices/status + verbs: + - get + - patch + - update + - apiGroups: + - iot.openyurt.io + resources: + - devices/finalizers + - deviceprofiles/finalizers + - deviceservices/finalizers + verbs: + - update + - apiGroups: + - apps.openyurt.io + resources: + - nodepools + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: yurt-iot-carrier +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: yurt-iot-carrier +subjects: + - apiGroup: rbac.authorization.k8s.io + kind: Group + name: system:authenticated \ No newline at end of file diff --git a/hack/make-rules/kustomize_to_chart.sh b/hack/make-rules/kustomize_to_chart.sh index a9442b31b2a..f8e0f98eff8 100755 --- a/hack/make-rules/kustomize_to_chart.sh +++ b/hack/make-rules/kustomize_to_chart.sh @@ -192,6 +192,10 @@ EOF mv ${crd_dir}/apiextensions.k8s.io_v1_customresourcedefinition_yurtappdaemons.apps.openyurt.io.yaml ${crd_dir}/apps.openyurt.io_yurtappdaemons.yaml mv ${crd_dir}/apiextensions.k8s.io_v1_customresourcedefinition_yurtappsets.apps.openyurt.io.yaml ${crd_dir}/apps.openyurt.io_yurtappsets.yaml mv ${crd_dir}/apiextensions.k8s.io_v1_customresourcedefinition_gateways.raven.openyurt.io.yaml ${crd_dir}/raven.openyurt.io_gateways.yaml + mv ${crd_dir}/apiextensions.k8s.io_v1_customresourcedefinition_platformadmins.iot.openyurt.io.yaml ${crd_dir}/iot.openyurt.io_platformadmins.yaml + mv ${crd_dir}/apiextensions.k8s.io_v1_customresourcedefinition_devices.iot.openyurt.io.yaml ${crd_dir}/iot.openyurt.io_devices.yaml + mv ${crd_dir}/apiextensions.k8s.io_v1_customresourcedefinition_deviceservices.iot.openyurt.io.yaml ${crd_dir}/iot.openyurt.io_deviceservices.yaml + mv ${crd_dir}/apiextensions.k8s.io_v1_customresourcedefinition_deviceprofiles.iot.openyurt.io.yaml ${crd_dir}/iot.openyurt.io_deviceprofiles.yaml # rbac dir local rbac_kustomization_resources="" diff --git a/pkg/controller/platformadmin/platformadmin_controller.go b/pkg/controller/platformadmin/platformadmin_controller.go index 2f9d0a40409..eb113785491 100644 --- a/pkg/controller/platformadmin/platformadmin_controller.go +++ b/pkg/controller/platformadmin/platformadmin_controller.go @@ -781,90 +781,89 @@ func NewYurtIoTCarrierComponent(platformAdmin *iotv1alpha2.PlatformAdmin) (*conf } // YurtIoTCarrier doesn't need a service yet yurtIoTCarrierConfig := map[string]interface{}{ - "deploymentTemplate": map[string]interface{}{ - "metadata": map[string]interface{}{ - "creationTimestamp": nil, - "labels": map[string]interface{}{ + "metadata": map[string]interface{}{ + "creationTimestamp": nil, + "labels": map[string]interface{}{ + "app": "yurt-iot-carrier", + }, + "namespace": ns, + }, + "spec": map[string]interface{}{ + "selector": map[string]interface{}{ + "matchLabels": map[string]interface{}{ "app": "yurt-iot-carrier", }, - "Namespace": ns, }, - "spec": map[string]interface{}{ - "selector": map[string]interface{}{ - "matchLabels": map[string]interface{}{ - "app": "yurt-iot-carrier", + "strategy": map[string]interface{}{}, + "template": map[string]interface{}{ + "metadata": map[string]interface{}{ + "creationTimestamp": nil, + "labels": map[string]interface{}{ + "app": "yurt-iot-carrier", + "control-plane": "edgex-controller-manager", }, }, - "strategy": map[string]interface{}{}, - "template": map[string]interface{}{ - "metadata": map[string]interface{}{ - "creationTimestamp": nil, - "labels": map[string]interface{}{ - "app": "yurt-iot-carrier", - "control-plane": "edgex-controller-manager", - }, - }, - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "args": []interface{}{ - "--health-probe-bind-address=:8081", - "--metrics-bind-address=127.0.0.1:8080", - "--leader-elect=false", - }, - "image": fmt.Sprintf("leoabyss/yurt-iot-carrier:%s", ver), - "imagePullPolicy": "IfNotPresent", - "livenessProbe": map[string]interface{}{ - "failureThreshold": 3, - "httpGet": map[string]interface{}{ - "path": "/healthz", - "port": 8081, - "scheme": "HTTP", - }, - "initialDelaySeconds": 15, - "periodSeconds": 20, - "successThreshold": 1, - "timeoutSeconds": 1, + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "args": []interface{}{ + "--health-probe-bind-address=:8081", + "--metrics-bind-address=127.0.0.1:8080", + "--leader-elect=false", + fmt.Sprintf("--namespace=%s", ns), + }, + "image": fmt.Sprintf("leoabyss/yurt-iot-carrier:%s", ver), + "imagePullPolicy": "IfNotPresent", + "livenessProbe": map[string]interface{}{ + "failureThreshold": 3, + "httpGet": map[string]interface{}{ + "path": "/healthz", + "port": 8081, + "scheme": "HTTP", }, - "name": "yurt-iot-carrier", - "readinessProbe": map[string]interface{}{ - "failureThreshold": 3, - "httpGet": map[string]interface{}{ - "path": "/readyz", - "port": 8081, - "scheme": "HTTP", - }, - "initialDelaySeconds": 5, - "periodSeconds": 10, - "successThreshold": 1, - "timeoutSeconds": 1, + "initialDelaySeconds": 15, + "periodSeconds": 20, + "successThreshold": 1, + "timeoutSeconds": 1, + }, + "name": "yurt-iot-carrier", + "readinessProbe": map[string]interface{}{ + "failureThreshold": 3, + "httpGet": map[string]interface{}{ + "path": "/readyz", + "port": 8081, + "scheme": "HTTP", }, - "resources": map[string]interface{}{ - "limits": map[string]interface{}{ - "cpu": "100m", - "memory": "512Mi", - }, - "requests": map[string]interface{}{ - "cpu": "100m", - "memory": "512Mi", - }, + "initialDelaySeconds": 5, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1, + }, + "resources": map[string]interface{}{ + "limits": map[string]interface{}{ + "cpu": "100m", + "memory": "512Mi", }, - "securityContext": map[string]interface{}{ - "allowPrivilegeEscalation": false, + "requests": map[string]interface{}{ + "cpu": "100m", + "memory": "512Mi", }, }, + "securityContext": map[string]interface{}{ + "allowPrivilegeEscalation": false, + }, }, - "dnsPolicy": "ClusterFirst", - "restartPolicy": "Always", - "securityContext": map[string]interface{}{ - "runAsUser": 65532, - }, + }, + "dnsPolicy": "ClusterFirst", + "restartPolicy": "Always", + "securityContext": map[string]interface{}{ + "runAsUser": 65532, }, }, }, }, } - yurtIoTCarrierConfigBytes, err := json.Marshal(yurtIoTCarrierConfig["deploymentTemplate"]) + yurtIoTCarrierConfigBytes, err := json.Marshal(yurtIoTCarrierConfig) if err != nil { return nil, err } @@ -874,7 +873,7 @@ func NewYurtIoTCarrierComponent(platformAdmin *iotv1alpha2.PlatformAdmin) (*conf return nil, err } - yurtIotCarrierComponent.Name = "yurt-iot-carrier" + yurtIotCarrierComponent.Name = util.IotCtrlName yurtIotCarrierComponent.Deployment = &yurtIoTCarrierDeployment.Spec yurtIotCarrierComponent.Service = nil diff --git a/pkg/controller/platformadmin/utils/version.go b/pkg/controller/platformadmin/utils/version.go index ac733ab290e..a1a5557b765 100644 --- a/pkg/controller/platformadmin/utils/version.go +++ b/pkg/controller/platformadmin/utils/version.go @@ -20,6 +20,8 @@ import ( iotv1alpha2 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha2" ) +const IotCtrlName = "yurt-iot-carrier" + func DefaultVersion(platformAdmin *iotv1alpha2.PlatformAdmin) (string, string, error) { version := "v0.2" diff --git a/pkg/yurtiotcarrier/controllers/device_controller.go b/pkg/yurtiotcarrier/controllers/device_controller.go index f41b18e832e..8c8fb771f92 100644 --- a/pkg/yurtiotcarrier/controllers/device_controller.go +++ b/pkg/yurtiotcarrier/controllers/device_controller.go @@ -46,9 +46,9 @@ type DeviceReconciler struct { NodePool string } -//+kubebuilder:rbac:groups=device.openyurt.io,resources=devices,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=device.openyurt.io,resources=devices/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=device.openyurt.io,resources=devices/finalizers,verbs=update +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=devices,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=devices/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=devices/finalizers,verbs=update func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { var d iotv1alpha1.Device diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go b/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go index 6ce5cf7702c..b437a2953e3 100644 --- a/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go +++ b/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go @@ -43,9 +43,9 @@ type DeviceProfileReconciler struct { NodePool string } -//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceprofiles,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceprofiles/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceprofiles/finalizers,verbs=update +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=deviceprofiles,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=deviceprofiles/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=deviceprofiles/finalizers,verbs=update // Reconcile make changes to a deviceprofile object in EdgeX based on it in Kubernetes func (r *DeviceProfileReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_controller.go b/pkg/yurtiotcarrier/controllers/deviceservice_controller.go index f57d2020761..e50b55cd70c 100644 --- a/pkg/yurtiotcarrier/controllers/deviceservice_controller.go +++ b/pkg/yurtiotcarrier/controllers/deviceservice_controller.go @@ -44,9 +44,9 @@ type DeviceServiceReconciler struct { NodePool string } -//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceservices,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceservices/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=device.openyurt.io,resources=deviceservices/finalizers,verbs=update +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=deviceservices,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=deviceservices/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=iot.openyurt.io,resources=deviceservices/finalizers,verbs=update func (r *DeviceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { var ds iotv1alpha1.DeviceService From 9827724bb77958be1f7807c64250ff9748056ba5 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Thu, 13 Jul 2023 22:25:15 +0800 Subject: [PATCH 5/7] rename yurt-device-controller to yurt-iot-dock Signed-off-by: wangxye <1031989637@qq.com> --- .github/workflows/trivy-scan.yml | 2 +- Makefile | 4 +- .../templates/yurt-manager-iot.yaml | 6 +- .../app/core.go | 32 ++--- .../app/options/options.go | 14 +-- .../yurt-iot-dock.go} | 4 +- ...t-iot-carrier => Dockerfile.yurt-iot-dock} | 6 +- ...t-iot-carrier => Dockerfile.yurt-iot-dock} | 8 +- hack/make-rules/build.sh | 2 +- hack/make-rules/image_build.sh | 2 +- hack/make-rules/local-up-openyurt.sh | 2 +- pkg/apis/iot/v1alpha1/device_types.go | 2 +- pkg/apis/iot/v1alpha1/deviceprofile_types.go | 2 +- pkg/apis/iot/v1alpha1/deviceservice_types.go | 2 +- .../platformadmin/platformadmin_controller.go | 118 +----------------- pkg/controller/platformadmin/utils/util.go | 85 +++++++++++++ pkg/controller/platformadmin/utils/version.go | 6 +- .../clients/edgex-foundry/device_client.go | 2 +- .../edgex-foundry/device_client_test.go | 2 +- .../edgex-foundry/deviceprofile_client.go | 2 +- .../deviceprofile_client_test.go | 2 +- .../edgex-foundry/deviceservice_client.go | 2 +- .../deviceservice_client_test.go | 2 +- .../clients/edgex-foundry/edgexobject.go | 0 .../clients/edgex-foundry/util.go | 2 +- .../clients/errors.go | 0 .../clients/interface.go | 0 .../controllers/device_controller.go | 10 +- .../controllers/device_syncer.go | 10 +- .../controllers/deviceprofile_controller.go | 10 +- .../controllers/deviceprofile_syncer.go | 10 +- .../controllers/deviceservice_controller.go | 10 +- .../controllers/deviceservice_syncer.go | 10 +- .../controllers/predicate.go | 2 +- .../controllers/util/conditions.go | 0 .../controllers/util/fieldindexer.go | 0 .../controllers/util/string.go | 0 .../controllers/util/string_test.go | 0 .../controllers/util/tools.go | 2 +- .../controllers/util/tools_test.go | 0 .../controllers/well_known_labels.go | 2 +- 41 files changed, 177 insertions(+), 200 deletions(-) rename cmd/{yurt-iot-carrier => yurt-iot-dock}/app/core.go (84%) rename cmd/{yurt-iot-carrier => yurt-iot-dock}/app/options/options.go (87%) rename cmd/{yurt-iot-carrier/yurt-iot-carrier.go => yurt-iot-dock/yurt-iot-dock.go} (90%) rename hack/dockerfiles/build/{Dockerfile.yurt-iot-carrier => Dockerfile.yurt-iot-dock} (62%) rename hack/dockerfiles/release/{Dockerfile.yurt-iot-carrier => Dockerfile.yurt-iot-dock} (74%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/edgex-foundry/device_client.go (99%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/edgex-foundry/device_client_test.go (99%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/edgex-foundry/deviceprofile_client.go (98%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/edgex-foundry/deviceprofile_client_test.go (99%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/edgex-foundry/deviceservice_client.go (98%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/edgex-foundry/deviceservice_client_test.go (98%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/edgex-foundry/edgexobject.go (100%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/edgex-foundry/util.go (99%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/errors.go (100%) rename pkg/{yurtiotcarrier => yurtiotdock}/clients/interface.go (100%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/device_controller.go (97%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/device_syncer.go (95%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/deviceprofile_controller.go (94%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/deviceprofile_syncer.go (95%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/deviceservice_controller.go (96%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/deviceservice_syncer.go (96%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/predicate.go (94%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/util/conditions.go (100%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/util/fieldindexer.go (100%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/util/string.go (100%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/util/string_test.go (100%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/util/tools.go (98%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/util/tools_test.go (100%) rename pkg/{yurtiotcarrier => yurtiotdock}/controllers/well_known_labels.go (91%) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 26cffbe58b8..31dc32bb5d2 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - target: [ yurthub, node-servant, yurt-manager, yurt-iot-carrier ] + target: [ yurthub, node-servant, yurt-manager, yurt-iot-dock ] steps: - uses: actions/checkout@v3 with: diff --git a/Makefile b/Makefile index 0e8c6cfb6f4..fefb7fe0ca0 100644 --- a/Makefile +++ b/Makefile @@ -171,8 +171,8 @@ docker-push-yurt-tunnel-server: docker-buildx-builder docker-push-yurt-tunnel-agent: docker-buildx-builder docker buildx build --no-cache --push ${DOCKER_BUILD_ARGS} --platform ${TARGET_PLATFORMS} -f hack/dockerfiles/release/Dockerfile.yurt-tunnel-agent . -t ${IMAGE_REPO}/yurt-tunnel-agent:${GIT_VERSION} -docker-push-yurt-iot-carrier: docker-buildx-builder - docker buildx build --no-cache --push ${DOCKER_BUILD_ARGS} --platform ${TARGET_PLATFORMS} -f hack/dockerfiles/release/Dockerfile.yurt-iot-carrier . -t ${IMAGE_REPO}/yurt-iot-carrier:${GIT_VERSION} +docker-push-yurt-iot-dock: docker-buildx-builder + docker buildx build --no-cache --push ${DOCKER_BUILD_ARGS} --platform ${TARGET_PLATFORMS} -f hack/dockerfiles/release/Dockerfile.yurt-iot-dock . -t ${IMAGE_REPO}/yurt-iot-dock:${GIT_VERSION} generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. # hack/make-rule/generate_openapi.sh // TODO by kadisi diff --git a/charts/yurt-manager/templates/yurt-manager-iot.yaml b/charts/yurt-manager/templates/yurt-manager-iot.yaml index a835d4499d1..ec415db5a72 100644 --- a/charts/yurt-manager/templates/yurt-manager-iot.yaml +++ b/charts/yurt-manager/templates/yurt-manager-iot.yaml @@ -1,7 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: yurt-iot-carrier + name: yurt-iot-dock rules: - apiGroups: - "" @@ -77,11 +77,11 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: yurt-iot-carrier + name: yurt-iot-dock roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: yurt-iot-carrier + name: yurt-iot-dock subjects: - apiGroup: rbac.authorization.k8s.io kind: Group diff --git a/cmd/yurt-iot-carrier/app/core.go b/cmd/yurt-iot-dock/app/core.go similarity index 84% rename from cmd/yurt-iot-carrier/app/core.go rename to cmd/yurt-iot-dock/app/core.go index c2df21a37cc..39e30bcaf30 100644 --- a/cmd/yurt-iot-carrier/app/core.go +++ b/cmd/yurt-iot-dock/app/core.go @@ -32,10 +32,10 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/cmd/yurt-iot-dock/app/options" "github.com/openyurtio/openyurt/pkg/apis" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/controllers" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/controllers/util" ) var ( @@ -52,28 +52,28 @@ func init() { // +kubebuilder:scaffold:scheme } -func NewCmdYurtIotCarrier(stopCh <-chan struct{}) *cobra.Command { - yurtDeviceControllerOptions := options.NewYurtIotCarrierOptions() +func NewCmdYurtIoTDock(stopCh <-chan struct{}) *cobra.Command { + yurtIoTDockOptions := options.NewYurtIoTDockOptions() cmd := &cobra.Command{ - Use: "yurt-iot-carrier", - Short: "Launch yurt-iot-carrier", - Long: "Launch yurt-iot-carrier", + Use: "yurt-iot-dock", + Short: "Launch yurt-iot-dock", + Long: "Launch yurt-iot-dock", Run: func(cmd *cobra.Command, args []string) { cmd.Flags().VisitAll(func(flag *pflag.Flag) { klog.V(1).Infof("FLAG: --%s=%q", flag.Name, flag.Value) }) - if err := options.ValidateOptions(yurtDeviceControllerOptions); err != nil { + if err := options.ValidateOptions(yurtIoTDockOptions); err != nil { klog.Fatalf("validate options: %v", err) } - Run(yurtDeviceControllerOptions, stopCh) + Run(yurtIoTDockOptions, stopCh) }, } - yurtDeviceControllerOptions.AddFlags(cmd.Flags()) + yurtIoTDockOptions.AddFlags(cmd.Flags()) return cmd } -func Run(opts *options.YurtIotCarrierOptions, stopCh <-chan struct{}) { +func Run(opts *options.YurtIoTDockOptions, stopCh <-chan struct{}) { ctrl.SetLogger(klogr.New()) cfg := ctrl.GetConfigOrDie() @@ -82,7 +82,7 @@ func Run(opts *options.YurtIotCarrierOptions, stopCh <-chan struct{}) { MetricsBindAddress: opts.MetricsAddr, HealthProbeBindAddress: opts.ProbeAddr, LeaderElection: opts.EnableLeaderElection, - LeaderElectionID: "yurt-iot-carrier", + LeaderElectionID: "yurt-iot-dock", Namespace: opts.Namespace, }) if err != nil { @@ -104,11 +104,11 @@ func Run(opts *options.YurtIotCarrierOptions, stopCh <-chan struct{}) { os.Exit(1) } - // get nodepool where device-controller run + // get nodepool where yurt-iot-dock run if opts.Nodepool == "" { opts.Nodepool, err = util.GetNodePool(mgr.GetConfig()) if err != nil { - setupLog.Error(err, "failed to get the nodepool where device-controller run") + setupLog.Error(err, "failed to get the nodepool where yurt-iot-dock run") os.Exit(1) } } @@ -187,7 +187,7 @@ func Run(opts *options.YurtIotCarrierOptions, stopCh <-chan struct{}) { } } -func preflightCheck(mgr ctrl.Manager, opts *options.YurtIotCarrierOptions) error { +func preflightCheck(mgr ctrl.Manager, opts *options.YurtIoTDockOptions) error { client, err := kubernetes.NewForConfig(mgr.GetConfig()) if err != nil { return err diff --git a/cmd/yurt-iot-carrier/app/options/options.go b/cmd/yurt-iot-dock/app/options/options.go similarity index 87% rename from cmd/yurt-iot-carrier/app/options/options.go rename to cmd/yurt-iot-dock/app/options/options.go index 73bf25115a4..08d9e37a920 100644 --- a/cmd/yurt-iot-carrier/app/options/options.go +++ b/cmd/yurt-iot-dock/app/options/options.go @@ -23,8 +23,8 @@ import ( "github.com/spf13/pflag" ) -// YurtIotCarrierOptions is the main settings for the yurt-iot-carrier -type YurtIotCarrierOptions struct { +// YurtIoTDockOptions is the main settings for the yurt-iot-dock +type YurtIoTDockOptions struct { MetricsAddr string ProbeAddr string EnableLeaderElection bool @@ -36,8 +36,8 @@ type YurtIotCarrierOptions struct { EdgeSyncPeriod uint } -func NewYurtIotCarrierOptions() *YurtIotCarrierOptions { - return &YurtIotCarrierOptions{ +func NewYurtIoTDockOptions() *YurtIoTDockOptions { + return &YurtIoTDockOptions{ MetricsAddr: ":8080", ProbeAddr: ":8080", EnableLeaderElection: false, @@ -50,14 +50,14 @@ func NewYurtIotCarrierOptions() *YurtIotCarrierOptions { } } -func ValidateOptions(options *YurtIotCarrierOptions) error { +func ValidateOptions(options *YurtIoTDockOptions) error { if err := ValidateEdgePlatformAddress(options); err != nil { return err } return nil } -func (o *YurtIotCarrierOptions) AddFlags(fs *pflag.FlagSet) { +func (o *YurtIoTDockOptions) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&o.MetricsAddr, "metrics-bind-address", o.MetricsAddr, "The address the metric endpoint binds to.") fs.StringVar(&o.ProbeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") fs.BoolVar(&o.EnableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+"Enabling this will ensure there is only one active controller manager.") @@ -69,7 +69,7 @@ func (o *YurtIotCarrierOptions) AddFlags(fs *pflag.FlagSet) { fs.UintVar(&o.EdgeSyncPeriod, "edge-sync-period", 5, "The period of the device management platform synchronizing the device status to the cloud.(in seconds,not less than 5 seconds)") } -func ValidateEdgePlatformAddress(options *YurtIotCarrierOptions) error { +func ValidateEdgePlatformAddress(options *YurtIoTDockOptions) error { addrs := []string{options.CoreDataAddr, options.CoreMetadataAddr, options.CoreCommandAddr} for _, addr := range addrs { if addr != "" { diff --git a/cmd/yurt-iot-carrier/yurt-iot-carrier.go b/cmd/yurt-iot-dock/yurt-iot-dock.go similarity index 90% rename from cmd/yurt-iot-carrier/yurt-iot-carrier.go rename to cmd/yurt-iot-dock/yurt-iot-dock.go index 0a0696515fa..c647e5241c0 100644 --- a/cmd/yurt-iot-carrier/yurt-iot-carrier.go +++ b/cmd/yurt-iot-dock/yurt-iot-dock.go @@ -27,7 +27,7 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "k8s.io/klog/v2" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app" + "github.com/openyurtio/openyurt/cmd/yurt-iot-dock/app" ) func main() { @@ -35,7 +35,7 @@ func main() { klog.InitFlags(nil) defer klog.Flush() - cmd := app.NewCmdYurtIotCarrier(wait.NeverStop) + cmd := app.NewCmdYurtIoTDock(wait.NeverStop) cmd.Flags().AddGoFlagSet(flag.CommandLine) if err := cmd.Execute(); err != nil { panic(err) diff --git a/hack/dockerfiles/build/Dockerfile.yurt-iot-carrier b/hack/dockerfiles/build/Dockerfile.yurt-iot-dock similarity index 62% rename from hack/dockerfiles/build/Dockerfile.yurt-iot-carrier rename to hack/dockerfiles/build/Dockerfile.yurt-iot-dock index 340eb4f3492..3e684bb6866 100644 --- a/hack/dockerfiles/build/Dockerfile.yurt-iot-carrier +++ b/hack/dockerfiles/build/Dockerfile.yurt-iot-dock @@ -1,8 +1,8 @@ -# multi-arch image building for yurt-iot-carrier +# multi-arch image building for yurt-iot-dock FROM --platform=${TARGETPLATFORM} alpine:3.17 ARG TARGETOS TARGETARCH MIRROR_REPO RUN if [ ! -z "${MIRROR_REPO+x}" ]; then sed -i "s/dl-cdn.alpinelinux.org/${MIRROR_REPO}/g" /etc/apk/repositories; fi && \ apk add ca-certificates bash libc6-compat iptables ip6tables && update-ca-certificates && rm /var/cache/apk/* -COPY ./_output/local/bin/${TARGETOS}/${TARGETARCH}/yurt-iot-carrier /usr/local/bin/yurt-iot-carrier -ENTRYPOINT ["/usr/local/bin/yurt-iot-carrier"] \ No newline at end of file +COPY ./_output/local/bin/${TARGETOS}/${TARGETARCH}/yurt-iot-dock /usr/local/bin/yurt-iot-dock +ENTRYPOINT ["/usr/local/bin/yurt-iot-dock"] \ No newline at end of file diff --git a/hack/dockerfiles/release/Dockerfile.yurt-iot-carrier b/hack/dockerfiles/release/Dockerfile.yurt-iot-dock similarity index 74% rename from hack/dockerfiles/release/Dockerfile.yurt-iot-carrier rename to hack/dockerfiles/release/Dockerfile.yurt-iot-dock index 56ad8303a80..d73a35cb790 100644 --- a/hack/dockerfiles/release/Dockerfile.yurt-iot-carrier +++ b/hack/dockerfiles/release/Dockerfile.yurt-iot-dock @@ -1,14 +1,14 @@ -# multi-arch image building for yurt-iot-carrier +# multi-arch image building for yurt-iot-dock FROM --platform=${BUILDPLATFORM} golang:1.18 as builder ADD . /build ARG TARGETOS TARGETARCH GIT_VERSION GOPROXY MIRROR_REPO WORKDIR /build/ -RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} GIT_VERSION=${GIT_VERSION} make build WHAT=cmd/yurt-iot-carrier +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} GIT_VERSION=${GIT_VERSION} make build WHAT=cmd/yurt-iot-dock FROM --platform=${TARGETPLATFORM} alpine:3.17 ARG TARGETOS TARGETARCH MIRROR_REPO RUN if [ ! -z "${MIRROR_REPO+x}" ]; then sed -i "s/dl-cdn.alpinelinux.org/${MIRROR_REPO}/g" /etc/apk/repositories; fi && \ apk add ca-certificates bash libc6-compat iptables ip6tables && update-ca-certificates && rm /var/cache/apk/* -COPY --from=builder /build/_output/local/bin/${TARGETOS}/${TARGETARCH}/yurt-iot-carrier /usr/local/bin/yurt-iot-carrier -ENTRYPOINT ["/usr/local/bin/yurt-iot-carrier"] \ No newline at end of file +COPY --from=builder /build/_output/local/bin/${TARGETOS}/${TARGETARCH}/yurt-iot-dock /usr/local/bin/yurt-iot-dock +ENTRYPOINT ["/usr/local/bin/yurt-iot-dock"] \ No newline at end of file diff --git a/hack/make-rules/build.sh b/hack/make-rules/build.sh index 10fd803d2b3..5e28026b658 100755 --- a/hack/make-rules/build.sh +++ b/hack/make-rules/build.sh @@ -25,7 +25,7 @@ readonly YURT_ALL_TARGETS=( yurt-node-servant yurthub yurt-manager - yurt-iot-carrier + yurt-iot-dock ) # clean old binaries at GOOS and GOARCH diff --git a/hack/make-rules/image_build.sh b/hack/make-rules/image_build.sh index c94e06464a5..1f4afa71d07 100755 --- a/hack/make-rules/image_build.sh +++ b/hack/make-rules/image_build.sh @@ -25,7 +25,7 @@ readonly IMAGE_TARGETS=( yurt-node-servant yurthub yurt-manager - yurt-iot-carrier + yurt-iot-dock ) http_proxy=${http_proxy:-} diff --git a/hack/make-rules/local-up-openyurt.sh b/hack/make-rules/local-up-openyurt.sh index 73a4d29e77e..88c709b0b4f 100755 --- a/hack/make-rules/local-up-openyurt.sh +++ b/hack/make-rules/local-up-openyurt.sh @@ -56,7 +56,7 @@ readonly REQUIRED_IMAGES=( openyurt/node-servant openyurt/yurt-manager openyurt/yurthub - openyurt/yurt-iot-carrier + openyurt/yurt-iot-dock ) readonly LOCAL_ARCH=$(go env GOHOSTARCH) diff --git a/pkg/apis/iot/v1alpha1/device_types.go b/pkg/apis/iot/v1alpha1/device_types.go index ea7c4c58158..9a1bafd257f 100644 --- a/pkg/apis/iot/v1alpha1/device_types.go +++ b/pkg/apis/iot/v1alpha1/device_types.go @@ -22,7 +22,7 @@ import ( ) const ( - DeviceFinalizer = "iot.device.finalizer" + DeviceFinalizer = "iot.openyurt.io/device" ) // DeviceConditionType indicates valid conditions type of a Device. diff --git a/pkg/apis/iot/v1alpha1/deviceprofile_types.go b/pkg/apis/iot/v1alpha1/deviceprofile_types.go index a35be26a1ad..2d25605bea9 100644 --- a/pkg/apis/iot/v1alpha1/deviceprofile_types.go +++ b/pkg/apis/iot/v1alpha1/deviceprofile_types.go @@ -21,7 +21,7 @@ import ( ) const ( - DeviceProfileFinalizer = "iot.deviceProfile.finalizer" + DeviceProfileFinalizer = "iot.openyurt.io/deviceprofile" ) type DeviceResource struct { diff --git a/pkg/apis/iot/v1alpha1/deviceservice_types.go b/pkg/apis/iot/v1alpha1/deviceservice_types.go index 3dce06f1a75..4720b4e46bf 100644 --- a/pkg/apis/iot/v1alpha1/deviceservice_types.go +++ b/pkg/apis/iot/v1alpha1/deviceservice_types.go @@ -22,7 +22,7 @@ import ( ) const ( - DeviceServiceFinalizer = "iot.deviceService.finalizer" + DeviceServiceFinalizer = "iot.openyurt.io/deviceservice" ) // DeviceServiceConditionType indicates valid conditions type of a Device Service. diff --git a/pkg/controller/platformadmin/platformadmin_controller.go b/pkg/controller/platformadmin/platformadmin_controller.go index eb113785491..d2c684b9b32 100644 --- a/pkg/controller/platformadmin/platformadmin_controller.go +++ b/pkg/controller/platformadmin/platformadmin_controller.go @@ -257,12 +257,12 @@ func (r *ReconcilePlatformAdmin) reconcileDelete(ctx context.Context, platformAd } desiredComponents = append(desiredComponents, additionalComponents...) - yurtIotCarrier, err := NewYurtIoTCarrierComponent(platformAdmin) + yurtIotDock, err := util.NewYurtIoTDockComponent(platformAdmin) if err != nil { - klog.Errorf(Format("yurtIoTCarrierComponent error %v", err)) + klog.Errorf(Format("yurtIoTDockComponent error %v", err)) return reconcile.Result{}, err } - desiredComponents = append(desiredComponents, yurtIotCarrier) + desiredComponents = append(desiredComponents, yurtIotDock) //TODO: handle PlatformAdmin.Spec.Components for _, dc := range desiredComponents { @@ -392,11 +392,11 @@ func (r *ReconcilePlatformAdmin) reconcileComponent(ctx context.Context, platfor } desireComponents = append(desireComponents, additionalComponents...) - yurtIotCarrier, err := NewYurtIoTCarrierComponent(platformAdmin) + yurtIotDock, err := util.NewYurtIoTDockComponent(platformAdmin) if err != nil { return false, err } - desireComponents = append(desireComponents, yurtIotCarrier) + desireComponents = append(desireComponents, yurtIotDock) //TODO: handle PlatformAdmin.Spec.Components defer func() { @@ -771,111 +771,3 @@ func (r *ReconcilePlatformAdmin) initFramework(ctx context.Context, platformAdmi } return nil } - -func NewYurtIoTCarrierComponent(platformAdmin *iotv1alpha2.PlatformAdmin) (*config.Component, error) { - var yurtIotCarrierComponent config.Component - var yurtIoTCarrierDeployment iotv1alpha1.DeploymentTemplateSpec - ver, ns, err := util.DefaultVersion(platformAdmin) - if err != nil { - return nil, err - } - // YurtIoTCarrier doesn't need a service yet - yurtIoTCarrierConfig := map[string]interface{}{ - "metadata": map[string]interface{}{ - "creationTimestamp": nil, - "labels": map[string]interface{}{ - "app": "yurt-iot-carrier", - }, - "namespace": ns, - }, - "spec": map[string]interface{}{ - "selector": map[string]interface{}{ - "matchLabels": map[string]interface{}{ - "app": "yurt-iot-carrier", - }, - }, - "strategy": map[string]interface{}{}, - "template": map[string]interface{}{ - "metadata": map[string]interface{}{ - "creationTimestamp": nil, - "labels": map[string]interface{}{ - "app": "yurt-iot-carrier", - "control-plane": "edgex-controller-manager", - }, - }, - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "args": []interface{}{ - "--health-probe-bind-address=:8081", - "--metrics-bind-address=127.0.0.1:8080", - "--leader-elect=false", - fmt.Sprintf("--namespace=%s", ns), - }, - "image": fmt.Sprintf("leoabyss/yurt-iot-carrier:%s", ver), - "imagePullPolicy": "IfNotPresent", - "livenessProbe": map[string]interface{}{ - "failureThreshold": 3, - "httpGet": map[string]interface{}{ - "path": "/healthz", - "port": 8081, - "scheme": "HTTP", - }, - "initialDelaySeconds": 15, - "periodSeconds": 20, - "successThreshold": 1, - "timeoutSeconds": 1, - }, - "name": "yurt-iot-carrier", - "readinessProbe": map[string]interface{}{ - "failureThreshold": 3, - "httpGet": map[string]interface{}{ - "path": "/readyz", - "port": 8081, - "scheme": "HTTP", - }, - "initialDelaySeconds": 5, - "periodSeconds": 10, - "successThreshold": 1, - "timeoutSeconds": 1, - }, - "resources": map[string]interface{}{ - "limits": map[string]interface{}{ - "cpu": "100m", - "memory": "512Mi", - }, - "requests": map[string]interface{}{ - "cpu": "100m", - "memory": "512Mi", - }, - }, - "securityContext": map[string]interface{}{ - "allowPrivilegeEscalation": false, - }, - }, - }, - "dnsPolicy": "ClusterFirst", - "restartPolicy": "Always", - "securityContext": map[string]interface{}{ - "runAsUser": 65532, - }, - }, - }, - }, - } - yurtIoTCarrierConfigBytes, err := json.Marshal(yurtIoTCarrierConfig) - if err != nil { - return nil, err - } - - err = json.Unmarshal([]byte(yurtIoTCarrierConfigBytes), &yurtIoTCarrierDeployment) - if err != nil { - return nil, err - } - - yurtIotCarrierComponent.Name = util.IotCtrlName - yurtIotCarrierComponent.Deployment = &yurtIoTCarrierDeployment.Spec - yurtIotCarrierComponent.Service = nil - - return &yurtIotCarrierComponent, nil -} diff --git a/pkg/controller/platformadmin/utils/util.go b/pkg/controller/platformadmin/utils/util.go index eb9deb1ae69..e1f74e957dc 100644 --- a/pkg/controller/platformadmin/utils/util.go +++ b/pkg/controller/platformadmin/utils/util.go @@ -17,10 +17,17 @@ limitations under the License. package util import ( + "fmt" + + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/pointer" iotv1alpha2 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha2" + "github.com/openyurtio/openyurt/pkg/controller/platformadmin/config" ) // NewPlatformAdminCondition creates a new PlatformAdmin condition. @@ -70,3 +77,81 @@ func filterOutCondition(conditions []iotv1alpha2.PlatformAdminCondition, condTyp } return newConditions } + +func NewYurtIoTDockComponent(platformAdmin *iotv1alpha2.PlatformAdmin) (*config.Component, error) { + var yurtIotDockComponent config.Component + + ver, ns, err := defaultVersion(platformAdmin) + if err != nil { + return nil, err + } + + yurtIotDockComponent.Name = IotDockName + yurtIotDockComponent.Deployment = &appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "yurt-iot-dock", + "control-plane": "edgex-controller-manager", + }, + Namespace: ns, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "yurt-iot-dock", + Image: fmt.Sprintf("leoabyss/yurt-iot-dock:%s", ver), + ImagePullPolicy: corev1.PullIfNotPresent, + Args: []string{ + "--health-probe-bind-address=:8081", + "--metrics-bind-address=127.0.0.1:8080", + "--leader-elect=false", + fmt.Sprintf("--namespace=%s", ns), + }, + LivenessProbe: &corev1.Probe{ + InitialDelaySeconds: 15, + PeriodSeconds: 20, + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt(8081), + }, + }, + }, + ReadinessProbe: &corev1.Probe{ + InitialDelaySeconds: 5, + PeriodSeconds: 10, + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/readyz", + Port: intstr.FromInt(8081), + }, + }, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("512m"), + corev1.ResourceMemory: resource.MustParse("256Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1024m"), + corev1.ResourceMemory: resource.MustParse("512Mi"), + }, + }, + SecurityContext: &corev1.SecurityContext{ + AllowPrivilegeEscalation: pointer.Bool(false), + }, + }, + }, + TerminationGracePeriodSeconds: pointer.Int64(10), + SecurityContext: &corev1.PodSecurityContext{ + RunAsUser: pointer.Int64(65532), + }, + }, + }, + } + // YurtIoTDock doesn't need a service yet + yurtIotDockComponent.Service = nil + + return &yurtIotDockComponent, nil +} diff --git a/pkg/controller/platformadmin/utils/version.go b/pkg/controller/platformadmin/utils/version.go index a1a5557b765..f3651b37f9d 100644 --- a/pkg/controller/platformadmin/utils/version.go +++ b/pkg/controller/platformadmin/utils/version.go @@ -20,11 +20,11 @@ import ( iotv1alpha2 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha2" ) -const IotCtrlName = "yurt-iot-carrier" +const IotDockName = "yurt-iot-dock" -func DefaultVersion(platformAdmin *iotv1alpha2.PlatformAdmin) (string, string, error) { +func defaultVersion(platformAdmin *iotv1alpha2.PlatformAdmin) (string, string, error) { - version := "v0.2" + version := "v0.3" ns := platformAdmin.Namespace return version, ns, nil diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go b/pkg/yurtiotdock/clients/edgex-foundry/device_client.go similarity index 99% rename from pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go rename to pkg/yurtiotdock/clients/edgex-foundry/device_client.go index 699d5cbf7b1..ec40d90ceca 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/device_client.go @@ -34,7 +34,7 @@ import ( "k8s.io/klog/v2" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" ) type EdgexDeviceClient struct { diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go b/pkg/yurtiotdock/clients/edgex-foundry/device_client_test.go similarity index 99% rename from pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go rename to pkg/yurtiotdock/clients/edgex-foundry/device_client_test.go index 81ecab164d1..bc89aa83a2d 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/device_client_test.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/device_client_test.go @@ -25,7 +25,7 @@ import ( "github.com/stretchr/testify/assert" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" ) const ( diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go b/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client.go similarity index 98% rename from pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go rename to pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client.go index fced1283b9c..4e86d97ef77 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client.go @@ -28,7 +28,7 @@ import ( "k8s.io/klog/v2" "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - devcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + devcli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" ) type EdgexDeviceProfile struct { diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go b/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client_test.go similarity index 99% rename from pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go rename to pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client_test.go index 43fdf7648db..15fba4e91de 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceprofile_client_test.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client_test.go @@ -24,7 +24,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" ) const ( diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go b/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client.go similarity index 98% rename from pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go rename to pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client.go index bb4f4d68d56..5fc2b4ad31f 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client.go @@ -28,7 +28,7 @@ import ( "k8s.io/klog/v2" "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" ) type EdgexDeviceServiceClient struct { diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go b/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client_test.go similarity index 98% rename from pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go rename to pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client_test.go index d50d3159885..81d8e6f071f 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/deviceservice_client_test.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client_test.go @@ -24,7 +24,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" ) const ( diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/edgexobject.go b/pkg/yurtiotdock/clients/edgex-foundry/edgexobject.go similarity index 100% rename from pkg/yurtiotcarrier/clients/edgex-foundry/edgexobject.go rename to pkg/yurtiotdock/clients/edgex-foundry/edgexobject.go diff --git a/pkg/yurtiotcarrier/clients/edgex-foundry/util.go b/pkg/yurtiotdock/clients/edgex-foundry/util.go similarity index 99% rename from pkg/yurtiotcarrier/clients/edgex-foundry/util.go rename to pkg/yurtiotdock/clients/edgex-foundry/util.go index 63eed91ecbd..ce685c8a645 100644 --- a/pkg/yurtiotcarrier/clients/edgex-foundry/util.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/util.go @@ -30,7 +30,7 @@ import ( ) const ( - EdgeXObjectName = "device-controller/edgex-object.name" + EdgeXObjectName = "yurt-iot-dock/edgex-object.name" DeviceServicePath = "/api/v2/deviceservice" DeviceProfilePath = "/api/v2/deviceprofile" DevicePath = "/api/v2/device" diff --git a/pkg/yurtiotcarrier/clients/errors.go b/pkg/yurtiotdock/clients/errors.go similarity index 100% rename from pkg/yurtiotcarrier/clients/errors.go rename to pkg/yurtiotdock/clients/errors.go diff --git a/pkg/yurtiotcarrier/clients/interface.go b/pkg/yurtiotdock/clients/interface.go similarity index 100% rename from pkg/yurtiotcarrier/clients/interface.go rename to pkg/yurtiotdock/clients/interface.go diff --git a/pkg/yurtiotcarrier/controllers/device_controller.go b/pkg/yurtiotdock/controllers/device_controller.go similarity index 97% rename from pkg/yurtiotcarrier/controllers/device_controller.go rename to pkg/yurtiotdock/controllers/device_controller.go index 8c8fb771f92..d817bd99b56 100644 --- a/pkg/yurtiotcarrier/controllers/device_controller.go +++ b/pkg/yurtiotdock/controllers/device_controller.go @@ -30,11 +30,11 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/cmd/yurt-iot-dock/app/options" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - util "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients/edgex-foundry" + util "github.com/openyurtio/openyurt/pkg/yurtiotdock/controllers/util" ) // DeviceReconciler reconciles a Device object @@ -107,7 +107,7 @@ func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } // SetupWithManager sets up the controller with the Manager. -func (r *DeviceReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIotCarrierOptions) error { +func (r *DeviceReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIoTDockOptions) error { r.deviceCli = edgexCli.NewEdgexDeviceClient(opts.CoreMetadataAddr, opts.CoreCommandAddr) r.NodePool = opts.Nodepool diff --git a/pkg/yurtiotcarrier/controllers/device_syncer.go b/pkg/yurtiotdock/controllers/device_syncer.go similarity index 95% rename from pkg/yurtiotcarrier/controllers/device_syncer.go rename to pkg/yurtiotdock/controllers/device_syncer.go index 82ee3bbfadd..ea962d42f55 100644 --- a/pkg/yurtiotcarrier/controllers/device_syncer.go +++ b/pkg/yurtiotdock/controllers/device_syncer.go @@ -26,11 +26,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/cmd/yurt-iot-dock/app/options" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - efCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + edgeCli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" + efCli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients/edgex-foundry" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/controllers/util" ) type DeviceSyncer struct { @@ -46,7 +46,7 @@ type DeviceSyncer struct { } // NewDeviceSyncer initialize a New DeviceSyncer -func NewDeviceSyncer(client client.Client, opts *options.YurtIotCarrierOptions) (DeviceSyncer, error) { +func NewDeviceSyncer(client client.Client, opts *options.YurtIoTDockOptions) (DeviceSyncer, error) { return DeviceSyncer{ syncPeriod: time.Duration(opts.EdgeSyncPeriod) * time.Second, deviceCli: efCli.NewEdgexDeviceClient(opts.CoreMetadataAddr, opts.CoreCommandAddr), diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go b/pkg/yurtiotdock/controllers/deviceprofile_controller.go similarity index 94% rename from pkg/yurtiotcarrier/controllers/deviceprofile_controller.go rename to pkg/yurtiotdock/controllers/deviceprofile_controller.go index b437a2953e3..c8d85501967 100644 --- a/pkg/yurtiotcarrier/controllers/deviceprofile_controller.go +++ b/pkg/yurtiotdock/controllers/deviceprofile_controller.go @@ -28,11 +28,11 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/cmd/yurt-iot-dock/app/options" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" + edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients/edgex-foundry" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/controllers/util" ) // DeviceProfileReconciler reconciles a DeviceProfile object @@ -85,7 +85,7 @@ func (r *DeviceProfileReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // SetupWithManager sets up the controller with the Manager. -func (r *DeviceProfileReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIotCarrierOptions) error { +func (r *DeviceProfileReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIoTDockOptions) error { r.edgeClient = edgexclis.NewEdgexDeviceProfile(opts.CoreMetadataAddr) r.NodePool = opts.Nodepool diff --git a/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go b/pkg/yurtiotdock/controllers/deviceprofile_syncer.go similarity index 95% rename from pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go rename to pkg/yurtiotdock/controllers/deviceprofile_syncer.go index e5383f397da..a663a1a417e 100644 --- a/pkg/yurtiotcarrier/controllers/deviceprofile_syncer.go +++ b/pkg/yurtiotdock/controllers/deviceprofile_syncer.go @@ -26,11 +26,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/cmd/yurt-iot-dock/app/options" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - devcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + devcli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" + edgexclis "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients/edgex-foundry" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/controllers/util" ) type DeviceProfileSyncer struct { @@ -45,7 +45,7 @@ type DeviceProfileSyncer struct { } // NewDeviceProfileSyncer initialize a New DeviceProfileSyncer -func NewDeviceProfileSyncer(client client.Client, opts *options.YurtIotCarrierOptions) (DeviceProfileSyncer, error) { +func NewDeviceProfileSyncer(client client.Client, opts *options.YurtIoTDockOptions) (DeviceProfileSyncer, error) { return DeviceProfileSyncer{ syncPeriod: time.Duration(opts.EdgeSyncPeriod) * time.Second, edgeClient: edgexclis.NewEdgexDeviceProfile(opts.CoreMetadataAddr), diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_controller.go b/pkg/yurtiotdock/controllers/deviceservice_controller.go similarity index 96% rename from pkg/yurtiotcarrier/controllers/deviceservice_controller.go rename to pkg/yurtiotdock/controllers/deviceservice_controller.go index e50b55cd70c..7afa7033df7 100644 --- a/pkg/yurtiotcarrier/controllers/deviceservice_controller.go +++ b/pkg/yurtiotdock/controllers/deviceservice_controller.go @@ -29,11 +29,11 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/cmd/yurt-iot-dock/app/options" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - util "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients/edgex-foundry" + util "github.com/openyurtio/openyurt/pkg/yurtiotdock/controllers/util" ) // DeviceServiceReconciler reconciles a DeviceService object @@ -105,7 +105,7 @@ func (r *DeviceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // SetupWithManager sets up the controller with the Manager. -func (r *DeviceServiceReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIotCarrierOptions) error { +func (r *DeviceServiceReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIoTDockOptions) error { r.deviceServiceCli = edgexCli.NewEdgexDeviceServiceClient(opts.CoreMetadataAddr) r.NodePool = opts.Nodepool diff --git a/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go b/pkg/yurtiotdock/controllers/deviceservice_syncer.go similarity index 96% rename from pkg/yurtiotcarrier/controllers/deviceservice_syncer.go rename to pkg/yurtiotdock/controllers/deviceservice_syncer.go index a5699de97d6..8b07c09c670 100644 --- a/pkg/yurtiotcarrier/controllers/deviceservice_syncer.go +++ b/pkg/yurtiotdock/controllers/deviceservice_syncer.go @@ -25,11 +25,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ctrlmgr "sigs.k8s.io/controller-runtime/pkg/manager" - "github.com/openyurtio/openyurt/cmd/yurt-iot-carrier/app/options" + "github.com/openyurtio/openyurt/cmd/yurt-iot-dock/app/options" iotv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha1" - iotcli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients" - edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" - "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/controllers/util" + iotcli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients" + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients/edgex-foundry" + "github.com/openyurtio/openyurt/pkg/yurtiotdock/controllers/util" ) type DeviceServiceSyncer struct { @@ -42,7 +42,7 @@ type DeviceServiceSyncer struct { Namespace string } -func NewDeviceServiceSyncer(client client.Client, opts *options.YurtIotCarrierOptions) (DeviceServiceSyncer, error) { +func NewDeviceServiceSyncer(client client.Client, opts *options.YurtIoTDockOptions) (DeviceServiceSyncer, error) { return DeviceServiceSyncer{ syncPeriod: time.Duration(opts.EdgeSyncPeriod) * time.Second, deviceServiceCli: edgexCli.NewEdgexDeviceServiceClient(opts.CoreMetadataAddr), diff --git a/pkg/yurtiotcarrier/controllers/predicate.go b/pkg/yurtiotdock/controllers/predicate.go similarity index 94% rename from pkg/yurtiotcarrier/controllers/predicate.go rename to pkg/yurtiotdock/controllers/predicate.go index 5078421e182..0c6dc5eadb5 100644 --- a/pkg/yurtiotcarrier/controllers/predicate.go +++ b/pkg/yurtiotdock/controllers/predicate.go @@ -21,7 +21,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" - edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotcarrier/clients/edgex-foundry" + edgexCli "github.com/openyurtio/openyurt/pkg/yurtiotdock/clients/edgex-foundry" ) func genFirstUpdateFilter(objKind string) predicate.Predicate { diff --git a/pkg/yurtiotcarrier/controllers/util/conditions.go b/pkg/yurtiotdock/controllers/util/conditions.go similarity index 100% rename from pkg/yurtiotcarrier/controllers/util/conditions.go rename to pkg/yurtiotdock/controllers/util/conditions.go diff --git a/pkg/yurtiotcarrier/controllers/util/fieldindexer.go b/pkg/yurtiotdock/controllers/util/fieldindexer.go similarity index 100% rename from pkg/yurtiotcarrier/controllers/util/fieldindexer.go rename to pkg/yurtiotdock/controllers/util/fieldindexer.go diff --git a/pkg/yurtiotcarrier/controllers/util/string.go b/pkg/yurtiotdock/controllers/util/string.go similarity index 100% rename from pkg/yurtiotcarrier/controllers/util/string.go rename to pkg/yurtiotdock/controllers/util/string.go diff --git a/pkg/yurtiotcarrier/controllers/util/string_test.go b/pkg/yurtiotdock/controllers/util/string_test.go similarity index 100% rename from pkg/yurtiotcarrier/controllers/util/string_test.go rename to pkg/yurtiotdock/controllers/util/string_test.go diff --git a/pkg/yurtiotcarrier/controllers/util/tools.go b/pkg/yurtiotdock/controllers/util/tools.go similarity index 98% rename from pkg/yurtiotcarrier/controllers/util/tools.go rename to pkg/yurtiotdock/controllers/util/tools.go index 7f1a8d6fd30..a95dc50395a 100644 --- a/pkg/yurtiotcarrier/controllers/util/tools.go +++ b/pkg/yurtiotdock/controllers/util/tools.go @@ -34,7 +34,7 @@ const ( PODNAMESPACE = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" ) -// GetNodePool get nodepool where device-controller run +// GetNodePool get nodepool where yurt-iot-dock run func GetNodePool(cfg *rest.Config) (string, error) { var nodePool string client, err := kubernetes.NewForConfig(cfg) diff --git a/pkg/yurtiotcarrier/controllers/util/tools_test.go b/pkg/yurtiotdock/controllers/util/tools_test.go similarity index 100% rename from pkg/yurtiotcarrier/controllers/util/tools_test.go rename to pkg/yurtiotdock/controllers/util/tools_test.go diff --git a/pkg/yurtiotcarrier/controllers/well_known_labels.go b/pkg/yurtiotdock/controllers/well_known_labels.go similarity index 91% rename from pkg/yurtiotcarrier/controllers/well_known_labels.go rename to pkg/yurtiotdock/controllers/well_known_labels.go index 1b0b9e5445a..fd414f13c9e 100644 --- a/pkg/yurtiotcarrier/controllers/well_known_labels.go +++ b/pkg/yurtiotdock/controllers/well_known_labels.go @@ -17,5 +17,5 @@ limitations under the License. package controllers const ( - EdgeXObjectName = "device-controller/edgex-object.name" + EdgeXObjectName = "yurt-iot-dock/edgex-object.name" ) From 1c5015837f4970ad97d3b02dda39a227d9cb529f Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Mon, 17 Jul 2023 20:31:13 +0800 Subject: [PATCH 6/7] extend yurt-iot-dock to sync it with the configuration in PlatformAdmin Signed-off-by: wangxye <1031989637@qq.com> --- .../platformadmin/platformadmin_controller.go | 17 ++--- .../platformadmin/{utils => }/util.go | 69 +++++------------- .../platformadmin/utils/conditions.go | 72 +++++++++++++++++++ pkg/controller/platformadmin/utils/version.go | 4 +- 4 files changed, 98 insertions(+), 64 deletions(-) rename pkg/controller/platformadmin/{utils => }/util.go (58%) create mode 100644 pkg/controller/platformadmin/utils/conditions.go diff --git a/pkg/controller/platformadmin/platformadmin_controller.go b/pkg/controller/platformadmin/platformadmin_controller.go index d2c684b9b32..dddb4178a9e 100644 --- a/pkg/controller/platformadmin/platformadmin_controller.go +++ b/pkg/controller/platformadmin/platformadmin_controller.go @@ -257,12 +257,6 @@ func (r *ReconcilePlatformAdmin) reconcileDelete(ctx context.Context, platformAd } desiredComponents = append(desiredComponents, additionalComponents...) - yurtIotDock, err := util.NewYurtIoTDockComponent(platformAdmin) - if err != nil { - klog.Errorf(Format("yurtIoTDockComponent error %v", err)) - return reconcile.Result{}, err - } - desiredComponents = append(desiredComponents, yurtIotDock) //TODO: handle PlatformAdmin.Spec.Components for _, dc := range desiredComponents { @@ -392,11 +386,6 @@ func (r *ReconcilePlatformAdmin) reconcileComponent(ctx context.Context, platfor } desireComponents = append(desireComponents, additionalComponents...) - yurtIotDock, err := util.NewYurtIoTDockComponent(platformAdmin) - if err != nil { - return false, err - } - desireComponents = append(desireComponents, yurtIotDock) //TODO: handle PlatformAdmin.Spec.Components defer func() { @@ -740,6 +729,12 @@ func (r *ReconcilePlatformAdmin) initFramework(ctx context.Context, platformAdmi platformAdminFramework.Components = r.Configration.NoSectyComponents[platformAdmin.Spec.Version] } + yurtIotDock, err := NewYurtIoTDockComponent(platformAdmin, platformAdminFramework) + if err != nil { + return err + } + platformAdminFramework.Components = append(platformAdminFramework.Components, yurtIotDock) + // For better serialization, the serialization method of the Kubernetes runtime library is used data, err := runtime.Encode(r.yamlSerializer, platformAdminFramework) if err != nil { diff --git a/pkg/controller/platformadmin/utils/util.go b/pkg/controller/platformadmin/util.go similarity index 58% rename from pkg/controller/platformadmin/utils/util.go rename to pkg/controller/platformadmin/util.go index e1f74e957dc..5071df727b7 100644 --- a/pkg/controller/platformadmin/utils/util.go +++ b/pkg/controller/platformadmin/util.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package platformadmin import ( "fmt" @@ -28,71 +28,40 @@ import ( iotv1alpha2 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha2" "github.com/openyurtio/openyurt/pkg/controller/platformadmin/config" + utils "github.com/openyurtio/openyurt/pkg/controller/platformadmin/utils" ) -// NewPlatformAdminCondition creates a new PlatformAdmin condition. -func NewPlatformAdminCondition(condType iotv1alpha2.PlatformAdminConditionType, status corev1.ConditionStatus, reason, message string) *iotv1alpha2.PlatformAdminCondition { - return &iotv1alpha2.PlatformAdminCondition{ - Type: condType, - Status: status, - LastTransitionTime: metav1.Now(), - Reason: reason, - Message: message, - } -} - -// GetPlatformAdminCondition returns the condition with the provided type. -func GetPlatformAdminCondition(status iotv1alpha2.PlatformAdminStatus, condType iotv1alpha2.PlatformAdminConditionType) *iotv1alpha2.PlatformAdminCondition { - for i := range status.Conditions { - c := status.Conditions[i] - if c.Type == condType { - return &c - } - } - return nil -} - -// SetPlatformAdminCondition updates the PlatformAdmin to include the provided condition. If the condition that -// we are about to add already exists and has the same status, reason and message then we are not going to update. -func SetPlatformAdminCondition(status *iotv1alpha2.PlatformAdminStatus, condition *iotv1alpha2.PlatformAdminCondition) { - currentCond := GetPlatformAdminCondition(*status, condition.Type) - if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { - return - } - - if currentCond != nil && currentCond.Status == condition.Status { - condition.LastTransitionTime = currentCond.LastTransitionTime - } - newConditions := filterOutCondition(status.Conditions, condition.Type) - status.Conditions = append(newConditions, *condition) -} +// NewYurtIoTDockComponent initialize the configuration of yurt-iot-dock component +func NewYurtIoTDockComponent(platformAdmin *iotv1alpha2.PlatformAdmin, platformAdminFramework *PlatformAdminFramework) (*config.Component, error) { + var yurtIotDockComponent config.Component -func filterOutCondition(conditions []iotv1alpha2.PlatformAdminCondition, condType iotv1alpha2.PlatformAdminConditionType) []iotv1alpha2.PlatformAdminCondition { - var newConditions []iotv1alpha2.PlatformAdminCondition - for _, c := range conditions { - if c.Type == condType { + // If the configuration of the yurt-iot-dock component that customized in the platformAdminFramework + for _, cp := range platformAdminFramework.Components { + if cp.Name != utils.IotDockName { continue } - newConditions = append(newConditions, c) + return cp, nil } - return newConditions -} -func NewYurtIoTDockComponent(platformAdmin *iotv1alpha2.PlatformAdmin) (*config.Component, error) { - var yurtIotDockComponent config.Component - - ver, ns, err := defaultVersion(platformAdmin) + // Otherwise, the default configuration is used to start + ver, ns, err := utils.DefaultVersion(platformAdmin) if err != nil { return nil, err } - yurtIotDockComponent.Name = IotDockName + yurtIotDockComponent.Name = utils.IotDockName yurtIotDockComponent.Deployment = &appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "yurt-iot-dock", + "control-plane": "platformadmin-controller", + }, + }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ "app": "yurt-iot-dock", - "control-plane": "edgex-controller-manager", + "control-plane": "platformadmin-controller", }, Namespace: ns, }, diff --git a/pkg/controller/platformadmin/utils/conditions.go b/pkg/controller/platformadmin/utils/conditions.go new file mode 100644 index 00000000000..eb9deb1ae69 --- /dev/null +++ b/pkg/controller/platformadmin/utils/conditions.go @@ -0,0 +1,72 @@ +/* +Copyright 2023 The OpenYurt Authors. + +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 util + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + iotv1alpha2 "github.com/openyurtio/openyurt/pkg/apis/iot/v1alpha2" +) + +// NewPlatformAdminCondition creates a new PlatformAdmin condition. +func NewPlatformAdminCondition(condType iotv1alpha2.PlatformAdminConditionType, status corev1.ConditionStatus, reason, message string) *iotv1alpha2.PlatformAdminCondition { + return &iotv1alpha2.PlatformAdminCondition{ + Type: condType, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + } +} + +// GetPlatformAdminCondition returns the condition with the provided type. +func GetPlatformAdminCondition(status iotv1alpha2.PlatformAdminStatus, condType iotv1alpha2.PlatformAdminConditionType) *iotv1alpha2.PlatformAdminCondition { + for i := range status.Conditions { + c := status.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} + +// SetPlatformAdminCondition updates the PlatformAdmin to include the provided condition. If the condition that +// we are about to add already exists and has the same status, reason and message then we are not going to update. +func SetPlatformAdminCondition(status *iotv1alpha2.PlatformAdminStatus, condition *iotv1alpha2.PlatformAdminCondition) { + currentCond := GetPlatformAdminCondition(*status, condition.Type) + if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { + return + } + + if currentCond != nil && currentCond.Status == condition.Status { + condition.LastTransitionTime = currentCond.LastTransitionTime + } + newConditions := filterOutCondition(status.Conditions, condition.Type) + status.Conditions = append(newConditions, *condition) +} + +func filterOutCondition(conditions []iotv1alpha2.PlatformAdminCondition, condType iotv1alpha2.PlatformAdminConditionType) []iotv1alpha2.PlatformAdminCondition { + var newConditions []iotv1alpha2.PlatformAdminCondition + for _, c := range conditions { + if c.Type == condType { + continue + } + newConditions = append(newConditions, c) + } + return newConditions +} diff --git a/pkg/controller/platformadmin/utils/version.go b/pkg/controller/platformadmin/utils/version.go index f3651b37f9d..8f906e768da 100644 --- a/pkg/controller/platformadmin/utils/version.go +++ b/pkg/controller/platformadmin/utils/version.go @@ -22,10 +22,8 @@ import ( const IotDockName = "yurt-iot-dock" -func defaultVersion(platformAdmin *iotv1alpha2.PlatformAdmin) (string, string, error) { - +func DefaultVersion(platformAdmin *iotv1alpha2.PlatformAdmin) (string, string, error) { version := "v0.3" ns := platformAdmin.Namespace - return version, ns, nil } From 10835d3f4a57a98124b18ba5076eb4810bde7d42 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Sun, 23 Jul 2023 19:16:17 +0800 Subject: [PATCH 7/7] ensure that yurt-iot-dock and CRs are in the same namespace Signed-off-by: wangxye <1031989637@qq.com> --- pkg/controller/platformadmin/util.go | 12 ++++++------ pkg/controller/platformadmin/utils/version.go | 4 +++- .../clients/edgex-foundry/device_client.go | 4 ++-- .../clients/edgex-foundry/device_client_test.go | 14 +++++++------- .../clients/edgex-foundry/deviceprofile_client.go | 4 ++-- .../edgex-foundry/deviceprofile_client_test.go | 6 +++--- .../clients/edgex-foundry/deviceservice_client.go | 4 ++-- .../edgex-foundry/deviceservice_client_test.go | 6 +++--- pkg/yurtiotdock/clients/edgex-foundry/util.go | 12 ++++++------ pkg/yurtiotdock/clients/interface.go | 9 ++++++++- pkg/yurtiotdock/controllers/device_controller.go | 6 ++++-- pkg/yurtiotdock/controllers/device_syncer.go | 2 +- .../controllers/deviceprofile_controller.go | 4 +++- .../controllers/deviceprofile_syncer.go | 2 +- .../controllers/deviceservice_controller.go | 4 +++- .../controllers/deviceservice_syncer.go | 2 +- 16 files changed, 55 insertions(+), 40 deletions(-) diff --git a/pkg/controller/platformadmin/util.go b/pkg/controller/platformadmin/util.go index 5071df727b7..6abc2b5e780 100644 --- a/pkg/controller/platformadmin/util.go +++ b/pkg/controller/platformadmin/util.go @@ -53,23 +53,23 @@ func NewYurtIoTDockComponent(platformAdmin *iotv1alpha2.PlatformAdmin, platformA yurtIotDockComponent.Deployment = &appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ - "app": "yurt-iot-dock", - "control-plane": "platformadmin-controller", + "app": utils.IotDockName, + "control-plane": utils.IotDockControlPlane, }, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ - "app": "yurt-iot-dock", - "control-plane": "platformadmin-controller", + "app": utils.IotDockName, + "control-plane": utils.IotDockControlPlane, }, Namespace: ns, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "yurt-iot-dock", - Image: fmt.Sprintf("leoabyss/yurt-iot-dock:%s", ver), + Name: utils.IotDockName, + Image: fmt.Sprintf("%s:%s", utils.IotDockImage, ver), ImagePullPolicy: corev1.PullIfNotPresent, Args: []string{ "--health-probe-bind-address=:8081", diff --git a/pkg/controller/platformadmin/utils/version.go b/pkg/controller/platformadmin/utils/version.go index 8f906e768da..08542f4c97c 100644 --- a/pkg/controller/platformadmin/utils/version.go +++ b/pkg/controller/platformadmin/utils/version.go @@ -21,9 +21,11 @@ import ( ) const IotDockName = "yurt-iot-dock" +const IotDockImage = "openyurt/yurt-iot-dock" +const IotDockControlPlane = "platformadmin-controller" func DefaultVersion(platformAdmin *iotv1alpha2.PlatformAdmin) (string, string, error) { - version := "v0.3" + version := "latest" ns := platformAdmin.Namespace return version, ns, nil } diff --git a/pkg/yurtiotdock/clients/edgex-foundry/device_client.go b/pkg/yurtiotdock/clients/edgex-foundry/device_client.go index ec40d90ceca..72ef381d8fa 100644 --- a/pkg/yurtiotdock/clients/edgex-foundry/device_client.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/device_client.go @@ -148,7 +148,7 @@ func (efc *EdgexDeviceClient) Get(ctx context.Context, deviceName string, option if err != nil { return nil, err } - device := toKubeDevice(dResp.Device) + device := toKubeDevice(dResp.Device, options.Namespace) return &device, err } @@ -166,7 +166,7 @@ func (efc *EdgexDeviceClient) List(ctx context.Context, options clients.ListOpti } var res []iotv1alpha1.Device for _, dp := range mdResp.Devices { - res = append(res, toKubeDevice(dp)) + res = append(res, toKubeDevice(dp, options.Namespace)) } return res, nil } diff --git a/pkg/yurtiotdock/clients/edgex-foundry/device_client_test.go b/pkg/yurtiotdock/clients/edgex-foundry/device_client_test.go index bc89aa83a2d..5873a540d74 100644 --- a/pkg/yurtiotdock/clients/edgex-foundry/device_client_test.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/device_client_test.go @@ -54,7 +54,7 @@ func Test_Get(t *testing.T) { httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/device/name/Random-Float-Device", httpmock.NewStringResponder(200, DeviceMetadata)) - device, err := deviceClient.Get(context.TODO(), "Random-Float-Device", clients.GetOptions{}) + device, err := deviceClient.Get(context.TODO(), "Random-Float-Device", clients.GetOptions{Namespace: "default"}) assert.Nil(t, err) assert.Equal(t, "Random-Float-Device", device.Spec.Profile) @@ -68,7 +68,7 @@ func Test_List(t *testing.T) { httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/device/all?limit=-1", httpmock.NewStringResponder(200, DeviceListMetadata)) - devices, err := deviceClient.List(context.TODO(), clients.ListOptions{}) + devices, err := deviceClient.List(context.TODO(), clients.ListOptions{Namespace: "default"}) assert.Nil(t, err) assert.Equal(t, len(devices), 5) @@ -86,7 +86,7 @@ func Test_Create(t *testing.T) { err := json.Unmarshal([]byte(DeviceMetadata), &resp) assert.Nil(t, err) - device := toKubeDevice(resp.Device) + device := toKubeDevice(resp.Device, "default") device.Name = "test-Random-Float-Device" create, err := deviceClient.Create(context.TODO(), &device, clients.CreateOptions{}) @@ -133,7 +133,7 @@ func Test_GetPropertyState(t *testing.T) { err := json.Unmarshal([]byte(DeviceMetadata), &resp) assert.Nil(t, err) - device := toKubeDevice(resp.Device) + device := toKubeDevice(resp.Device, "default") _, err = deviceClient.GetPropertyState(context.TODO(), "Float32", &device, clients.GetOptions{}) assert.Nil(t, err) @@ -151,7 +151,7 @@ func Test_ListPropertiesState(t *testing.T) { err := json.Unmarshal([]byte(DeviceMetadata), &resp) assert.Nil(t, err) - device := toKubeDevice(resp.Device) + device := toKubeDevice(resp.Device, "default") _, _, err = deviceClient.ListPropertiesState(context.TODO(), &device, clients.ListOptions{}) assert.Nil(t, err) @@ -169,7 +169,7 @@ func Test_UpdateDevice(t *testing.T) { err := json.Unmarshal([]byte(DeviceMetadata), &resp) assert.Nil(t, err) - device := toKubeDevice(resp.Device) + device := toKubeDevice(resp.Device, "default") device.Spec.AdminState = "LOCKED" _, err = deviceClient.Update(context.TODO(), &device, clients.UpdateOptions{}) @@ -189,7 +189,7 @@ func Test_UpdatePropertyState(t *testing.T) { err := json.Unmarshal([]byte(DeviceMetadata), &resp) assert.Nil(t, err) - device := toKubeDevice(resp.Device) + device := toKubeDevice(resp.Device, "default") device.Spec.DeviceProperties = map[string]iotv1alpha1.DesiredPropertyState{ "Float32": { Name: "Float32", diff --git a/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client.go b/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client.go index 4e86d97ef77..2cd9c9d40c6 100644 --- a/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client.go @@ -65,7 +65,7 @@ func (cdc *EdgexDeviceProfile) List(ctx context.Context, opts devcli.ListOptions } var deviceProfiles []v1alpha1.DeviceProfile for _, dp := range mdpResp.Profiles { - deviceProfiles = append(deviceProfiles, toKubeDeviceProfile(&dp)) + deviceProfiles = append(deviceProfiles, toKubeDeviceProfile(&dp, opts.Namespace)) } return deviceProfiles, nil } @@ -84,7 +84,7 @@ func (cdc *EdgexDeviceProfile) Get(ctx context.Context, name string, opts devcli if err = json.Unmarshal(resp.Body(), &dpResp); err != nil { return nil, err } - kubedp := toKubeDeviceProfile(&dpResp.Profile) + kubedp := toKubeDeviceProfile(&dpResp.Profile, opts.Namespace) return &kubedp, nil } diff --git a/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client_test.go b/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client_test.go index 15fba4e91de..0f32eecf687 100644 --- a/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client_test.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/deviceprofile_client_test.go @@ -47,7 +47,7 @@ func Test_ListProfile(t *testing.T) { httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/deviceprofile/all?limit=-1", httpmock.NewStringResponder(200, DeviceProfileListMetaData)) - profiles, err := profileClient.List(context.TODO(), clients.ListOptions{}) + profiles, err := profileClient.List(context.TODO(), clients.ListOptions{Namespace: "default"}) assert.Nil(t, err) assert.Equal(t, 5, len(profiles)) @@ -60,7 +60,7 @@ func Test_GetProfile(T *testing.T) { httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/deviceprofile/name/Random-Boolean-Device", httpmock.NewStringResponder(200, DeviceProfileMetaData)) - _, err := profileClient.Get(context.TODO(), "Random-Boolean-Device", clients.GetOptions{}) + _, err := profileClient.Get(context.TODO(), "Random-Boolean-Device", clients.GetOptions{Namespace: "default"}) assert.Nil(T, err) } @@ -76,7 +76,7 @@ func Test_CreateProfile(t *testing.T) { err := json.Unmarshal([]byte(DeviceProfileMetaData), &resp) assert.Nil(t, err) - profile := toKubeDeviceProfile(&resp.Profile) + profile := toKubeDeviceProfile(&resp.Profile, "default") profile.Name = "test-Random-Boolean-Device" _, err = profileClient.Create(context.TODO(), &profile, clients.CreateOptions{}) diff --git a/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client.go b/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client.go index 5fc2b4ad31f..c7cd9f70f24 100644 --- a/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client.go @@ -139,7 +139,7 @@ func (eds *EdgexDeviceServiceClient) Get(ctx context.Context, name string, optio if err != nil { return nil, err } - ds := toKubeDeviceService(dsResp.Service) + ds := toKubeDeviceService(dsResp.Service, options.Namespace) return &ds, nil } @@ -160,7 +160,7 @@ func (eds *EdgexDeviceServiceClient) List(ctx context.Context, options edgeCli.L } var res []v1alpha1.DeviceService for _, ds := range mdsResponse.Services { - res = append(res, toKubeDeviceService(ds)) + res = append(res, toKubeDeviceService(ds, options.Namespace)) } return res, nil } diff --git a/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client_test.go b/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client_test.go index 81d8e6f071f..c00d7b9118e 100644 --- a/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client_test.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/deviceservice_client_test.go @@ -49,7 +49,7 @@ func Test_GetService(t *testing.T) { httpmock.RegisterResponder("GET", "http://edgex-core-metadata:59881/api/v2/deviceservice/name/device-virtual", httpmock.NewStringResponder(200, DeviceServiceMetaData)) - _, err := serviceClient.Get(context.TODO(), "device-virtual", clients.GetOptions{}) + _, err := serviceClient.Get(context.TODO(), "device-virtual", clients.GetOptions{Namespace: "default"}) assert.Nil(t, err) } @@ -77,7 +77,7 @@ func Test_CreateService(t *testing.T) { err := json.Unmarshal([]byte(DeviceServiceMetaData), &resp) assert.Nil(t, err) - service := toKubeDeviceService(resp.Service) + service := toKubeDeviceService(resp.Service, "default") service.Name = "test-device-virtual" _, err = serviceClient.Create(context.TODO(), &service, clients.CreateOptions{}) @@ -114,7 +114,7 @@ func Test_UpdateService(t *testing.T) { err := json.Unmarshal([]byte(DeviceServiceMetaData), &resp) assert.Nil(t, err) - service := toKubeDeviceService(resp.Service) + service := toKubeDeviceService(resp.Service, "default") _, err = serviceClient.Update(context.TODO(), &service, clients.UpdateOptions{}) assert.Nil(t, err) diff --git a/pkg/yurtiotdock/clients/edgex-foundry/util.go b/pkg/yurtiotdock/clients/edgex-foundry/util.go index ce685c8a645..8fbf276ab30 100644 --- a/pkg/yurtiotdock/clients/edgex-foundry/util.go +++ b/pkg/yurtiotdock/clients/edgex-foundry/util.go @@ -107,11 +107,11 @@ func toEdgeXProfileProperty(pp iotv1alpha1.ResourceProperties) dtos.ResourceProp } } -func toKubeDeviceService(ds dtos.DeviceService) iotv1alpha1.DeviceService { +func toKubeDeviceService(ds dtos.DeviceService, namespace string) iotv1alpha1.DeviceService { return iotv1alpha1.DeviceService{ ObjectMeta: metav1.ObjectMeta{ Name: toKubeName(ds.Name), - Namespace: "default", + Namespace: namespace, Labels: map[string]string{ EdgeXObjectName: ds.Name, }, @@ -200,7 +200,7 @@ func toEdgeXOperatingState(os iotv1alpha1.OperatingState) models.OperatingState } // toKubeDevice serialize the EdgeX Device to the corresponding Kubernetes Device -func toKubeDevice(ed dtos.Device) iotv1alpha1.Device { +func toKubeDevice(ed dtos.Device, namespace string) iotv1alpha1.Device { var loc string if ed.Location != nil { loc = ed.Location.(string) @@ -208,7 +208,7 @@ func toKubeDevice(ed dtos.Device) iotv1alpha1.Device { return iotv1alpha1.Device{ ObjectMeta: metav1.ObjectMeta{ Name: toKubeName(ed.Name), - Namespace: "default", + Namespace: namespace, Labels: map[string]string{ EdgeXObjectName: ed.Name, }, @@ -247,11 +247,11 @@ func toKubeProtocols( } // toKubeDeviceProfile create DeviceProfile in cloud according to devicProfile in edge -func toKubeDeviceProfile(dp *dtos.DeviceProfile) iotv1alpha1.DeviceProfile { +func toKubeDeviceProfile(dp *dtos.DeviceProfile, namespace string) iotv1alpha1.DeviceProfile { return iotv1alpha1.DeviceProfile{ ObjectMeta: metav1.ObjectMeta{ Name: toKubeName(dp.Name), - Namespace: "default", + Namespace: namespace, Labels: map[string]string{ EdgeXObjectName: dp.Name, }, diff --git a/pkg/yurtiotdock/clients/interface.go b/pkg/yurtiotdock/clients/interface.go index fdfe654fea1..93ffc3574bd 100644 --- a/pkg/yurtiotdock/clients/interface.go +++ b/pkg/yurtiotdock/clients/interface.go @@ -36,7 +36,11 @@ type UpdateOptions struct{} // GetOptions defines additional options when getting an object // Additional general field definitions can be added -type GetOptions struct{} +type GetOptions struct { + // Namespace represents the namespace to list for, or empty for + // non-namespaced objects, or to list across all namespaces. + Namespace string +} // ListOptions defines additional options when listing an object type ListOptions struct { @@ -48,6 +52,9 @@ type ListOptions struct { // Defaults to everything. // +optional FieldSelector map[string]string + // Namespace represents the namespace to list for, or empty for + // non-namespaced objects, or to list across all namespaces. + Namespace string } // DeviceInterface defines the interfaces which used to create, delete, update, get and list Device objects on edge-side platform diff --git a/pkg/yurtiotdock/controllers/device_controller.go b/pkg/yurtiotdock/controllers/device_controller.go index d817bd99b56..dd61d5a6a96 100644 --- a/pkg/yurtiotdock/controllers/device_controller.go +++ b/pkg/yurtiotdock/controllers/device_controller.go @@ -43,7 +43,8 @@ type DeviceReconciler struct { Scheme *runtime.Scheme deviceCli clients.DeviceInterface // which nodePool deviceController is deployed in - NodePool string + NodePool string + Namespace string } //+kubebuilder:rbac:groups=iot.openyurt.io,resources=devices,verbs=get;list;watch;create;update;patch;delete @@ -110,6 +111,7 @@ func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr func (r *DeviceReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIoTDockOptions) error { r.deviceCli = edgexCli.NewEdgexDeviceClient(opts.CoreMetadataAddr, opts.CoreCommandAddr) r.NodePool = opts.Nodepool + r.Namespace = opts.Namespace return ctrl.NewControllerManagedBy(mgr). For(&iotv1alpha1.Device{}). @@ -157,7 +159,7 @@ func (r *DeviceReconciler) reconcileCreateDevice(ctx context.Context, d *iotv1al newDeviceStatus := d.Status.DeepCopy() klog.V(4).Infof("Checking if device already exist on the edge platform: %s", d.GetName()) // Checking if device already exist on the edge platform - edgeDevice, err := r.deviceCli.Get(context.TODO(), edgeDeviceName, clients.GetOptions{}) + edgeDevice, err := r.deviceCli.Get(context.TODO(), edgeDeviceName, clients.GetOptions{Namespace: r.Namespace}) if err == nil { // a. If object exists, the status of the device on OpenYurt is updated klog.V(4).Infof("Device already exists on edge platform: %s", d.GetName()) diff --git a/pkg/yurtiotdock/controllers/device_syncer.go b/pkg/yurtiotdock/controllers/device_syncer.go index ea962d42f55..501ba6101dc 100644 --- a/pkg/yurtiotdock/controllers/device_syncer.go +++ b/pkg/yurtiotdock/controllers/device_syncer.go @@ -113,7 +113,7 @@ func (ds *DeviceSyncer) getAllDevices() (map[string]iotv1alpha1.Device, map[stri edgeDevice := map[string]iotv1alpha1.Device{} kubeDevice := map[string]iotv1alpha1.Device{} // 1. list devices on edge platform - eDevs, err := ds.deviceCli.List(context.TODO(), edgeCli.ListOptions{}) + eDevs, err := ds.deviceCli.List(context.TODO(), edgeCli.ListOptions{Namespace: ds.Namespace}) if err != nil { klog.V(4).ErrorS(err, "fail to list the devices object on the Edge Platform") return edgeDevice, kubeDevice, err diff --git a/pkg/yurtiotdock/controllers/deviceprofile_controller.go b/pkg/yurtiotdock/controllers/deviceprofile_controller.go index c8d85501967..d174cc2ebb6 100644 --- a/pkg/yurtiotdock/controllers/deviceprofile_controller.go +++ b/pkg/yurtiotdock/controllers/deviceprofile_controller.go @@ -41,6 +41,7 @@ type DeviceProfileReconciler struct { Scheme *runtime.Scheme edgeClient clients.DeviceProfileInterface NodePool string + Namespace string } //+kubebuilder:rbac:groups=iot.openyurt.io,resources=deviceprofiles,verbs=get;list;watch;create;update;patch;delete @@ -88,6 +89,7 @@ func (r *DeviceProfileReconciler) Reconcile(ctx context.Context, req ctrl.Reques func (r *DeviceProfileReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIoTDockOptions) error { r.edgeClient = edgexclis.NewEdgexDeviceProfile(opts.CoreMetadataAddr) r.NodePool = opts.Nodepool + r.Namespace = opts.Namespace return ctrl.NewControllerManagedBy(mgr). For(&iotv1alpha1.DeviceProfile{}). @@ -137,7 +139,7 @@ func (r *DeviceProfileReconciler) reconcileDeleteDeviceProfile(ctx context.Conte func (r *DeviceProfileReconciler) reconcileCreateDeviceProfile(ctx context.Context, dp *iotv1alpha1.DeviceProfile, actualName string) error { klog.V(4).Infof("Checking if deviceProfile already exist on the edge platform: %s", dp.GetName()) - if edgeDp, err := r.edgeClient.Get(context.TODO(), actualName, clients.GetOptions{}); err != nil { + if edgeDp, err := r.edgeClient.Get(context.TODO(), actualName, clients.GetOptions{Namespace: r.Namespace}); err != nil { if !clients.IsNotFoundErr(err) { klog.V(4).ErrorS(err, "fail to visit the edge platform") return nil diff --git a/pkg/yurtiotdock/controllers/deviceprofile_syncer.go b/pkg/yurtiotdock/controllers/deviceprofile_syncer.go index a663a1a417e..72d67729b47 100644 --- a/pkg/yurtiotdock/controllers/deviceprofile_syncer.go +++ b/pkg/yurtiotdock/controllers/deviceprofile_syncer.go @@ -114,7 +114,7 @@ func (dps *DeviceProfileSyncer) getAllDeviceProfiles() ( kubeDeviceProfiles := map[string]iotv1alpha1.DeviceProfile{} // 1. list deviceProfiles on edge platform - eDps, err := dps.edgeClient.List(context.TODO(), devcli.ListOptions{}) + eDps, err := dps.edgeClient.List(context.TODO(), devcli.ListOptions{Namespace: dps.Namespace}) if err != nil { klog.V(4).ErrorS(err, "fail to list the deviceProfiles on the edge platform") return edgeDeviceProfiles, kubeDeviceProfiles, err diff --git a/pkg/yurtiotdock/controllers/deviceservice_controller.go b/pkg/yurtiotdock/controllers/deviceservice_controller.go index 7afa7033df7..2822558b5f0 100644 --- a/pkg/yurtiotdock/controllers/deviceservice_controller.go +++ b/pkg/yurtiotdock/controllers/deviceservice_controller.go @@ -42,6 +42,7 @@ type DeviceServiceReconciler struct { Scheme *runtime.Scheme deviceServiceCli clients.DeviceServiceInterface NodePool string + Namespace string } //+kubebuilder:rbac:groups=iot.openyurt.io,resources=deviceservices,verbs=get;list;watch;create;update;patch;delete @@ -108,6 +109,7 @@ func (r *DeviceServiceReconciler) Reconcile(ctx context.Context, req ctrl.Reques func (r *DeviceServiceReconciler) SetupWithManager(mgr ctrl.Manager, opts *options.YurtIoTDockOptions) error { r.deviceServiceCli = edgexCli.NewEdgexDeviceServiceClient(opts.CoreMetadataAddr) r.NodePool = opts.Nodepool + r.Namespace = opts.Namespace return ctrl.NewControllerManagedBy(mgr). For(&iotv1alpha1.DeviceService{}). @@ -161,7 +163,7 @@ func (r *DeviceServiceReconciler) reconcileCreateDeviceService(ctx context.Conte edgeDeviceServiceName := util.GetEdgeDeviceServiceName(ds, EdgeXObjectName) klog.V(4).Infof("Checking if deviceService already exist on the edge platform: %s", ds.GetName()) // Checking if deviceService already exist on the edge platform - if edgeDs, err := r.deviceServiceCli.Get(context.TODO(), edgeDeviceServiceName, clients.GetOptions{}); err != nil { + if edgeDs, err := r.deviceServiceCli.Get(context.TODO(), edgeDeviceServiceName, clients.GetOptions{Namespace: r.Namespace}); err != nil { if !clients.IsNotFoundErr(err) { klog.V(4).ErrorS(err, "fail to visit the edge platform") return nil diff --git a/pkg/yurtiotdock/controllers/deviceservice_syncer.go b/pkg/yurtiotdock/controllers/deviceservice_syncer.go index 8b07c09c670..78949c55069 100644 --- a/pkg/yurtiotdock/controllers/deviceservice_syncer.go +++ b/pkg/yurtiotdock/controllers/deviceservice_syncer.go @@ -112,7 +112,7 @@ func (ds *DeviceServiceSyncer) getAllDeviceServices() ( kubeDeviceServices := map[string]iotv1alpha1.DeviceService{} // 1. list deviceServices on edge platform - eDevSs, err := ds.deviceServiceCli.List(context.TODO(), iotcli.ListOptions{}) + eDevSs, err := ds.deviceServiceCli.List(context.TODO(), iotcli.ListOptions{Namespace: ds.Namespace}) if err != nil { klog.V(4).ErrorS(err, "fail to list the deviceServices object on the edge platform") return edgeDeviceServices, kubeDeviceServices, err