From be061073f4d3bcfc757f8619db9195ab312a5164 Mon Sep 17 00:00:00 2001 From: Rui-Gan <1171530954@qq.com> Date: Tue, 23 May 2023 20:27:30 +0800 Subject: [PATCH] add a new crd device/v1alpha1&v1alpha2/iot and controller, webhook Signed-off-by: Rui-Gan <1171530954@qq.com> --- .../crds/device.openyurt.io_iot.yaml | 8491 +++++++++++++++++ .../yurt-manager-auto-generated.yaml | 88 + cmd/yurt-manager/app/options/iotcontroller.go | 62 + cmd/yurt-manager/app/options/options.go | 7 + go.mod | 2 +- pkg/apis/addtoscheme_device_v1alpha1.go | 26 + pkg/apis/addtoscheme_device_v1alpha2.go | 26 + pkg/apis/device/v1alpha1/condition_const.go | 38 + pkg/apis/device/v1alpha1/default.go | 24 + pkg/apis/device/v1alpha1/doc.go | 17 + pkg/apis/device/v1alpha1/groupversion_info.go | 44 + pkg/apis/device/v1alpha1/iot_conversion.go | 140 + pkg/apis/device/v1alpha1/iot_types.go | 142 + .../device/v1alpha1/zz_generated.deepcopy.go | 184 + pkg/apis/device/v1alpha2/condition_const.go | 32 + pkg/apis/device/v1alpha2/default.go | 24 + pkg/apis/device/v1alpha2/doc.go | 17 + pkg/apis/device/v1alpha2/groupversion_info.go | 44 + pkg/apis/device/v1alpha2/iot_conversion.go | 20 + pkg/apis/device/v1alpha2/iot_types.go | 141 + .../device/v1alpha2/zz_generated.deepcopy.go | 156 + pkg/controller/apis/config/types.go | 4 + pkg/controller/controller.go | 2 + .../config/EdgeXConfig/config-nosecty.json | 4657 +++++++++ .../iot/config/EdgeXConfig/config.json | 1 + .../iot/config/EdgeXConfig/manifest.yaml | 9 + pkg/controller/iot/config/types.go | 107 + pkg/controller/iot/iot_controller.go | 566 ++ pkg/controller/iot/utils/fieldindexer.go | 50 + pkg/controller/iot/utils/util.go | 72 + pkg/webhook/iot/v1alpha1/iot_default.go | 43 + pkg/webhook/iot/v1alpha1/iot_handler.go | 86 + pkg/webhook/iot/v1alpha1/iot_validation.go | 119 + pkg/webhook/iot/v1alpha2/iot_default.go | 47 + pkg/webhook/iot/v1alpha2/iot_handler.go | 89 + pkg/webhook/iot/v1alpha2/iot_validation.go | 144 + pkg/webhook/server.go | 12 +- 37 files changed, 15728 insertions(+), 5 deletions(-) create mode 100644 charts/yurt-manager/crds/device.openyurt.io_iot.yaml create mode 100644 cmd/yurt-manager/app/options/iotcontroller.go create mode 100644 pkg/apis/addtoscheme_device_v1alpha1.go create mode 100644 pkg/apis/addtoscheme_device_v1alpha2.go create mode 100644 pkg/apis/device/v1alpha1/condition_const.go create mode 100644 pkg/apis/device/v1alpha1/default.go create mode 100644 pkg/apis/device/v1alpha1/doc.go create mode 100644 pkg/apis/device/v1alpha1/groupversion_info.go create mode 100644 pkg/apis/device/v1alpha1/iot_conversion.go create mode 100644 pkg/apis/device/v1alpha1/iot_types.go create mode 100644 pkg/apis/device/v1alpha1/zz_generated.deepcopy.go create mode 100644 pkg/apis/device/v1alpha2/condition_const.go create mode 100644 pkg/apis/device/v1alpha2/default.go create mode 100644 pkg/apis/device/v1alpha2/doc.go create mode 100644 pkg/apis/device/v1alpha2/groupversion_info.go create mode 100644 pkg/apis/device/v1alpha2/iot_conversion.go create mode 100644 pkg/apis/device/v1alpha2/iot_types.go create mode 100644 pkg/apis/device/v1alpha2/zz_generated.deepcopy.go create mode 100644 pkg/controller/iot/config/EdgeXConfig/config-nosecty.json create mode 100644 pkg/controller/iot/config/EdgeXConfig/config.json create mode 100644 pkg/controller/iot/config/EdgeXConfig/manifest.yaml create mode 100644 pkg/controller/iot/config/types.go create mode 100644 pkg/controller/iot/iot_controller.go create mode 100644 pkg/controller/iot/utils/fieldindexer.go create mode 100644 pkg/controller/iot/utils/util.go create mode 100644 pkg/webhook/iot/v1alpha1/iot_default.go create mode 100644 pkg/webhook/iot/v1alpha1/iot_handler.go create mode 100644 pkg/webhook/iot/v1alpha1/iot_validation.go create mode 100644 pkg/webhook/iot/v1alpha2/iot_default.go create mode 100644 pkg/webhook/iot/v1alpha2/iot_handler.go create mode 100644 pkg/webhook/iot/v1alpha2/iot_validation.go diff --git a/charts/yurt-manager/crds/device.openyurt.io_iot.yaml b/charts/yurt-manager/crds/device.openyurt.io_iot.yaml new file mode 100644 index 00000000000..1d4544f133a --- /dev/null +++ b/charts/yurt-manager/crds/device.openyurt.io_iot.yaml @@ -0,0 +1,8491 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: iots.device.openyurt.io +spec: + group: device.openyurt.io + names: + categories: + - all + kind: IoT + listKind: IoTList + plural: iots + shortNames: + - iot + singular: iot + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The edgex 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: device.openyurt.io/v1alpha1 EdgeX will be deprecated in future; + use device.openyurt.io/v1alpha2 EdgeX; v1alpha1 EdgeX.Spec.ServiceType only + support ClusterIP + name: v1alpha1 + schema: + openAPIV3Schema: + description: IoT 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: IoTSpec defines the desired state of IoT + 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: IoTStatus defines the observed state of IoT + properties: + conditions: + description: Current Edgex state + items: + description: IoTCondition describes current state of a IoT. + 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 edgex 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: IoT 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: IoTSpec defines the desired state of IoT + 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: IoTStatus defines the observed state of IoT + properties: + conditions: + description: Current Edgex state + items: + description: IoTCondition describes current state of a IoT. + 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 70435df84b4..a7fd29b5ee7 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -255,6 +255,28 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps/status + - services/status + verbs: + - get + - patch + - update - apiGroups: - "" resources: @@ -351,6 +373,32 @@ rules: - get - list - watch +- apiGroups: + - device.openyurt.io + resources: + - iots + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - device.openyurt.io + resources: + - iots/finalizers + verbs: + - update +- apiGroups: + - device.openyurt.io + resources: + - iots/status + verbs: + - get + - patch + - update - apiGroups: - discovery.k8s.io resources: @@ -410,6 +458,26 @@ webhooks: resources: - gateways sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: yurt-manager-webhook-service + namespace: {{ .Release.Namespace }} + path: /mutate-device-openyurt-io-v1alpha2-iot + failurePolicy: Fail + name: miot.kb.io + rules: + - apiGroups: + - device.openyurt.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - iots + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -520,6 +588,26 @@ webhooks: resources: - gateways sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: yurt-manager-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-device-openyurt-io-v1alpha2-iot + failurePolicy: Fail + name: viot.kb.io + rules: + - apiGroups: + - device.openyurt.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - iots + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/cmd/yurt-manager/app/options/iotcontroller.go b/cmd/yurt-manager/app/options/iotcontroller.go new file mode 100644 index 00000000000..aff119c7595 --- /dev/null +++ b/cmd/yurt-manager/app/options/iotcontroller.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 options + +import ( + "errors" + + "github.com/openyurtio/openyurt/pkg/controller/iot/config" + "github.com/spf13/pflag" +) + +type IoTControllerOptions struct { + *config.IoTControllerConfiguration +} + +func NewIoTControllerOptions() *IoTControllerOptions { + return &IoTControllerOptions{ + config.NewIoTControllerConfiguration(), + } +} + +// AddFlags adds flags related to nodepool for yurt-manager to the specified FlagSet. +func (n *IoTControllerOptions) AddFlags(fs *pflag.FlagSet) { + if n == nil { + return + } +} + +// ApplyTo fills up nodepool config with options. +func (o *IoTControllerOptions) ApplyTo(cfg *config.IoTControllerConfiguration) error { + if o == nil { + return nil + } + *cfg = *o.IoTControllerConfiguration + return nil +} + +// Validate checks validation of IoTControllerOptions. +func (o *IoTControllerOptions) Validate() []error { + if o == nil { + return nil + } + errs := []error{} + if o.IoTControllerConfiguration == nil { + errs = append(errs, errors.New("IoTControllerConfiguration can not be empty!")) + } + return errs +} diff --git a/cmd/yurt-manager/app/options/options.go b/cmd/yurt-manager/app/options/options.go index 0ae5bb68304..b04e43dbf2a 100644 --- a/cmd/yurt-manager/app/options/options.go +++ b/cmd/yurt-manager/app/options/options.go @@ -31,6 +31,7 @@ type YurtManagerOptions struct { YurtStaticSetController *YurtStaticSetControllerOptions YurtAppSetController *YurtAppSetControllerOptions YurtAppDaemonController *YurtAppDaemonControllerOptions + IoTController *IoTControllerOptions } // NewYurtManagerOptions creates a new YurtManagerOptions with a default config. @@ -43,6 +44,7 @@ func NewYurtManagerOptions() (*YurtManagerOptions, error) { YurtStaticSetController: NewYurtStaticSetControllerOptions(), YurtAppSetController: NewYurtAppSetControllerOptions(), YurtAppDaemonController: NewYurtAppDaemonControllerOptions(), + IoTController: NewIoTControllerOptions(), } return &s, nil @@ -55,6 +57,7 @@ func (y *YurtManagerOptions) Flags() cliflag.NamedFlagSets { y.GatewayController.AddFlags(fss.FlagSet("gateway controller")) y.YurtStaticSetController.AddFlags(fss.FlagSet("yurtstaticset controller")) y.YurtAppDaemonController.AddFlags(fss.FlagSet("yurtappdaemon controller")) + y.IoTController.AddFlags(fss.FlagSet("iot controller")) // Please Add Other controller flags @kadisi return fss @@ -68,6 +71,7 @@ func (y *YurtManagerOptions) Validate() error { errs = append(errs, y.GatewayController.Validate()...) errs = append(errs, y.YurtStaticSetController.Validate()...) errs = append(errs, y.YurtAppDaemonController.Validate()...) + errs = append(errs, y.IoTController.Validate()...) return utilerrors.NewAggregate(errs) } @@ -85,6 +89,9 @@ func (y *YurtManagerOptions) ApplyTo(c *config.Config) error { if err := y.YurtAppDaemonController.ApplyTo(&c.ComponentConfig.YurtAppDaemonController); err != nil { return err } + if err := y.IoTController.ApplyTo(&c.ComponentConfig.IoTController); err != nil { + return err + } return nil } diff --git a/go.mod b/go.mod index 6d51a51ae1f..3734cf342ab 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( google.golang.org/grpc v1.54.0 gopkg.in/cheggaaa/pb.v1 v1.0.28 gopkg.in/square/go-jose.v2 v2.6.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.22.3 k8s.io/apimachinery v0.22.3 k8s.io/apiserver v0.22.3 @@ -153,7 +154,6 @@ require ( gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.22.2 // indirect k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 // indirect diff --git a/pkg/apis/addtoscheme_device_v1alpha1.go b/pkg/apis/addtoscheme_device_v1alpha1.go new file mode 100644 index 00000000000..ca1fa5ea3a5 --- /dev/null +++ b/pkg/apis/addtoscheme_device_v1alpha1.go @@ -0,0 +1,26 @@ +/* +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 apis + +import ( + version "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, version.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/addtoscheme_device_v1alpha2.go b/pkg/apis/addtoscheme_device_v1alpha2.go new file mode 100644 index 00000000000..1583f3e233a --- /dev/null +++ b/pkg/apis/addtoscheme_device_v1alpha2.go @@ -0,0 +1,26 @@ +/* +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 apis + +import ( + version "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha2" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, version.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/device/v1alpha1/condition_const.go b/pkg/apis/device/v1alpha1/condition_const.go new file mode 100644 index 00000000000..f487a2c0f1f --- /dev/null +++ b/pkg/apis/device/v1alpha1/condition_const.go @@ -0,0 +1,38 @@ +/* +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 + +const ( + // ConfigmapAvailableCondition documents the status of the EdgeX configmap. + ConfigmapAvailableCondition IoTConditionType = "ConfigmapAvailable" + + ConfigmapProvisioningReason = "ConfigmapProvisioning" + + ConfigmapProvisioningFailedReason = "ConfigmapProvisioningFailed" + // ServiceAvailableCondition documents the status of the EdgeX service. + ServiceAvailableCondition IoTConditionType = "ServiceAvailable" + + ServiceProvisioningReason = "ServiceProvisioning" + + ServiceProvisioningFailedReason = "ServiceProvisioningFailed" + // DeploymentAvailableCondition documents the status of the EdgeX deployment. + DeploymentAvailableCondition IoTConditionType = "DeploymentAvailable" + + DeploymentProvisioningReason = "DeploymentProvisioning" + + DeploymentProvisioningFailedReason = "DeploymentProvisioningFailed" +) diff --git a/pkg/apis/device/v1alpha1/default.go b/pkg/apis/device/v1alpha1/default.go new file mode 100644 index 00000000000..28e1432ad9f --- /dev/null +++ b/pkg/apis/device/v1alpha1/default.go @@ -0,0 +1,24 @@ +/* +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 + +// SetDefaultsIoT set default values for IoT. +func SetDefaultsIoT(obj *IoT) { + if obj.Annotations == nil { + obj.Annotations = make(map[string]string) + } +} diff --git a/pkg/apis/device/v1alpha1/doc.go b/pkg/apis/device/v1alpha1/doc.go new file mode 100644 index 00000000000..f1c0c289775 --- /dev/null +++ b/pkg/apis/device/v1alpha1/doc.go @@ -0,0 +1,17 @@ +/* +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. +*/ +// +groupName=device.openyurt.io +package v1alpha1 diff --git a/pkg/apis/device/v1alpha1/groupversion_info.go b/pkg/apis/device/v1alpha1/groupversion_info.go new file mode 100644 index 00000000000..0fe6e00dad4 --- /dev/null +++ b/pkg/apis/device/v1alpha1/groupversion_info.go @@ -0,0 +1,44 @@ +/* +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 + +// Package v1alpha1 contains API Schema definitions for the device v1alpha1API group +// +kubebuilder:object:generate=true +// +groupName=device.openyurt.io + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "device.openyurt.io", Version: "v1alpha1"} + + SchemeGroupVersion = GroupVersion + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource is required by pkg/client/listers/... +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/pkg/apis/device/v1alpha1/iot_conversion.go b/pkg/apis/device/v1alpha1/iot_conversion.go new file mode 100644 index 00000000000..aa7def14e19 --- /dev/null +++ b/pkg/apis/device/v1alpha1/iot_conversion.go @@ -0,0 +1,140 @@ +/* +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 ( + "encoding/json" + + "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha2" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *IoT) ConvertTo(dstRaw conversion.Hub) error { + // Transform metadata + dst := dstRaw.(*v1alpha2.IoT) + dst.ObjectMeta = src.ObjectMeta + dst.TypeMeta = src.TypeMeta + dst.TypeMeta.APIVersion = "device.openyurt.io/v1alpha2" + + // Transform spec + dst.Spec.Version = src.Spec.Version + dst.Spec.Security = false + dst.Spec.ImageRegistry = src.Spec.ImageRegistry + dst.Spec.PoolName = src.Spec.PoolName + dst.Spec.Platform = v1alpha2.IoTPlatformEdgeX + + // Transform status + dst.Status.Ready = src.Status.Ready + dst.Status.Initialized = src.Status.Initialized + dst.Status.ReadyComponentNum = src.Status.DeploymentReadyReplicas + dst.Status.UnreadyComponentNum = src.Status.DeploymentReplicas - src.Status.DeploymentReadyReplicas + dst.Status.Conditions = transToV2Condition(src.Status.Conditions) + + // Transform additionaldeployment + if len(src.Spec.AdditionalDeployment) > 0 { + additionalDeployment, err := json.Marshal(src.Spec.AdditionalDeployment) + if err != nil { + return err + } + dst.ObjectMeta.Annotations["AdditionalDeployments"] = string(additionalDeployment) + } + + // Transform additionalservice + if len(src.Spec.AdditionalService) > 0 { + additionalService, err := json.Marshal(src.Spec.AdditionalService) + if err != nil { + return err + } + dst.ObjectMeta.Annotations["AdditionalServices"] = string(additionalService) + } + + //TODO: Components + + return nil +} + +func (dst *IoT) ConvertFrom(srcRaw conversion.Hub) error { + // Transform metadata + src := srcRaw.(*v1alpha2.IoT) + dst.ObjectMeta = src.ObjectMeta + dst.TypeMeta = src.TypeMeta + dst.TypeMeta.APIVersion = "device.openyurt.io/v1alpha1" + + // Transform spec + dst.Spec.Version = src.Spec.Version + dst.Spec.ImageRegistry = src.Spec.ImageRegistry + dst.Spec.PoolName = src.Spec.PoolName + dst.Spec.ServiceType = corev1.ServiceTypeClusterIP + + // Transform status + dst.Status.Ready = src.Status.Ready + dst.Status.Initialized = src.Status.Initialized + dst.Status.ServiceReadyReplicas = src.Status.ReadyComponentNum + dst.Status.ServiceReplicas = src.Status.ReadyComponentNum + src.Status.UnreadyComponentNum + dst.Status.DeploymentReadyReplicas = src.Status.ReadyComponentNum + dst.Status.DeploymentReplicas = src.Status.ReadyComponentNum + src.Status.UnreadyComponentNum + dst.Status.Conditions = transToV1Condition(src.Status.Conditions) + + // Transform additionaldeployment + if _, ok := src.ObjectMeta.Annotations["AdditionalDeployments"]; ok { + var additionalDeployments []DeploymentTemplateSpec = make([]DeploymentTemplateSpec, 0) + err := json.Unmarshal([]byte(src.ObjectMeta.Annotations["AdditionalDeployments"]), &additionalDeployments) + if err != nil { + return err + } + dst.Spec.AdditionalDeployment = additionalDeployments + } + + // Transform additionalservice + if _, ok := src.ObjectMeta.Annotations["AdditionalServices"]; ok { + var additionalServices []ServiceTemplateSpec = make([]ServiceTemplateSpec, 0) + err := json.Unmarshal([]byte(src.ObjectMeta.Annotations["AdditionalServices"]), &additionalServices) + if err != nil { + return err + } + dst.Spec.AdditionalService = additionalServices + } + + return nil +} + +func transToV1Condition(c2 []v1alpha2.IoTCondition) (c1 []IoTCondition) { + for _, ic := range c2 { + c1 = append(c1, IoTCondition{ + Type: IoTConditionType(ic.Type), + Status: ic.Status, + LastTransitionTime: ic.LastTransitionTime, + Reason: ic.Reason, + Message: ic.Message, + }) + } + return +} + +func transToV2Condition(c1 []IoTCondition) (c2 []v1alpha2.IoTCondition) { + for _, ic := range c1 { + c2 = append(c2, v1alpha2.IoTCondition{ + Type: v1alpha2.IoTConditionType(ic.Type), + Status: ic.Status, + LastTransitionTime: ic.LastTransitionTime, + Reason: ic.Reason, + Message: ic.Message, + }) + } + return +} diff --git a/pkg/apis/device/v1alpha1/iot_types.go b/pkg/apis/device/v1alpha1/iot_types.go new file mode 100644 index 00000000000..75fc6e230b2 --- /dev/null +++ b/pkg/apis/device/v1alpha1/iot_types.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 v1alpha1 + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // name of finalizer + EdgexFinalizer = "edgex.edgexfoundry.org" + + LabelEdgeXGenerate = "www.edgexfoundry.org/generate" +) + +// IoTConditionType indicates valid conditions type of a IoT. +type IoTConditionType string +type IoTConditionSeverity string + +// DeploymentTemplateSpec defines the pool template of Deployment. +type DeploymentTemplateSpec struct { + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec appsv1.DeploymentSpec `json:"spec"` +} + +// DeploymentTemplateSpec defines the pool template of Deployment. +type ServiceTemplateSpec struct { + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec corev1.ServiceSpec `json:"spec"` +} + +// IoTSpec defines the desired state of IoT +type IoTSpec struct { + Version string `json:"version,omitempty"` + + ImageRegistry string `json:"imageRegistry,omitempty"` + + PoolName string `json:"poolName,omitempty"` + + ServiceType corev1.ServiceType `json:"serviceType,omitempty"` + // +optional + AdditionalService []ServiceTemplateSpec `json:"additionalServices,omitempty"` + + // +optional + AdditionalDeployment []DeploymentTemplateSpec `json:"additionalDeployments,omitempty"` +} + +// IoTStatus defines the observed state of IoT +type IoTStatus struct { + // +optional + Ready bool `json:"ready,omitempty"` + // +optional + Initialized bool `json:"initialized,omitempty"` + // +optional + ServiceReplicas int32 `json:"serviceReplicas,omitempty"` + // +optional + ServiceReadyReplicas int32 `json:"serviceReadyReplicas,omitempty"` + // +optional + DeploymentReplicas int32 `json:"deploymentReplicas,omitempty"` + // +optional + DeploymentReadyReplicas int32 `json:"deploymentReadyReplicas,omitempty"` + + // Current Edgex state + // +optional + Conditions []IoTCondition `json:"conditions,omitempty"` +} + +// IoTCondition describes current state of a IoT. +type IoTCondition struct { + // Type of in place set condition. + Type IoTConditionType `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"` +} + +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced,path=iots,shortName=iot,categories=all +// +kubebuilder:printcolumn:name="READY",type="boolean",JSONPath=".status.ready",description="The edgex ready status" +// +kubebuilder:printcolumn:name="Service",type="integer",JSONPath=".status.serviceReplicas",description="The Service Replica." +// +kubebuilder:printcolumn:name="ReadyService",type="integer",JSONPath=".status.serviceReadyReplicas",description="The Ready Service Replica." +// +kubebuilder:printcolumn:name="Deployment",type="integer",JSONPath=".status.deploymentReplicas",description="The Deployment Replica." +// +kubebuilder:printcolumn:name="ReadyDeployment",type="integer",JSONPath=".status.deploymentReadyReplicas",description="The Ready Deployment Replica." +// +kubebuilder:deprecatedversion:warning="device.openyurt.io/v1alpha1 EdgeX will be deprecated in future; use device.openyurt.io/v1alpha2 EdgeX; v1alpha1 EdgeX.Spec.ServiceType only support ClusterIP" + +// IoT is the Schema for the samples API +type IoT struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec IoTSpec `json:"spec,omitempty"` + Status IoTStatus `json:"status,omitempty"` +} + +func (c *IoT) GetConditions() []IoTCondition { + return c.Status.Conditions +} + +func (c *IoT) SetConditions(conditions []IoTCondition) { + c.Status.Conditions = conditions +} + +//+kubebuilder:object:root=true + +// IoTList contains a list of IoT +type IoTList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []IoT `json:"items"` +} + +func init() { + SchemeBuilder.Register(&IoT{}, &IoTList{}) +} diff --git a/pkg/apis/device/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/device/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..8374c53e7fd --- /dev/null +++ b/pkg/apis/device/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,184 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +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 ( + 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 *DeploymentTemplateSpec) DeepCopyInto(out *DeploymentTemplateSpec) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentTemplateSpec. +func (in *DeploymentTemplateSpec) DeepCopy() *DeploymentTemplateSpec { + if in == nil { + return nil + } + out := new(DeploymentTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IoT) DeepCopyInto(out *IoT) { + *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 IoT. +func (in *IoT) DeepCopy() *IoT { + if in == nil { + return nil + } + out := new(IoT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IoT) 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 *IoTCondition) DeepCopyInto(out *IoTCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IoTCondition. +func (in *IoTCondition) DeepCopy() *IoTCondition { + if in == nil { + return nil + } + out := new(IoTCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IoTList) DeepCopyInto(out *IoTList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]IoT, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IoTList. +func (in *IoTList) DeepCopy() *IoTList { + if in == nil { + return nil + } + out := new(IoTList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IoTList) 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 *IoTSpec) DeepCopyInto(out *IoTSpec) { + *out = *in + if in.AdditionalService != nil { + in, out := &in.AdditionalService, &out.AdditionalService + *out = make([]ServiceTemplateSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AdditionalDeployment != nil { + in, out := &in.AdditionalDeployment, &out.AdditionalDeployment + *out = make([]DeploymentTemplateSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IoTSpec. +func (in *IoTSpec) DeepCopy() *IoTSpec { + if in == nil { + return nil + } + out := new(IoTSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IoTStatus) DeepCopyInto(out *IoTStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]IoTCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IoTStatus. +func (in *IoTStatus) DeepCopy() *IoTStatus { + if in == nil { + return nil + } + out := new(IoTStatus) + 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 + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceTemplateSpec. +func (in *ServiceTemplateSpec) DeepCopy() *ServiceTemplateSpec { + if in == nil { + return nil + } + out := new(ServiceTemplateSpec) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/device/v1alpha2/condition_const.go b/pkg/apis/device/v1alpha2/condition_const.go new file mode 100644 index 00000000000..721fe1b1971 --- /dev/null +++ b/pkg/apis/device/v1alpha2/condition_const.go @@ -0,0 +1,32 @@ +/* +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 v1alpha2 + +const ( + // ConfigmapAvailableCondition documents the status of the EdgeX configmap. + ConfigmapAvailableCondition IoTConditionType = "ConfigmapAvailable" + + ConfigmapProvisioningReason = "ConfigmapProvisioning" + + ConfigmapProvisioningFailedReason = "ConfigmapProvisioningFailed" + // ComponentAvailableCondition documents the status of the EdgeX component. + ComponentAvailableCondition IoTConditionType = "ComponentAvailable" + + ComponentProvisioningReason = "ComponentProvisioning" + + ComponentProvisioningFailedReason = "ComponentProvisioningFailed" +) diff --git a/pkg/apis/device/v1alpha2/default.go b/pkg/apis/device/v1alpha2/default.go new file mode 100644 index 00000000000..a469c31cb66 --- /dev/null +++ b/pkg/apis/device/v1alpha2/default.go @@ -0,0 +1,24 @@ +/* +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 v1alpha2 + +// SetDefaultsIoT set default values for IoT. +func SetDefaultsIoT(obj *IoT) { + if obj.Annotations == nil { + obj.Annotations = make(map[string]string) + } +} diff --git a/pkg/apis/device/v1alpha2/doc.go b/pkg/apis/device/v1alpha2/doc.go new file mode 100644 index 00000000000..76d580d0fa9 --- /dev/null +++ b/pkg/apis/device/v1alpha2/doc.go @@ -0,0 +1,17 @@ +/* +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. +*/ +// +groupName=device.openyurt.io +package v1alpha2 diff --git a/pkg/apis/device/v1alpha2/groupversion_info.go b/pkg/apis/device/v1alpha2/groupversion_info.go new file mode 100644 index 00000000000..94c61b1cc17 --- /dev/null +++ b/pkg/apis/device/v1alpha2/groupversion_info.go @@ -0,0 +1,44 @@ +/* +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 v1alpha2 + +// Package v1alpha2 contains API Schema definitions for the device v1alpha2API group +// +kubebuilder:object:generate=true +// +groupName=device.openyurt.io + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "device.openyurt.io", Version: "v1alpha2"} + + SchemeGroupVersion = GroupVersion + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource is required by pkg/client/listers/... +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/pkg/apis/device/v1alpha2/iot_conversion.go b/pkg/apis/device/v1alpha2/iot_conversion.go new file mode 100644 index 00000000000..9426509968d --- /dev/null +++ b/pkg/apis/device/v1alpha2/iot_conversion.go @@ -0,0 +1,20 @@ +/* +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 v1alpha2 + +// Hub marks this type as a conversion hub. +func (*IoT) Hub() {} diff --git a/pkg/apis/device/v1alpha2/iot_types.go b/pkg/apis/device/v1alpha2/iot_types.go new file mode 100644 index 00000000000..6d3a9d4b9c2 --- /dev/null +++ b/pkg/apis/device/v1alpha2/iot_types.go @@ -0,0 +1,141 @@ +/* +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 v1alpha2 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // name of finalizer + IoTFinalizer = "device.openyurt.io" + + LabelIoTGenerate = "device.openyurt.io/generate" +) + +// IoT platform supported by openyurt +const ( + IoTPlatformEdgeX = "edgex" +) + +// IoTConditionType indicates valid conditions type of a IoT. +type IoTConditionType string +type IoTConditionSeverity string + +// Component defines the components of EdgeX +type Component struct { + Name string `json:"name"` + + // +optional + Image string `json:"image,omitempty"` +} + +// IoTSpec defines the desired state of IoT +type IoTSpec struct { + Version string `json:"version,omitempty"` + + ImageRegistry string `json:"imageRegistry,omitempty"` + + PoolName string `json:"poolName,omitempty"` + + // +optional + Platform string `json:"platform,omitempty"` + + // +optional + Components []Component `json:"components,omitempty"` + + // +optional + Security bool `json:"security,omitempty"` +} + +// IoTStatus defines the observed state of IoT +type IoTStatus struct { + // +optional + Ready bool `json:"ready,omitempty"` + + // +optional + Initialized bool `json:"initialized,omitempty"` + + // +optional + ReadyComponentNum int32 `json:"readyComponentNum,omitempty"` + + // +optional + UnreadyComponentNum int32 `json:"unreadyComponentNum,omitempty"` + + // Current Edgex state + // +optional + Conditions []IoTCondition `json:"conditions,omitempty"` +} + +// IoTCondition describes current state of a IoT. +type IoTCondition struct { + // Type of in place set condition. + Type IoTConditionType `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"` +} + +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced,path=iots,shortName=iot,categories=all +// +kubebuilder:printcolumn:name="READY",type="boolean",JSONPath=".status.ready",description="The edgex ready status" +// +kubebuilder:printcolumn:name="ReadyComponentNum",type="integer",JSONPath=".status.readyComponentNum",description="The Ready Component." +// +kubebuilder:printcolumn:name="UnreadyComponentNum",type="integer",JSONPath=".status.unreadyComponentNum",description="The Unready Component." +// +kubebuilder:storageversion + +// IoT is the Schema for the samples API +type IoT struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec IoTSpec `json:"spec,omitempty"` + Status IoTStatus `json:"status,omitempty"` +} + +func (c *IoT) GetConditions() []IoTCondition { + return c.Status.Conditions +} + +func (c *IoT) SetConditions(conditions []IoTCondition) { + c.Status.Conditions = conditions +} + +//+kubebuilder:object:root=true + +// IoTList contains a list of IoT +type IoTList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []IoT `json:"items"` +} + +func init() { + SchemeBuilder.Register(&IoT{}, &IoTList{}) +} diff --git a/pkg/apis/device/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/device/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 00000000000..6706b67d47b --- /dev/null +++ b/pkg/apis/device/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,156 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +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 v1alpha2 + +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 *Component) DeepCopyInto(out *Component) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Component. +func (in *Component) DeepCopy() *Component { + if in == nil { + return nil + } + out := new(Component) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IoT) DeepCopyInto(out *IoT) { + *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 IoT. +func (in *IoT) DeepCopy() *IoT { + if in == nil { + return nil + } + out := new(IoT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IoT) 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 *IoTCondition) DeepCopyInto(out *IoTCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IoTCondition. +func (in *IoTCondition) DeepCopy() *IoTCondition { + if in == nil { + return nil + } + out := new(IoTCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IoTList) DeepCopyInto(out *IoTList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]IoT, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IoTList. +func (in *IoTList) DeepCopy() *IoTList { + if in == nil { + return nil + } + out := new(IoTList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IoTList) 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 *IoTSpec) DeepCopyInto(out *IoTSpec) { + *out = *in + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]Component, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IoTSpec. +func (in *IoTSpec) DeepCopy() *IoTSpec { + if in == nil { + return nil + } + out := new(IoTSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IoTStatus) DeepCopyInto(out *IoTStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]IoTCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IoTStatus. +func (in *IoTStatus) DeepCopy() *IoTStatus { + if in == nil { + return nil + } + out := new(IoTStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/controller/apis/config/types.go b/pkg/controller/apis/config/types.go index 00232ae1ba0..bb8e939a73a 100644 --- a/pkg/controller/apis/config/types.go +++ b/pkg/controller/apis/config/types.go @@ -19,6 +19,7 @@ package config import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + iotconfig "github.com/openyurtio/openyurt/pkg/controller/iot/config" nodepoolconfig "github.com/openyurtio/openyurt/pkg/controller/nodepool/config" gatewayconfig "github.com/openyurtio/openyurt/pkg/controller/raven/config" yurtappdaemonconfig "github.com/openyurtio/openyurt/pkg/controller/yurtappdaemon/config" @@ -44,6 +45,9 @@ type YurtManagerConfiguration struct { // YurtAppDaemonControllerConfiguration holds configuration for YurtAppDaemonController related features. YurtAppDaemonController yurtappdaemonconfig.YurtAppDaemonControllerConfiguration + + // IoTControllerConfiguration holds configuration for IoTController related features. + IoTController iotconfig.IoTControllerConfiguration } type GenericConfiguration struct { diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index abec0324a27..d66ff0cb175 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -24,6 +24,7 @@ import ( "github.com/openyurtio/openyurt/cmd/yurt-manager/app/config" "github.com/openyurtio/openyurt/pkg/controller/csrapprover" "github.com/openyurtio/openyurt/pkg/controller/daemonpodupdater" + "github.com/openyurtio/openyurt/pkg/controller/iot" "github.com/openyurtio/openyurt/pkg/controller/nodepool" poolcoordinatorcert "github.com/openyurtio/openyurt/pkg/controller/poolcoordinator/cert" "github.com/openyurtio/openyurt/pkg/controller/poolcoordinator/delegatelease" @@ -62,6 +63,7 @@ func init() { controllerAddFuncs[yurtstaticset.ControllerName] = []AddControllerFn{yurtstaticset.Add} controllerAddFuncs[yurtappset.ControllerName] = []AddControllerFn{yurtappset.Add} controllerAddFuncs[yurtappdaemon.ControllerName] = []AddControllerFn{yurtappdaemon.Add} + controllerAddFuncs[iot.ControllerName] = []AddControllerFn{iot.Add} } // If you want to add additional RBAC, enter it here !!! @kadisi diff --git a/pkg/controller/iot/config/EdgeXConfig/config-nosecty.json b/pkg/controller/iot/config/EdgeXConfig/config-nosecty.json new file mode 100644 index 00000000000..eff97d58ac7 --- /dev/null +++ b/pkg/controller/iot/config/EdgeXConfig/config-nosecty.json @@ -0,0 +1,4657 @@ +{ + "versions": [ + { + "versionName": "levski", + "configMaps": [ + { + "metadata": { + "name": "common-variable-levski", + "creationTimestamp": null + }, + "data": { + "CLIENTS_CORE_COMMAND_HOST": "edgex-core-command", + "CLIENTS_CORE_DATA_HOST": "edgex-core-data", + "CLIENTS_CORE_METADATA_HOST": "edgex-core-metadata", + "CLIENTS_SUPPORT_NOTIFICATIONS_HOST": "edgex-support-notifications", + "CLIENTS_SUPPORT_SCHEDULER_HOST": "edgex-support-scheduler", + "DATABASES_PRIMARY_HOST": "edgex-redis", + "EDGEX_SECURITY_SECRET_STORE": "false", + "MESSAGEQUEUE_HOST": "edgex-redis", + "REGISTRY_HOST": "edgex-core-consul" + } + } + ], + "components": [ + { + "name": "edgex-support-scheduler", + "service": { + "ports": [ + { + "name": "tcp-59861", + "protocol": "TCP", + "port": 59861, + "targetPort": 59861 + } + ], + "selector": { + "app": "edgex-support-scheduler" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-scheduler" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-scheduler" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-scheduler", + "image": "openyurt/support-scheduler:2.3.0", + "ports": [ + { + "name": "tcp-59861", + "containerPort": 59861, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "INTERVALACTIONS_SCRUBPUSHED_HOST", + "value": "edgex-core-data" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-support-scheduler" + }, + { + "name": "INTERVALACTIONS_SCRUBAGED_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-scheduler" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-app-rules-engine", + "service": { + "ports": [ + { + "name": "tcp-59701", + "protocol": "TCP", + "port": 59701, + "targetPort": 59701 + } + ], + "selector": { + "app": "edgex-app-rules-engine" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-app-rules-engine" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-app-rules-engine" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-app-rules-engine", + "image": "openyurt/app-service-configurable:2.3.0", + "ports": [ + { + "name": "tcp-59701", + "containerPort": 59701, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "TRIGGER_EDGEXMESSAGEBUS_SUBSCRIBEHOST_HOST", + "value": "edgex-redis" + }, + { + "name": "EDGEX_PROFILE", + "value": "rules-engine" + }, + { + "name": "TRIGGER_EDGEXMESSAGEBUS_PUBLISHHOST_HOST", + "value": "edgex-redis" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-app-rules-engine" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-app-rules-engine" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-metadata", + "service": { + "ports": [ + { + "name": "tcp-59881", + "protocol": "TCP", + "port": 59881, + "targetPort": 59881 + } + ], + "selector": { + "app": "edgex-core-metadata" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-metadata" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-metadata" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-metadata", + "image": "openyurt/core-metadata:2.3.0", + "ports": [ + { + "name": "tcp-59881", + "containerPort": 59881, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-metadata" + }, + { + "name": "NOTIFICATIONS_SENDER", + "value": "edgex-core-metadata" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-metadata" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-consul", + "service": { + "ports": [ + { + "name": "tcp-8500", + "protocol": "TCP", + "port": 8500, + "targetPort": 8500 + } + ], + "selector": { + "app": "edgex-core-consul" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-consul" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-consul" + } + }, + "spec": { + "volumes": [ + { + "name": "consul-config", + "emptyDir": {} + }, + { + "name": "consul-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-core-consul", + "image": "openyurt/consul:1.13.2", + "ports": [ + { + "name": "tcp-8500", + "containerPort": 8500, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "consul-config", + "mountPath": "/consul/config" + }, + { + "name": "consul-data", + "mountPath": "/consul/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-consul" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-data", + "service": { + "ports": [ + { + "name": "tcp-5563", + "protocol": "TCP", + "port": 5563, + "targetPort": 5563 + }, + { + "name": "tcp-59880", + "protocol": "TCP", + "port": 59880, + "targetPort": 59880 + } + ], + "selector": { + "app": "edgex-core-data" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-data" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-data" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-data", + "image": "openyurt/core-data:2.3.0", + "ports": [ + { + "name": "tcp-5563", + "containerPort": 5563, + "protocol": "TCP" + }, + { + "name": "tcp-59880", + "containerPort": 59880, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-data" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-command", + "service": { + "ports": [ + { + "name": "tcp-59882", + "protocol": "TCP", + "port": 59882, + "targetPort": 59882 + } + ], + "selector": { + "app": "edgex-core-command" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-command" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-command" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-command", + "image": "openyurt/core-command:2.3.0", + "ports": [ + { + "name": "tcp-59882", + "containerPort": 59882, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-command" + }, + { + "name": "MESSAGEQUEUE_EXTERNAL_URL", + "value": "tcp://edgex-mqtt-broker:1883" + }, + { + "name": "MESSAGEQUEUE_INTERNAL_HOST", + "value": "edgex-redis" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-command" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-ui-go", + "service": { + "ports": [ + { + "name": "tcp-4000", + "protocol": "TCP", + "port": 4000, + "targetPort": 4000 + } + ], + "selector": { + "app": "edgex-ui-go" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-ui-go" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-ui-go" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-ui-go", + "image": "openyurt/edgex-ui:2.3.0", + "ports": [ + { + "name": "tcp-4000", + "containerPort": 4000, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-ui-go" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-ui-go" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-sys-mgmt-agent", + "service": { + "ports": [ + { + "name": "tcp-58890", + "protocol": "TCP", + "port": 58890, + "targetPort": 58890 + } + ], + "selector": { + "app": "edgex-sys-mgmt-agent" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-sys-mgmt-agent", + "image": "openyurt/sys-mgmt-agent:2.3.0", + "ports": [ + { + "name": "tcp-58890", + "containerPort": 58890, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-sys-mgmt-agent" + }, + { + "name": "EXECUTORPATH", + "value": "/sys-mgmt-executor" + }, + { + "name": "METRICSMECHANISM", + "value": "executor" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-sys-mgmt-agent" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-rest", + "service": { + "ports": [ + { + "name": "tcp-59986", + "protocol": "TCP", + "port": 59986, + "targetPort": 59986 + } + ], + "selector": { + "app": "edgex-device-rest" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-rest" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-rest" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-rest", + "image": "openyurt/device-rest:2.3.0", + "ports": [ + { + "name": "tcp-59986", + "containerPort": 59986, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-rest" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-rest" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-virtual", + "service": { + "ports": [ + { + "name": "tcp-59900", + "protocol": "TCP", + "port": 59900, + "targetPort": 59900 + } + ], + "selector": { + "app": "edgex-device-virtual" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-virtual" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-virtual" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-virtual", + "image": "openyurt/device-virtual:2.3.0", + "ports": [ + { + "name": "tcp-59900", + "containerPort": 59900, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-virtual" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-virtual" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-support-notifications", + "service": { + "ports": [ + { + "name": "tcp-59860", + "protocol": "TCP", + "port": 59860, + "targetPort": 59860 + } + ], + "selector": { + "app": "edgex-support-notifications" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-notifications" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-notifications" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-notifications", + "image": "openyurt/support-notifications:2.3.0", + "ports": [ + { + "name": "tcp-59860", + "containerPort": 59860, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-support-notifications" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-notifications" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-redis", + "service": { + "ports": [ + { + "name": "tcp-6379", + "protocol": "TCP", + "port": 6379, + "targetPort": 6379 + } + ], + "selector": { + "app": "edgex-redis" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-redis" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-redis" + } + }, + "spec": { + "volumes": [ + { + "name": "db-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-redis", + "image": "openyurt/redis:7.0.5-alpine", + "ports": [ + { + "name": "tcp-6379", + "containerPort": 6379, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "db-data", + "mountPath": "/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-redis" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-kuiper", + "service": { + "ports": [ + { + "name": "tcp-59720", + "protocol": "TCP", + "port": 59720, + "targetPort": 59720 + } + ], + "selector": { + "app": "edgex-kuiper" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-kuiper" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-kuiper" + } + }, + "spec": { + "volumes": [ + { + "name": "kuiper-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-kuiper", + "image": "openyurt/ekuiper:1.7.1-alpine", + "ports": [ + { + "name": "tcp-59720", + "containerPort": 59720, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-levski" + } + } + ], + "env": [ + { + "name": "EDGEX__DEFAULT__PROTOCOL", + "value": "redis" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__TYPE", + "value": "redis" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__PROTOCOL", + "value": "redis" + }, + { + "name": "KUIPER__BASIC__RESTPORT", + "value": "59720" + }, + { + "name": "EDGEX__DEFAULT__PORT", + "value": "6379" + }, + { + "name": "KUIPER__BASIC__CONSOLELOG", + "value": "true" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__SERVER", + "value": "edgex-redis" + }, + { + "name": "EDGEX__DEFAULT__TOPIC", + "value": "rules-events" + }, + { + "name": "EDGEX__DEFAULT__SERVER", + "value": "edgex-redis" + }, + { + "name": "EDGEX__DEFAULT__TYPE", + "value": "redis" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__PORT", + "value": "6379" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "kuiper-data", + "mountPath": "/kuiper/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-kuiper" + } + }, + "strategy": {} + } + } + ] + }, + { + "versionName": "jakarta", + "configMaps": [ + { + "metadata": { + "name": "common-variable-jakarta", + "creationTimestamp": null + }, + "data": { + "CLIENTS_CORE_COMMAND_HOST": "edgex-core-command", + "CLIENTS_CORE_DATA_HOST": "edgex-core-data", + "CLIENTS_CORE_METADATA_HOST": "edgex-core-metadata", + "CLIENTS_SUPPORT_NOTIFICATIONS_HOST": "edgex-support-notifications", + "CLIENTS_SUPPORT_SCHEDULER_HOST": "edgex-support-scheduler", + "DATABASES_PRIMARY_HOST": "edgex-redis", + "EDGEX_SECURITY_SECRET_STORE": "false", + "MESSAGEQUEUE_HOST": "edgex-redis", + "REGISTRY_HOST": "edgex-core-consul" + } + } + ], + "components": [ + { + "name": "edgex-support-notifications", + "service": { + "ports": [ + { + "name": "tcp-59860", + "protocol": "TCP", + "port": 59860, + "targetPort": 59860 + } + ], + "selector": { + "app": "edgex-support-notifications" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-notifications" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-notifications" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-notifications", + "image": "openyurt/support-notifications:2.1.1", + "ports": [ + { + "name": "tcp-59860", + "containerPort": 59860, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-support-notifications" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-notifications" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-sys-mgmt-agent", + "service": { + "ports": [ + { + "name": "tcp-58890", + "protocol": "TCP", + "port": 58890, + "targetPort": 58890 + } + ], + "selector": { + "app": "edgex-sys-mgmt-agent" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-sys-mgmt-agent", + "image": "openyurt/sys-mgmt-agent:2.1.1", + "ports": [ + { + "name": "tcp-58890", + "containerPort": 58890, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "METRICSMECHANISM", + "value": "executor" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-sys-mgmt-agent" + }, + { + "name": "EXECUTORPATH", + "value": "/sys-mgmt-executor" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-sys-mgmt-agent" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-rest", + "service": { + "ports": [ + { + "name": "tcp-59986", + "protocol": "TCP", + "port": 59986, + "targetPort": 59986 + } + ], + "selector": { + "app": "edgex-device-rest" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-rest" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-rest" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-rest", + "image": "openyurt/device-rest:2.1.1", + "ports": [ + { + "name": "tcp-59986", + "containerPort": 59986, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-rest" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-rest" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-virtual", + "service": { + "ports": [ + { + "name": "tcp-59900", + "protocol": "TCP", + "port": 59900, + "targetPort": 59900 + } + ], + "selector": { + "app": "edgex-device-virtual" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-virtual" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-virtual" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-virtual", + "image": "openyurt/device-virtual:2.1.1", + "ports": [ + { + "name": "tcp-59900", + "containerPort": 59900, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-virtual" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-virtual" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-metadata", + "service": { + "ports": [ + { + "name": "tcp-59881", + "protocol": "TCP", + "port": 59881, + "targetPort": 59881 + } + ], + "selector": { + "app": "edgex-core-metadata" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-metadata" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-metadata" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-metadata", + "image": "openyurt/core-metadata:2.1.1", + "ports": [ + { + "name": "tcp-59881", + "containerPort": 59881, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "NOTIFICATIONS_SENDER", + "value": "edgex-core-metadata" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-core-metadata" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-metadata" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-app-rules-engine", + "service": { + "ports": [ + { + "name": "tcp-59701", + "protocol": "TCP", + "port": 59701, + "targetPort": 59701 + } + ], + "selector": { + "app": "edgex-app-rules-engine" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-app-rules-engine" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-app-rules-engine" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-app-rules-engine", + "image": "openyurt/app-service-configurable:2.1.1", + "ports": [ + { + "name": "tcp-59701", + "containerPort": 59701, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "TRIGGER_EDGEXMESSAGEBUS_PUBLISHHOST_HOST", + "value": "edgex-redis" + }, + { + "name": "TRIGGER_EDGEXMESSAGEBUS_SUBSCRIBEHOST_HOST", + "value": "edgex-redis" + }, + { + "name": "EDGEX_PROFILE", + "value": "rules-engine" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-app-rules-engine" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-app-rules-engine" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-redis", + "service": { + "ports": [ + { + "name": "tcp-6379", + "protocol": "TCP", + "port": 6379, + "targetPort": 6379 + } + ], + "selector": { + "app": "edgex-redis" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-redis" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-redis" + } + }, + "spec": { + "volumes": [ + { + "name": "db-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-redis", + "image": "openyurt/redis:6.2.6-alpine", + "ports": [ + { + "name": "tcp-6379", + "containerPort": 6379, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "db-data", + "mountPath": "/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-redis" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-command", + "service": { + "ports": [ + { + "name": "tcp-59882", + "protocol": "TCP", + "port": 59882, + "targetPort": 59882 + } + ], + "selector": { + "app": "edgex-core-command" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-command" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-command" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-command", + "image": "openyurt/core-command:2.1.1", + "ports": [ + { + "name": "tcp-59882", + "containerPort": 59882, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-command" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-command" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-data", + "service": { + "ports": [ + { + "name": "tcp-5563", + "protocol": "TCP", + "port": 5563, + "targetPort": 5563 + }, + { + "name": "tcp-59880", + "protocol": "TCP", + "port": 59880, + "targetPort": 59880 + } + ], + "selector": { + "app": "edgex-core-data" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-data" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-data" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-data", + "image": "openyurt/core-data:2.1.1", + "ports": [ + { + "name": "tcp-5563", + "containerPort": 5563, + "protocol": "TCP" + }, + { + "name": "tcp-59880", + "containerPort": 59880, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-data" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-support-scheduler", + "service": { + "ports": [ + { + "name": "tcp-59861", + "protocol": "TCP", + "port": 59861, + "targetPort": 59861 + } + ], + "selector": { + "app": "edgex-support-scheduler" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-scheduler" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-scheduler" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-scheduler", + "image": "openyurt/support-scheduler:2.1.1", + "ports": [ + { + "name": "tcp-59861", + "containerPort": 59861, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-support-scheduler" + }, + { + "name": "INTERVALACTIONS_SCRUBPUSHED_HOST", + "value": "edgex-core-data" + }, + { + "name": "INTERVALACTIONS_SCRUBAGED_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-scheduler" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-kuiper", + "service": { + "ports": [ + { + "name": "tcp-59720", + "protocol": "TCP", + "port": 59720, + "targetPort": 59720 + } + ], + "selector": { + "app": "edgex-kuiper" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-kuiper" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-kuiper" + } + }, + "spec": { + "volumes": [ + { + "name": "kuiper-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-kuiper", + "image": "openyurt/ekuiper:1.4.4-alpine", + "ports": [ + { + "name": "tcp-59720", + "containerPort": 59720, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "env": [ + { + "name": "EDGEX__DEFAULT__TOPIC", + "value": "rules-events" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__TYPE", + "value": "redis" + }, + { + "name": "EDGEX__DEFAULT__PROTOCOL", + "value": "redis" + }, + { + "name": "EDGEX__DEFAULT__PORT", + "value": "6379" + }, + { + "name": "KUIPER__BASIC__RESTPORT", + "value": "59720" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__PROTOCOL", + "value": "redis" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__PORT", + "value": "6379" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__SERVER", + "value": "edgex-redis" + }, + { + "name": "EDGEX__DEFAULT__TYPE", + "value": "redis" + }, + { + "name": "EDGEX__DEFAULT__SERVER", + "value": "edgex-redis" + }, + { + "name": "KUIPER__BASIC__CONSOLELOG", + "value": "true" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "kuiper-data", + "mountPath": "/kuiper/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-kuiper" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-ui-go", + "service": { + "ports": [ + { + "name": "tcp-4000", + "protocol": "TCP", + "port": 4000, + "targetPort": 4000 + } + ], + "selector": { + "app": "edgex-ui-go" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-ui-go" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-ui-go" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-ui-go", + "image": "openyurt/edgex-ui:2.1.0", + "ports": [ + { + "name": "tcp-4000", + "containerPort": 4000, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-ui-go" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-consul", + "service": { + "ports": [ + { + "name": "tcp-8500", + "protocol": "TCP", + "port": 8500, + "targetPort": 8500 + } + ], + "selector": { + "app": "edgex-core-consul" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-consul" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-consul" + } + }, + "spec": { + "volumes": [ + { + "name": "consul-config", + "emptyDir": {} + }, + { + "name": "consul-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-core-consul", + "image": "openyurt/consul:1.10.3", + "ports": [ + { + "name": "tcp-8500", + "containerPort": 8500, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-jakarta" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "consul-config", + "mountPath": "/consul/config" + }, + { + "name": "consul-data", + "mountPath": "/consul/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-consul" + } + }, + "strategy": {} + } + } + ] + }, + { + "versionName": "kamakura", + "configMaps": [ + { + "metadata": { + "name": "common-variable-kamakura", + "creationTimestamp": null + }, + "data": { + "CLIENTS_CORE_COMMAND_HOST": "edgex-core-command", + "CLIENTS_CORE_DATA_HOST": "edgex-core-data", + "CLIENTS_CORE_METADATA_HOST": "edgex-core-metadata", + "CLIENTS_SUPPORT_NOTIFICATIONS_HOST": "edgex-support-notifications", + "CLIENTS_SUPPORT_SCHEDULER_HOST": "edgex-support-scheduler", + "DATABASES_PRIMARY_HOST": "edgex-redis", + "EDGEX_SECURITY_SECRET_STORE": "false", + "MESSAGEQUEUE_HOST": "edgex-redis", + "REGISTRY_HOST": "edgex-core-consul" + } + } + ], + "components": [ + { + "name": "edgex-core-command", + "service": { + "ports": [ + { + "name": "tcp-59882", + "protocol": "TCP", + "port": 59882, + "targetPort": 59882 + } + ], + "selector": { + "app": "edgex-core-command" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-command" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-command" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-command", + "image": "openyurt/core-command:2.2.0", + "ports": [ + { + "name": "tcp-59882", + "containerPort": 59882, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-command" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-command" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-app-rules-engine", + "service": { + "ports": [ + { + "name": "tcp-59701", + "protocol": "TCP", + "port": 59701, + "targetPort": 59701 + } + ], + "selector": { + "app": "edgex-app-rules-engine" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-app-rules-engine" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-app-rules-engine" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-app-rules-engine", + "image": "openyurt/app-service-configurable:2.2.0", + "ports": [ + { + "name": "tcp-59701", + "containerPort": 59701, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "TRIGGER_EDGEXMESSAGEBUS_PUBLISHHOST_HOST", + "value": "edgex-redis" + }, + { + "name": "EDGEX_PROFILE", + "value": "rules-engine" + }, + { + "name": "TRIGGER_EDGEXMESSAGEBUS_SUBSCRIBEHOST_HOST", + "value": "edgex-redis" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-app-rules-engine" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-app-rules-engine" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-virtual", + "service": { + "ports": [ + { + "name": "tcp-59900", + "protocol": "TCP", + "port": 59900, + "targetPort": 59900 + } + ], + "selector": { + "app": "edgex-device-virtual" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-virtual" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-virtual" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-virtual", + "image": "openyurt/device-virtual:2.2.0", + "ports": [ + { + "name": "tcp-59900", + "containerPort": 59900, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-virtual" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-virtual" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-support-notifications", + "service": { + "ports": [ + { + "name": "tcp-59860", + "protocol": "TCP", + "port": 59860, + "targetPort": 59860 + } + ], + "selector": { + "app": "edgex-support-notifications" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-notifications" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-notifications" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-notifications", + "image": "openyurt/support-notifications:2.2.0", + "ports": [ + { + "name": "tcp-59860", + "containerPort": 59860, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-support-notifications" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-notifications" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-metadata", + "service": { + "ports": [ + { + "name": "tcp-59881", + "protocol": "TCP", + "port": 59881, + "targetPort": 59881 + } + ], + "selector": { + "app": "edgex-core-metadata" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-metadata" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-metadata" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-metadata", + "image": "openyurt/core-metadata:2.2.0", + "ports": [ + { + "name": "tcp-59881", + "containerPort": 59881, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-metadata" + }, + { + "name": "NOTIFICATIONS_SENDER", + "value": "edgex-core-metadata" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-metadata" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-kuiper", + "service": { + "ports": [ + { + "name": "tcp-59720", + "protocol": "TCP", + "port": 59720, + "targetPort": 59720 + } + ], + "selector": { + "app": "edgex-kuiper" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-kuiper" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-kuiper" + } + }, + "spec": { + "volumes": [ + { + "name": "kuiper-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-kuiper", + "image": "openyurt/ekuiper:1.4.4-alpine", + "ports": [ + { + "name": "tcp-59720", + "containerPort": 59720, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__TYPE", + "value": "redis" + }, + { + "name": "EDGEX__DEFAULT__TYPE", + "value": "redis" + }, + { + "name": "EDGEX__DEFAULT__TOPIC", + "value": "rules-events" + }, + { + "name": "KUIPER__BASIC__RESTPORT", + "value": "59720" + }, + { + "name": "EDGEX__DEFAULT__SERVER", + "value": "edgex-redis" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__PROTOCOL", + "value": "redis" + }, + { + "name": "KUIPER__BASIC__CONSOLELOG", + "value": "true" + }, + { + "name": "EDGEX__DEFAULT__PORT", + "value": "6379" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__PORT", + "value": "6379" + }, + { + "name": "CONNECTION__EDGEX__REDISMSGBUS__SERVER", + "value": "edgex-redis" + }, + { + "name": "EDGEX__DEFAULT__PROTOCOL", + "value": "redis" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "kuiper-data", + "mountPath": "/kuiper/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-kuiper" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-sys-mgmt-agent", + "service": { + "ports": [ + { + "name": "tcp-58890", + "protocol": "TCP", + "port": 58890, + "targetPort": 58890 + } + ], + "selector": { + "app": "edgex-sys-mgmt-agent" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-sys-mgmt-agent", + "image": "openyurt/sys-mgmt-agent:2.2.0", + "ports": [ + { + "name": "tcp-58890", + "containerPort": 58890, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "EXECUTORPATH", + "value": "/sys-mgmt-executor" + }, + { + "name": "METRICSMECHANISM", + "value": "executor" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-sys-mgmt-agent" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-sys-mgmt-agent" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-ui-go", + "service": { + "ports": [ + { + "name": "tcp-4000", + "protocol": "TCP", + "port": 4000, + "targetPort": 4000 + } + ], + "selector": { + "app": "edgex-ui-go" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-ui-go" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-ui-go" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-ui-go", + "image": "openyurt/edgex-ui:2.2.0", + "ports": [ + { + "name": "tcp-4000", + "containerPort": 4000, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-ui-go" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-data", + "service": { + "ports": [ + { + "name": "tcp-5563", + "protocol": "TCP", + "port": 5563, + "targetPort": 5563 + }, + { + "name": "tcp-59880", + "protocol": "TCP", + "port": 59880, + "targetPort": 59880 + } + ], + "selector": { + "app": "edgex-core-data" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-data" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-data" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-data", + "image": "openyurt/core-data:2.2.0", + "ports": [ + { + "name": "tcp-5563", + "containerPort": 5563, + "protocol": "TCP" + }, + { + "name": "tcp-59880", + "containerPort": 59880, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-data" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-support-scheduler", + "service": { + "ports": [ + { + "name": "tcp-59861", + "protocol": "TCP", + "port": 59861, + "targetPort": 59861 + } + ], + "selector": { + "app": "edgex-support-scheduler" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-scheduler" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-scheduler" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-scheduler", + "image": "openyurt/support-scheduler:2.2.0", + "ports": [ + { + "name": "tcp-59861", + "containerPort": 59861, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "INTERVALACTIONS_SCRUBPUSHED_HOST", + "value": "edgex-core-data" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-support-scheduler" + }, + { + "name": "INTERVALACTIONS_SCRUBAGED_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-scheduler" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-redis", + "service": { + "ports": [ + { + "name": "tcp-6379", + "protocol": "TCP", + "port": 6379, + "targetPort": 6379 + } + ], + "selector": { + "app": "edgex-redis" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-redis" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-redis" + } + }, + "spec": { + "volumes": [ + { + "name": "db-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-redis", + "image": "openyurt/redis:6.2.6-alpine", + "ports": [ + { + "name": "tcp-6379", + "containerPort": 6379, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "db-data", + "mountPath": "/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-redis" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-rest", + "service": { + "ports": [ + { + "name": "tcp-59986", + "protocol": "TCP", + "port": 59986, + "targetPort": 59986 + } + ], + "selector": { + "app": "edgex-device-rest" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-rest" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-rest" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-rest", + "image": "openyurt/device-rest:2.2.0", + "ports": [ + { + "name": "tcp-59986", + "containerPort": 59986, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-rest" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-rest" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-consul", + "service": { + "ports": [ + { + "name": "tcp-8500", + "protocol": "TCP", + "port": 8500, + "targetPort": 8500 + } + ], + "selector": { + "app": "edgex-core-consul" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-consul" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-consul" + } + }, + "spec": { + "volumes": [ + { + "name": "consul-config", + "emptyDir": {} + }, + { + "name": "consul-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-core-consul", + "image": "openyurt/consul:1.10.10", + "ports": [ + { + "name": "tcp-8500", + "containerPort": 8500, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-kamakura" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "consul-config", + "mountPath": "/consul/config" + }, + { + "name": "consul-data", + "mountPath": "/consul/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-consul" + } + }, + "strategy": {} + } + } + ] + }, + { + "versionName": "ireland", + "configMaps": [ + { + "metadata": { + "name": "common-variable-ireland", + "creationTimestamp": null + }, + "data": { + "CLIENTS_CORE_COMMAND_HOST": "edgex-core-command", + "CLIENTS_CORE_DATA_HOST": "edgex-core-data", + "CLIENTS_CORE_METADATA_HOST": "edgex-core-metadata", + "CLIENTS_SUPPORT_NOTIFICATIONS_HOST": "edgex-support-notifications", + "CLIENTS_SUPPORT_SCHEDULER_HOST": "edgex-support-scheduler", + "DATABASES_PRIMARY_HOST": "edgex-redis", + "EDGEX_SECURITY_SECRET_STORE": "false", + "MESSAGEQUEUE_HOST": "edgex-redis", + "REGISTRY_HOST": "edgex-core-consul" + } + } + ], + "components": [ + { + "name": "edgex-core-command", + "service": { + "ports": [ + { + "name": "tcp-59882", + "protocol": "TCP", + "port": 59882, + "targetPort": 59882 + } + ], + "selector": { + "app": "edgex-core-command" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-command" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-command" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-command", + "image": "openyurt/core-command:2.0.0", + "ports": [ + { + "name": "tcp-59882", + "containerPort": 59882, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-command" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-command" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-sys-mgmt-agent", + "service": { + "ports": [ + { + "name": "tcp-58890", + "protocol": "TCP", + "port": 58890, + "targetPort": 58890 + } + ], + "selector": { + "app": "edgex-sys-mgmt-agent" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-sys-mgmt-agent", + "image": "openyurt/sys-mgmt-agent:2.0.0", + "ports": [ + { + "name": "tcp-58890", + "containerPort": 58890, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "METRICSMECHANISM", + "value": "executor" + }, + { + "name": "EXECUTORPATH", + "value": "/sys-mgmt-executor" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-sys-mgmt-agent" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-sys-mgmt-agent" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-support-notifications", + "service": { + "ports": [ + { + "name": "tcp-59860", + "protocol": "TCP", + "port": 59860, + "targetPort": 59860 + } + ], + "selector": { + "app": "edgex-support-notifications" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-notifications" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-notifications" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-notifications", + "image": "openyurt/support-notifications:2.0.0", + "ports": [ + { + "name": "tcp-59860", + "containerPort": 59860, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-support-notifications" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-notifications" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-support-scheduler", + "service": { + "ports": [ + { + "name": "tcp-59861", + "protocol": "TCP", + "port": 59861, + "targetPort": 59861 + } + ], + "selector": { + "app": "edgex-support-scheduler" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-scheduler" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-scheduler" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-scheduler", + "image": "openyurt/support-scheduler:2.0.0", + "ports": [ + { + "name": "tcp-59861", + "containerPort": 59861, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-support-scheduler" + }, + { + "name": "INTERVALACTIONS_SCRUBAGED_HOST", + "value": "edgex-core-data" + }, + { + "name": "INTERVALACTIONS_SCRUBPUSHED_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-scheduler" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-consul", + "service": { + "ports": [ + { + "name": "tcp-8500", + "protocol": "TCP", + "port": 8500, + "targetPort": 8500 + } + ], + "selector": { + "app": "edgex-core-consul" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-consul" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-consul" + } + }, + "spec": { + "volumes": [ + { + "name": "consul-config", + "emptyDir": {} + }, + { + "name": "consul-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-core-consul", + "image": "openyurt/consul:1.9.5", + "ports": [ + { + "name": "tcp-8500", + "containerPort": 8500, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "consul-config", + "mountPath": "/consul/config" + }, + { + "name": "consul-data", + "mountPath": "/consul/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-consul" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-kuiper", + "service": { + "ports": [ + { + "name": "tcp-59720", + "protocol": "TCP", + "port": 59720, + "targetPort": 59720 + } + ], + "selector": { + "app": "edgex-kuiper" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-kuiper" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-kuiper" + } + }, + "spec": { + "volumes": [ + { + "name": "kuiper-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-kuiper", + "image": "openyurt/ekuiper:1.3.0-alpine", + "ports": [ + { + "name": "tcp-59720", + "containerPort": 59720, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "KUIPER__BASIC__RESTPORT", + "value": "59720" + }, + { + "name": "EDGEX__DEFAULT__PORT", + "value": "6379" + }, + { + "name": "EDGEX__DEFAULT__PROTOCOL", + "value": "redis" + }, + { + "name": "EDGEX__DEFAULT__SERVER", + "value": "edgex-redis" + }, + { + "name": "EDGEX__DEFAULT__TOPIC", + "value": "rules-events" + }, + { + "name": "EDGEX__DEFAULT__TYPE", + "value": "redis" + }, + { + "name": "KUIPER__BASIC__CONSOLELOG", + "value": "true" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "kuiper-data", + "mountPath": "/kuiper/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-kuiper" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-redis", + "service": { + "ports": [ + { + "name": "tcp-6379", + "protocol": "TCP", + "port": 6379, + "targetPort": 6379 + } + ], + "selector": { + "app": "edgex-redis" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-redis" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-redis" + } + }, + "spec": { + "volumes": [ + { + "name": "db-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-redis", + "image": "openyurt/redis:6.2.4-alpine", + "ports": [ + { + "name": "tcp-6379", + "containerPort": 6379, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "db-data", + "mountPath": "/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-redis" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-app-rules-engine", + "service": { + "ports": [ + { + "name": "tcp-59701", + "protocol": "TCP", + "port": 59701, + "targetPort": 59701 + } + ], + "selector": { + "app": "edgex-app-rules-engine" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-app-rules-engine" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-app-rules-engine" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-app-rules-engine", + "image": "openyurt/app-service-configurable:2.0.1", + "ports": [ + { + "name": "tcp-59701", + "containerPort": 59701, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "TRIGGER_EDGEXMESSAGEBUS_SUBSCRIBEHOST_HOST", + "value": "edgex-redis" + }, + { + "name": "EDGEX_PROFILE", + "value": "rules-engine" + }, + { + "name": "TRIGGER_EDGEXMESSAGEBUS_PUBLISHHOST_HOST", + "value": "edgex-redis" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-app-rules-engine" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-app-rules-engine" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-rest", + "service": { + "ports": [ + { + "name": "tcp-59986", + "protocol": "TCP", + "port": 59986, + "targetPort": 59986 + } + ], + "selector": { + "app": "edgex-device-rest" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-rest" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-rest" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-rest", + "image": "openyurt/device-rest:2.0.0", + "ports": [ + { + "name": "tcp-59986", + "containerPort": 59986, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-rest" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-rest" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-virtual", + "service": { + "ports": [ + { + "name": "tcp-59900", + "protocol": "TCP", + "port": 59900, + "targetPort": 59900 + } + ], + "selector": { + "app": "edgex-device-virtual" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-virtual" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-virtual" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-virtual", + "image": "openyurt/device-virtual:2.0.0", + "ports": [ + { + "name": "tcp-59900", + "containerPort": 59900, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-virtual" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-virtual" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-metadata", + "service": { + "ports": [ + { + "name": "tcp-59881", + "protocol": "TCP", + "port": 59881, + "targetPort": 59881 + } + ], + "selector": { + "app": "edgex-core-metadata" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-metadata" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-metadata" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-metadata", + "image": "openyurt/core-metadata:2.0.0", + "ports": [ + { + "name": "tcp-59881", + "containerPort": 59881, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-metadata" + }, + { + "name": "NOTIFICATIONS_SENDER", + "value": "edgex-core-metadata" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-metadata" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-data", + "service": { + "ports": [ + { + "name": "tcp-5563", + "protocol": "TCP", + "port": 5563, + "targetPort": 5563 + }, + { + "name": "tcp-59880", + "protocol": "TCP", + "port": 59880, + "targetPort": 59880 + } + ], + "selector": { + "app": "edgex-core-data" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-data" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-data" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-data", + "image": "openyurt/core-data:2.0.0", + "ports": [ + { + "name": "tcp-5563", + "containerPort": 5563, + "protocol": "TCP" + }, + { + "name": "tcp-59880", + "containerPort": 59880, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-ireland" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-data" + } + }, + "strategy": {} + } + } + ] + }, + { + "versionName": "hanoi", + "configMaps": [ + { + "metadata": { + "name": "common-variable-hanoi", + "creationTimestamp": null + }, + "data": { + "CLIENTS_COMMAND_HOST": "edgex-core-command", + "CLIENTS_COREDATA_HOST": "edgex-core-data", + "CLIENTS_DATA_HOST": "edgex-core-data", + "CLIENTS_METADATA_HOST": "edgex-core-metadata", + "CLIENTS_NOTIFICATIONS_HOST": "edgex-support-notifications", + "CLIENTS_RULESENGINE_HOST": "edgex-kuiper", + "CLIENTS_SCHEDULER_HOST": "edgex-support-scheduler", + "CLIENTS_VIRTUALDEVICE_HOST": "edgex-device-virtual", + "DATABASES_PRIMARY_HOST": "edgex-redis", + "EDGEX_SECURITY_SECRET_STORE": "false", + "LOGGING_ENABLEREMOTE": "false", + "REGISTRY_HOST": "edgex-core-consul", + "SERVICE_SERVERBINDADDR": "0.0.0.0" + } + } + ], + "components": [ + { + "name": "edgex-redis", + "service": { + "ports": [ + { + "name": "tcp-6379", + "protocol": "TCP", + "port": 6379, + "targetPort": 6379 + } + ], + "selector": { + "app": "edgex-redis" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-redis" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-redis" + } + }, + "spec": { + "volumes": [ + { + "name": "db-data", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-redis", + "image": "openyurt/redis:6.0.9-alpine", + "ports": [ + { + "name": "tcp-6379", + "containerPort": 6379, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "db-data", + "mountPath": "/data" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-redis" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-support-scheduler", + "service": { + "ports": [ + { + "name": "tcp-48085", + "protocol": "TCP", + "port": 48085, + "targetPort": 48085 + } + ], + "selector": { + "app": "edgex-support-scheduler" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-scheduler" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-scheduler" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-scheduler", + "image": "openyurt/docker-support-scheduler-go:1.3.1", + "ports": [ + { + "name": "tcp-48085", + "containerPort": 48085, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-support-scheduler" + }, + { + "name": "INTERVALACTIONS_SCRUBAGED_HOST", + "value": "edgex-core-data" + }, + { + "name": "INTERVALACTIONS_SCRUBPUSHED_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-scheduler" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-consul", + "service": { + "ports": [ + { + "name": "tcp-8500", + "protocol": "TCP", + "port": 8500, + "targetPort": 8500 + } + ], + "selector": { + "app": "edgex-core-consul" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-consul" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-consul" + } + }, + "spec": { + "volumes": [ + { + "name": "consul-config", + "emptyDir": {} + }, + { + "name": "consul-data", + "emptyDir": {} + }, + { + "name": "consul-scripts", + "emptyDir": {} + } + ], + "containers": [ + { + "name": "edgex-core-consul", + "image": "openyurt/docker-edgex-consul:1.3.0", + "ports": [ + { + "name": "tcp-8500", + "containerPort": 8500, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "EDGEX_DB", + "value": "redis" + }, + { + "name": "EDGEX_SECURE", + "value": "false" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "consul-config", + "mountPath": "/consul/config" + }, + { + "name": "consul-data", + "mountPath": "/consul/data" + }, + { + "name": "consul-scripts", + "mountPath": "/consul/scripts" + } + ], + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-consul" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-kuiper", + "service": { + "ports": [ + { + "name": "tcp-20498", + "protocol": "TCP", + "port": 20498, + "targetPort": 20498 + }, + { + "name": "tcp-48075", + "protocol": "TCP", + "port": 48075, + "targetPort": 48075 + } + ], + "selector": { + "app": "edgex-kuiper" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-kuiper" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-kuiper" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-kuiper", + "image": "openyurt/kuiper:1.1.1-alpine", + "ports": [ + { + "name": "tcp-20498", + "containerPort": 20498, + "protocol": "TCP" + }, + { + "name": "tcp-48075", + "containerPort": 48075, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "KUIPER__BASIC__CONSOLELOG", + "value": "true" + }, + { + "name": "KUIPER__BASIC__RESTPORT", + "value": "48075" + }, + { + "name": "EDGEX__DEFAULT__PORT", + "value": "5566" + }, + { + "name": "EDGEX__DEFAULT__PROTOCOL", + "value": "tcp" + }, + { + "name": "EDGEX__DEFAULT__SERVER", + "value": "edgex-app-service-configurable-rules" + }, + { + "name": "EDGEX__DEFAULT__SERVICESERVER", + "value": "http://edgex-core-data:48080" + }, + { + "name": "EDGEX__DEFAULT__TOPIC", + "value": "events" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-kuiper" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-command", + "service": { + "ports": [ + { + "name": "tcp-48082", + "protocol": "TCP", + "port": 48082, + "targetPort": 48082 + } + ], + "selector": { + "app": "edgex-core-command" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-command" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-command" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-command", + "image": "openyurt/docker-core-command-go:1.3.1", + "ports": [ + { + "name": "tcp-48082", + "containerPort": 48082, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-command" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-command" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-virtual", + "service": { + "ports": [ + { + "name": "tcp-49990", + "protocol": "TCP", + "port": 49990, + "targetPort": 49990 + } + ], + "selector": { + "app": "edgex-device-virtual" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-virtual" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-virtual" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-virtual", + "image": "openyurt/docker-device-virtual-go:1.3.1", + "ports": [ + { + "name": "tcp-49990", + "containerPort": 49990, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-virtual" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-virtual" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-support-notifications", + "service": { + "ports": [ + { + "name": "tcp-48060", + "protocol": "TCP", + "port": 48060, + "targetPort": 48060 + } + ], + "selector": { + "app": "edgex-support-notifications" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-support-notifications" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-support-notifications" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-support-notifications", + "image": "openyurt/docker-support-notifications-go:1.3.1", + "ports": [ + { + "name": "tcp-48060", + "containerPort": 48060, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-support-notifications" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-support-notifications" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-metadata", + "service": { + "ports": [ + { + "name": "tcp-48081", + "protocol": "TCP", + "port": 48081, + "targetPort": 48081 + } + ], + "selector": { + "app": "edgex-core-metadata" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-metadata" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-metadata" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-metadata", + "image": "openyurt/docker-core-metadata-go:1.3.1", + "ports": [ + { + "name": "tcp-48081", + "containerPort": 48081, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-metadata" + }, + { + "name": "NOTIFICATIONS_SENDER", + "value": "edgex-core-metadata" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-metadata" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-core-data", + "service": { + "ports": [ + { + "name": "tcp-5563", + "protocol": "TCP", + "port": 5563, + "targetPort": 5563 + }, + { + "name": "tcp-48080", + "protocol": "TCP", + "port": 48080, + "targetPort": 48080 + } + ], + "selector": { + "app": "edgex-core-data" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-core-data" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-core-data" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-core-data", + "image": "openyurt/docker-core-data-go:1.3.1", + "ports": [ + { + "name": "tcp-5563", + "containerPort": 5563, + "protocol": "TCP" + }, + { + "name": "tcp-48080", + "containerPort": 48080, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-core-data" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-core-data" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-device-rest", + "service": { + "ports": [ + { + "name": "tcp-49986", + "protocol": "TCP", + "port": 49986, + "targetPort": 49986 + } + ], + "selector": { + "app": "edgex-device-rest" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-device-rest" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-device-rest" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-device-rest", + "image": "openyurt/docker-device-rest-go:1.2.1", + "ports": [ + { + "name": "tcp-49986", + "containerPort": 49986, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-device-rest" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-device-rest" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-app-service-configurable-rules", + "service": { + "ports": [ + { + "name": "tcp-48100", + "protocol": "TCP", + "port": 48100, + "targetPort": 48100 + } + ], + "selector": { + "app": "edgex-app-service-configurable-rules" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-app-service-configurable-rules" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-app-service-configurable-rules" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-app-service-configurable-rules", + "image": "openyurt/docker-app-service-configurable:1.3.1", + "ports": [ + { + "name": "tcp-48100", + "containerPort": 48100, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "MESSAGEBUS_SUBSCRIBEHOST_HOST", + "value": "edgex-core-data" + }, + { + "name": "SERVICE_PORT", + "value": "48100" + }, + { + "name": "BINDING_PUBLISHTOPIC", + "value": "events" + }, + { + "name": "SERVICE_HOST", + "value": "edgex-app-service-configurable-rules" + }, + { + "name": "EDGEX_PROFILE", + "value": "rules-engine" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-app-service-configurable-rules" + } + }, + "strategy": {} + } + }, + { + "name": "edgex-sys-mgmt-agent", + "service": { + "ports": [ + { + "name": "tcp-48090", + "protocol": "TCP", + "port": 48090, + "targetPort": 48090 + } + ], + "selector": { + "app": "edgex-sys-mgmt-agent" + } + }, + "deployment": { + "selector": { + "matchLabels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "edgex-sys-mgmt-agent" + } + }, + "spec": { + "containers": [ + { + "name": "edgex-sys-mgmt-agent", + "image": "openyurt/docker-sys-mgmt-agent-go:1.3.1", + "ports": [ + { + "name": "tcp-48090", + "containerPort": 48090, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "common-variable-hanoi" + } + } + ], + "env": [ + { + "name": "SERVICE_HOST", + "value": "edgex-sys-mgmt-agent" + }, + { + "name": "EXECUTORPATH", + "value": "/sys-mgmt-executor" + }, + { + "name": "METRICSMECHANISM", + "value": "executor" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + } + ], + "hostname": "edgex-sys-mgmt-agent" + } + }, + "strategy": {} + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/controller/iot/config/EdgeXConfig/config.json b/pkg/controller/iot/config/EdgeXConfig/config.json new file mode 100644 index 00000000000..cdd981dd785 --- /dev/null +++ b/pkg/controller/iot/config/EdgeXConfig/config.json @@ -0,0 +1 @@ +{"versions":[{"versionName":"levski","configMaps":[{"metadata":{"name":"common-variable-levski","creationTimestamp":null},"data":{"API_GATEWAY_HOST":"edgex-kong","API_GATEWAY_STATUS_PORT":"8100","CLIENTS_CORE_COMMAND_HOST":"edgex-core-command","CLIENTS_CORE_DATA_HOST":"edgex-core-data","CLIENTS_CORE_METADATA_HOST":"edgex-core-metadata","CLIENTS_SUPPORT_NOTIFICATIONS_HOST":"edgex-support-notifications","CLIENTS_SUPPORT_SCHEDULER_HOST":"edgex-support-scheduler","DATABASES_PRIMARY_HOST":"edgex-redis","EDGEX_SECURITY_SECRET_STORE":"true","MESSAGEQUEUE_HOST":"edgex-redis","PROXY_SETUP_HOST":"edgex-security-proxy-setup","REGISTRY_HOST":"edgex-core-consul","SECRETSTORE_HOST":"edgex-vault","SECRETSTORE_PORT":"8200","SPIFFE_ENDPOINTSOCKET":"/tmp/edgex/secrets/spiffe/public/api.sock","SPIFFE_TRUSTBUNDLE_PATH":"/tmp/edgex/secrets/spiffe/trust/bundle","SPIFFE_TRUSTDOMAIN":"edgexfoundry.org","STAGEGATE_BOOTSTRAPPER_HOST":"edgex-security-bootstrapper","STAGEGATE_BOOTSTRAPPER_STARTPORT":"54321","STAGEGATE_DATABASE_HOST":"edgex-redis","STAGEGATE_DATABASE_PORT":"6379","STAGEGATE_DATABASE_READYPORT":"6379","STAGEGATE_KONGDB_HOST":"edgex-kong-db","STAGEGATE_KONGDB_PORT":"5432","STAGEGATE_KONGDB_READYPORT":"54325","STAGEGATE_READY_TORUNPORT":"54329","STAGEGATE_REGISTRY_HOST":"edgex-core-consul","STAGEGATE_REGISTRY_PORT":"8500","STAGEGATE_REGISTRY_READYPORT":"54324","STAGEGATE_SECRETSTORESETUP_HOST":"edgex-security-secretstore-setup","STAGEGATE_SECRETSTORESETUP_TOKENS_READYPORT":"54322","STAGEGATE_WAITFOR_TIMEOUT":"60s"}}],"components":[{"name":"edgex-core-command","service":{"ports":[{"name":"tcp-59882","protocol":"TCP","port":59882,"targetPort":59882}],"selector":{"app":"edgex-core-command"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-command"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-command"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-command","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-command","image":"openyurt/core-command:2.3.0","ports":[{"name":"tcp-59882","containerPort":59882,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"MESSAGEQUEUE_EXTERNAL_URL","value":"tcp://edgex-mqtt-broker:1883"},{"name":"SERVICE_HOST","value":"edgex-core-command"},{"name":"MESSAGEQUEUE_INTERNAL_HOST","value":"edgex-redis"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-command"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-command"}},"strategy":{}}},{"name":"edgex-app-rules-engine","service":{"ports":[{"name":"tcp-59701","protocol":"TCP","port":59701,"targetPort":59701}],"selector":{"app":"edgex-app-rules-engine"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-app-rules-engine"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-app-rules-engine"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/app-rules-engine","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-app-rules-engine","image":"openyurt/app-service-configurable:2.3.0","ports":[{"name":"tcp-59701","containerPort":59701,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"EDGEX_PROFILE","value":"rules-engine"},{"name":"TRIGGER_EDGEXMESSAGEBUS_PUBLISHHOST_HOST","value":"edgex-redis"},{"name":"SERVICE_HOST","value":"edgex-app-rules-engine"},{"name":"TRIGGER_EDGEXMESSAGEBUS_SUBSCRIBEHOST_HOST","value":"edgex-redis"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/app-rules-engine"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-app-rules-engine"}},"strategy":{}}},{"name":"edgex-support-notifications","service":{"ports":[{"name":"tcp-59860","protocol":"TCP","port":59860,"targetPort":59860}],"selector":{"app":"edgex-support-notifications"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-notifications"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-notifications"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/support-notifications","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-notifications","image":"openyurt/support-notifications:2.3.0","ports":[{"name":"tcp-59860","containerPort":59860,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-support-notifications"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/support-notifications"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-notifications"}},"strategy":{}}},{"name":"edgex-support-scheduler","service":{"ports":[{"name":"tcp-59861","protocol":"TCP","port":59861,"targetPort":59861}],"selector":{"app":"edgex-support-scheduler"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-scheduler"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-scheduler"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/support-scheduler","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-scheduler","image":"openyurt/support-scheduler:2.3.0","ports":[{"name":"tcp-59861","containerPort":59861,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-support-scheduler"},{"name":"INTERVALACTIONS_SCRUBPUSHED_HOST","value":"edgex-core-data"},{"name":"INTERVALACTIONS_SCRUBAGED_HOST","value":"edgex-core-data"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/support-scheduler"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-scheduler"}},"strategy":{}}},{"name":"edgex-core-metadata","service":{"ports":[{"name":"tcp-59881","protocol":"TCP","port":59881,"targetPort":59881}],"selector":{"app":"edgex-core-metadata"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-metadata"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-metadata"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-metadata","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-metadata","image":"openyurt/core-metadata:2.3.0","ports":[{"name":"tcp-59881","containerPort":59881,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"NOTIFICATIONS_SENDER","value":"edgex-core-metadata"},{"name":"SERVICE_HOST","value":"edgex-core-metadata"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-metadata"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-metadata"}},"strategy":{}}},{"name":"edgex-kuiper","service":{"ports":[{"name":"tcp-59720","protocol":"TCP","port":59720,"targetPort":59720}],"selector":{"app":"edgex-kuiper"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kuiper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kuiper"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"kuiper-data","emptyDir":{}},{"name":"kuiper-connections","emptyDir":{}},{"name":"kuiper-sources","emptyDir":{}}],"containers":[{"name":"edgex-kuiper","image":"openyurt/ekuiper:1.7.1-alpine","ports":[{"name":"tcp-59720","containerPort":59720,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"EDGEX__DEFAULT__TYPE","value":"redis"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__SERVER","value":"edgex-redis"},{"name":"EDGEX__DEFAULT__PROTOCOL","value":"redis"},{"name":"KUIPER__BASIC__RESTPORT","value":"59720"},{"name":"EDGEX__DEFAULT__SERVER","value":"edgex-redis"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__PROTOCOL","value":"redis"},{"name":"EDGEX__DEFAULT__TOPIC","value":"rules-events"},{"name":"KUIPER__BASIC__CONSOLELOG","value":"true"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__PORT","value":"6379"},{"name":"EDGEX__DEFAULT__PORT","value":"6379"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__TYPE","value":"redis"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"kuiper-data","mountPath":"/kuiper/data"},{"name":"kuiper-connections","mountPath":"/kuiper/etc/connections"},{"name":"kuiper-sources","mountPath":"/kuiper/etc/sources"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kuiper"}},"strategy":{}}},{"name":"edgex-security-secretstore-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-security-secretstore-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-secretstore-setup"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets","type":"DirectoryOrCreate"}},{"name":"kong","emptyDir":{}},{"name":"kuiper-sources","emptyDir":{}},{"name":"kuiper-connections","emptyDir":{}},{"name":"vault-config","emptyDir":{}}],"containers":[{"name":"edgex-security-secretstore-setup","image":"openyurt/security-secretstore-setup:2.3.0","envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"EDGEX_GROUP","value":"2001"},{"name":"SECUREMESSAGEBUS_TYPE","value":"redis"},{"name":"ADD_KNOWN_SECRETS","value":"redisdb[app-rules-engine],redisdb[device-rest],message-bus[device-rest],redisdb[device-virtual],message-bus[device-virtual]"},{"name":"EDGEX_USER","value":"2002"},{"name":"ADD_SECRETSTORE_TOKENS"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/vault"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets"},{"name":"kong","mountPath":"/tmp/kong"},{"name":"kuiper-sources","mountPath":"/tmp/kuiper"},{"name":"kuiper-connections","mountPath":"/tmp/kuiper-connections"},{"name":"vault-config","mountPath":"/vault/config"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-secretstore-setup"}},"strategy":{}}},{"name":"edgex-device-virtual","service":{"ports":[{"name":"tcp-59900","protocol":"TCP","port":59900,"targetPort":59900}],"selector":{"app":"edgex-device-virtual"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-virtual"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-virtual"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/device-virtual","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-device-virtual","image":"openyurt/device-virtual:2.3.0","ports":[{"name":"tcp-59900","containerPort":59900,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-virtual"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/device-virtual"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-virtual"}},"strategy":{}}},{"name":"edgex-sys-mgmt-agent","service":{"ports":[{"name":"tcp-58890","protocol":"TCP","port":58890,"targetPort":58890}],"selector":{"app":"edgex-sys-mgmt-agent"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-sys-mgmt-agent"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-sys-mgmt-agent"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/sys-mgmt-agent","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-sys-mgmt-agent","image":"openyurt/sys-mgmt-agent:2.3.0","ports":[{"name":"tcp-58890","containerPort":58890,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-sys-mgmt-agent"},{"name":"EXECUTORPATH","value":"/sys-mgmt-executor"},{"name":"METRICSMECHANISM","value":"executor"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/sys-mgmt-agent"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-sys-mgmt-agent"}},"strategy":{}}},{"name":"edgex-core-consul","service":{"ports":[{"name":"tcp-8500","protocol":"TCP","port":8500,"targetPort":8500}],"selector":{"app":"edgex-core-consul"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-consul"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-consul"}},"spec":{"volumes":[{"name":"consul-config","emptyDir":{}},{"name":"consul-data","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"consul-acl-token","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/edgex-consul","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-consul","image":"openyurt/consul:1.13.2","ports":[{"name":"tcp-8500","containerPort":8500,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"STAGEGATE_REGISTRY_ACL_BOOTSTRAPTOKENPATH","value":"/tmp/edgex/secrets/consul-acl-token/bootstrap_token.json"},{"name":"ADD_REGISTRY_ACL_ROLES"},{"name":"EDGEX_USER","value":"2002"},{"name":"STAGEGATE_REGISTRY_ACL_SENTINELFILEPATH","value":"/consul/config/consul_acl_done"},{"name":"STAGEGATE_REGISTRY_ACL_MANAGEMENTTOKENPATH","value":"/tmp/edgex/secrets/consul-acl-token/mgmt_token.json"},{"name":"EDGEX_GROUP","value":"2001"}],"resources":{},"volumeMounts":[{"name":"consul-config","mountPath":"/consul/config"},{"name":"consul-data","mountPath":"/consul/data"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"consul-acl-token","mountPath":"/tmp/edgex/secrets/consul-acl-token"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/edgex-consul"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-consul"}},"strategy":{}}},{"name":"edgex-ui-go","service":{"ports":[{"name":"tcp-4000","protocol":"TCP","port":4000,"targetPort":4000}],"selector":{"app":"edgex-ui-go"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-ui-go"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-ui-go"}},"spec":{"containers":[{"name":"edgex-ui-go","image":"openyurt/edgex-ui:2.3.0","ports":[{"name":"tcp-4000","containerPort":4000,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-ui-go"}],"resources":{},"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-ui-go"}},"strategy":{}}},{"name":"edgex-device-rest","service":{"ports":[{"name":"tcp-59986","protocol":"TCP","port":59986,"targetPort":59986}],"selector":{"app":"edgex-device-rest"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-rest"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-rest"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/device-rest","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-device-rest","image":"openyurt/device-rest:2.3.0","ports":[{"name":"tcp-59986","containerPort":59986,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-rest"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/device-rest"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-rest"}},"strategy":{}}},{"name":"edgex-security-bootstrapper","deployment":{"selector":{"matchLabels":{"app":"edgex-security-bootstrapper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-bootstrapper"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}}],"containers":[{"name":"edgex-security-bootstrapper","image":"openyurt/security-bootstrapper:2.3.0","envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"EDGEX_USER","value":"2002"},{"name":"EDGEX_GROUP","value":"2001"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-bootstrapper"}},"strategy":{}}},{"name":"edgex-core-data","service":{"ports":[{"name":"tcp-5563","protocol":"TCP","port":5563,"targetPort":5563},{"name":"tcp-59880","protocol":"TCP","port":59880,"targetPort":59880}],"selector":{"app":"edgex-core-data"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-data"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-data"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-data","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-data","image":"openyurt/core-data:2.3.0","ports":[{"name":"tcp-5563","containerPort":5563,"protocol":"TCP"},{"name":"tcp-59880","containerPort":59880,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/core-data/secrets-token.json"},{"name":"SERVICE_HOST","value":"edgex-core-data"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-data"}},"strategy":{}}},{"name":"edgex-kong","service":{"ports":[{"name":"tcp-8000","protocol":"TCP","port":8000,"targetPort":8000},{"name":"tcp-8100","protocol":"TCP","port":8100,"targetPort":8100},{"name":"tcp-8443","protocol":"TCP","port":8443,"targetPort":8443}],"selector":{"app":"edgex-kong"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kong"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kong"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-proxy-setup","type":"DirectoryOrCreate"}},{"name":"postgres-config","emptyDir":{}},{"name":"kong","emptyDir":{}}],"containers":[{"name":"edgex-kong","image":"openyurt/kong:2.8.1","ports":[{"name":"tcp-8000","containerPort":8000,"protocol":"TCP"},{"name":"tcp-8100","containerPort":8100,"protocol":"TCP"},{"name":"tcp-8443","containerPort":8443,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"KONG_PROXY_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_ADMIN_LISTEN","value":"127.0.0.1:8001, 127.0.0.1:8444 ssl"},{"name":"KONG_PG_PASSWORD_FILE","value":"/tmp/postgres-config/.pgpassword"},{"name":"KONG_DATABASE","value":"postgres"},{"name":"KONG_SSL_CIPHER_SUITE","value":"modern"},{"name":"KONG_STATUS_LISTEN","value":"0.0.0.0:8100"},{"name":"KONG_DNS_VALID_TTL","value":"1"},{"name":"KONG_PROXY_ERROR_LOG","value":"/dev/stderr"},{"name":"KONG_DNS_ORDER","value":"LAST,A,CNAME"},{"name":"KONG_PG_HOST","value":"edgex-kong-db"},{"name":"KONG_NGINX_WORKER_PROCESSES","value":"1"},{"name":"KONG_ADMIN_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_ADMIN_ERROR_LOG","value":"/dev/stderr"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-proxy-setup"},{"name":"postgres-config","mountPath":"/tmp/postgres-config"},{"name":"kong","mountPath":"/usr/local/kong"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kong"}},"strategy":{}}},{"name":"edgex-redis","service":{"ports":[{"name":"tcp-6379","protocol":"TCP","port":6379,"targetPort":6379}],"selector":{"app":"edgex-redis"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-redis"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-redis"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"db-data","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"redis-config","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-bootstrapper-redis","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-redis","image":"openyurt/redis:7.0.5-alpine","ports":[{"name":"tcp-6379","containerPort":6379,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"DATABASECONFIG_NAME","value":"redis.conf"},{"name":"DATABASECONFIG_PATH","value":"/run/redis/conf"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"db-data","mountPath":"/data"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"redis-config","mountPath":"/run/redis/conf"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-bootstrapper-redis"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-redis"}},"strategy":{}}},{"name":"edgex-vault","service":{"ports":[{"name":"tcp-8200","protocol":"TCP","port":8200,"targetPort":8200}],"selector":{"app":"edgex-vault"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-vault"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-vault"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"vault-file","emptyDir":{}},{"name":"vault-logs","emptyDir":{}}],"containers":[{"name":"edgex-vault","image":"openyurt/vault:1.11.4","ports":[{"name":"tcp-8200","containerPort":8200,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"VAULT_UI","value":"true"},{"name":"VAULT_ADDR","value":"http://edgex-vault:8200"},{"name":"VAULT_CONFIG_DIR","value":"/vault/config"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/vault/config"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"vault-file","mountPath":"/vault/file"},{"name":"vault-logs","mountPath":"/vault/logs"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-vault"}},"strategy":{}}},{"name":"edgex-security-proxy-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-security-proxy-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-proxy-setup"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"consul-acl-token","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-proxy-setup","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-security-proxy-setup","image":"openyurt/security-proxy-setup:2.3.0","envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"ROUTES_RULES_ENGINE_HOST","value":"edgex-kuiper"},{"name":"ROUTES_SYS_MGMT_AGENT_HOST","value":"edgex-sys-mgmt-agent"},{"name":"ROUTES_CORE_DATA_HOST","value":"edgex-core-data"},{"name":"ROUTES_CORE_COMMAND_HOST","value":"edgex-core-command"},{"name":"ADD_PROXY_ROUTE"},{"name":"ROUTES_SUPPORT_NOTIFICATIONS_HOST","value":"edgex-support-notifications"},{"name":"ROUTES_CORE_CONSUL_HOST","value":"edgex-core-consul"},{"name":"KONGURL_SERVER","value":"edgex-kong"},{"name":"ROUTES_CORE_METADATA_HOST","value":"edgex-core-metadata"},{"name":"ROUTES_DEVICE_VIRTUAL_HOST","value":"device-virtual"},{"name":"ROUTES_SUPPORT_SCHEDULER_HOST","value":"edgex-support-scheduler"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"consul-acl-token","mountPath":"/tmp/edgex/secrets/consul-acl-token"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-proxy-setup"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-proxy-setup"}},"strategy":{}}},{"name":"edgex-kong-db","service":{"ports":[{"name":"tcp-5432","protocol":"TCP","port":5432,"targetPort":5432}],"selector":{"app":"edgex-kong-db"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kong-db"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kong-db"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"tmpfs-volume3","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"postgres-config","emptyDir":{}},{"name":"postgres-data","emptyDir":{}}],"containers":[{"name":"edgex-kong-db","image":"openyurt/postgres:13.8-alpine","ports":[{"name":"tcp-5432","containerPort":5432,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-levski"}}],"env":[{"name":"POSTGRES_PASSWORD_FILE","value":"/tmp/postgres-config/.pgpassword"},{"name":"POSTGRES_USER","value":"kong"},{"name":"POSTGRES_DB","value":"kong"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/var/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"tmpfs-volume3","mountPath":"/run"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"postgres-config","mountPath":"/tmp/postgres-config"},{"name":"postgres-data","mountPath":"/var/lib/postgresql/data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kong-db"}},"strategy":{}}}]},{"versionName":"jakarta","configMaps":[{"metadata":{"name":"common-variable-jakarta","creationTimestamp":null},"data":{"API_GATEWAY_HOST":"edgex-kong","API_GATEWAY_STATUS_PORT":"8100","CLIENTS_CORE_COMMAND_HOST":"edgex-core-command","CLIENTS_CORE_DATA_HOST":"edgex-core-data","CLIENTS_CORE_METADATA_HOST":"edgex-core-metadata","CLIENTS_SUPPORT_NOTIFICATIONS_HOST":"edgex-support-notifications","CLIENTS_SUPPORT_SCHEDULER_HOST":"edgex-support-scheduler","DATABASES_PRIMARY_HOST":"edgex-redis","EDGEX_SECURITY_SECRET_STORE":"true","MESSAGEQUEUE_HOST":"edgex-redis","PROXY_SETUP_HOST":"edgex-security-proxy-setup","REGISTRY_HOST":"edgex-core-consul","SECRETSTORE_HOST":"edgex-vault","SECRETSTORE_PORT":"8200","STAGEGATE_BOOTSTRAPPER_HOST":"edgex-security-bootstrapper","STAGEGATE_BOOTSTRAPPER_STARTPORT":"54321","STAGEGATE_DATABASE_HOST":"edgex-redis","STAGEGATE_DATABASE_PORT":"6379","STAGEGATE_DATABASE_READYPORT":"6379","STAGEGATE_KONGDB_HOST":"edgex-kong-db","STAGEGATE_KONGDB_PORT":"5432","STAGEGATE_KONGDB_READYPORT":"54325","STAGEGATE_READY_TORUNPORT":"54329","STAGEGATE_REGISTRY_HOST":"edgex-core-consul","STAGEGATE_REGISTRY_PORT":"8500","STAGEGATE_REGISTRY_READYPORT":"54324","STAGEGATE_SECRETSTORESETUP_HOST":"edgex-security-secretstore-setup","STAGEGATE_SECRETSTORESETUP_TOKENS_READYPORT":"54322","STAGEGATE_WAITFOR_TIMEOUT":"60s"}}],"components":[{"name":"edgex-app-rules-engine","service":{"ports":[{"name":"tcp-59701","protocol":"TCP","port":59701,"targetPort":59701}],"selector":{"app":"edgex-app-rules-engine"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-app-rules-engine"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-app-rules-engine"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/app-rules-engine","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-app-rules-engine","image":"openyurt/app-service-configurable:2.1.1","ports":[{"name":"tcp-59701","containerPort":59701,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-app-rules-engine"},{"name":"EDGEX_PROFILE","value":"rules-engine"},{"name":"TRIGGER_EDGEXMESSAGEBUS_PUBLISHHOST_HOST","value":"edgex-redis"},{"name":"TRIGGER_EDGEXMESSAGEBUS_SUBSCRIBEHOST_HOST","value":"edgex-redis"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/app-rules-engine"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-app-rules-engine"}},"strategy":{}}},{"name":"edgex-ui-go","service":{"ports":[{"name":"tcp-4000","protocol":"TCP","port":4000,"targetPort":4000}],"selector":{"app":"edgex-ui-go"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-ui-go"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-ui-go"}},"spec":{"containers":[{"name":"edgex-ui-go","image":"openyurt/edgex-ui:2.1.0","ports":[{"name":"tcp-4000","containerPort":4000,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"resources":{},"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-ui-go"}},"strategy":{}}},{"name":"edgex-redis","service":{"ports":[{"name":"tcp-6379","protocol":"TCP","port":6379,"targetPort":6379}],"selector":{"app":"edgex-redis"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-redis"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-redis"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"db-data","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"redis-config","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-bootstrapper-redis","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-redis","image":"openyurt/redis:6.2.6-alpine","ports":[{"name":"tcp-6379","containerPort":6379,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"DATABASECONFIG_PATH","value":"/run/redis/conf"},{"name":"DATABASECONFIG_NAME","value":"redis.conf"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"db-data","mountPath":"/data"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"redis-config","mountPath":"/run/redis/conf"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-bootstrapper-redis"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-redis"}},"strategy":{}}},{"name":"edgex-sys-mgmt-agent","service":{"ports":[{"name":"tcp-58890","protocol":"TCP","port":58890,"targetPort":58890}],"selector":{"app":"edgex-sys-mgmt-agent"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-sys-mgmt-agent"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-sys-mgmt-agent"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/sys-mgmt-agent","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-sys-mgmt-agent","image":"openyurt/sys-mgmt-agent:2.1.1","ports":[{"name":"tcp-58890","containerPort":58890,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-sys-mgmt-agent"},{"name":"METRICSMECHANISM","value":"executor"},{"name":"EXECUTORPATH","value":"/sys-mgmt-executor"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/sys-mgmt-agent"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-sys-mgmt-agent"}},"strategy":{}}},{"name":"edgex-core-data","service":{"ports":[{"name":"tcp-5563","protocol":"TCP","port":5563,"targetPort":5563},{"name":"tcp-59880","protocol":"TCP","port":59880,"targetPort":59880}],"selector":{"app":"edgex-core-data"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-data"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-data"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-data","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-data","image":"openyurt/core-data:2.1.1","ports":[{"name":"tcp-5563","containerPort":5563,"protocol":"TCP"},{"name":"tcp-59880","containerPort":59880,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-data"},{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/core-data/secrets-token.json"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-data"}},"strategy":{}}},{"name":"edgex-kuiper","service":{"ports":[{"name":"tcp-59720","protocol":"TCP","port":59720,"targetPort":59720}],"selector":{"app":"edgex-kuiper"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kuiper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kuiper"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"kuiper-data","emptyDir":{}},{"name":"kuiper-connections","emptyDir":{}},{"name":"kuiper-sources","emptyDir":{}}],"containers":[{"name":"edgex-kuiper","image":"openyurt/ekuiper:1.4.4-alpine","ports":[{"name":"tcp-59720","containerPort":59720,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"EDGEX__DEFAULT__TYPE","value":"redis"},{"name":"EDGEX__DEFAULT__PORT","value":"6379"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__PROTOCOL","value":"redis"},{"name":"EDGEX__DEFAULT__PROTOCOL","value":"redis"},{"name":"EDGEX__DEFAULT__TOPIC","value":"rules-events"},{"name":"EDGEX__DEFAULT__SERVER","value":"edgex-redis"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__SERVER","value":"edgex-redis"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__PORT","value":"6379"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__TYPE","value":"redis"},{"name":"KUIPER__BASIC__CONSOLELOG","value":"true"},{"name":"KUIPER__BASIC__RESTPORT","value":"59720"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"kuiper-data","mountPath":"/kuiper/data"},{"name":"kuiper-connections","mountPath":"/kuiper/etc/connections"},{"name":"kuiper-sources","mountPath":"/kuiper/etc/sources"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kuiper"}},"strategy":{}}},{"name":"edgex-device-virtual","service":{"ports":[{"name":"tcp-59900","protocol":"TCP","port":59900,"targetPort":59900}],"selector":{"app":"edgex-device-virtual"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-virtual"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-virtual"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/device-virtual","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-device-virtual","image":"openyurt/device-virtual:2.1.1","ports":[{"name":"tcp-59900","containerPort":59900,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-virtual"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/device-virtual"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-virtual"}},"strategy":{}}},{"name":"edgex-support-scheduler","service":{"ports":[{"name":"tcp-59861","protocol":"TCP","port":59861,"targetPort":59861}],"selector":{"app":"edgex-support-scheduler"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-scheduler"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-scheduler"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/support-scheduler","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-scheduler","image":"openyurt/support-scheduler:2.1.1","ports":[{"name":"tcp-59861","containerPort":59861,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"INTERVALACTIONS_SCRUBPUSHED_HOST","value":"edgex-core-data"},{"name":"INTERVALACTIONS_SCRUBAGED_HOST","value":"edgex-core-data"},{"name":"SERVICE_HOST","value":"edgex-support-scheduler"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/support-scheduler"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-scheduler"}},"strategy":{}}},{"name":"edgex-core-command","service":{"ports":[{"name":"tcp-59882","protocol":"TCP","port":59882,"targetPort":59882}],"selector":{"app":"edgex-core-command"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-command"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-command"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-command","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-command","image":"openyurt/core-command:2.1.1","ports":[{"name":"tcp-59882","containerPort":59882,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-command"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-command"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-command"}},"strategy":{}}},{"name":"edgex-core-metadata","service":{"ports":[{"name":"tcp-59881","protocol":"TCP","port":59881,"targetPort":59881}],"selector":{"app":"edgex-core-metadata"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-metadata"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-metadata"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-metadata","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-metadata","image":"openyurt/core-metadata:2.1.1","ports":[{"name":"tcp-59881","containerPort":59881,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-metadata"},{"name":"NOTIFICATIONS_SENDER","value":"edgex-core-metadata"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-metadata"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-metadata"}},"strategy":{}}},{"name":"edgex-device-rest","service":{"ports":[{"name":"tcp-59986","protocol":"TCP","port":59986,"targetPort":59986}],"selector":{"app":"edgex-device-rest"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-rest"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-rest"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/device-rest","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-device-rest","image":"openyurt/device-rest:2.1.1","ports":[{"name":"tcp-59986","containerPort":59986,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-rest"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/device-rest"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-rest"}},"strategy":{}}},{"name":"edgex-security-bootstrapper","deployment":{"selector":{"matchLabels":{"app":"edgex-security-bootstrapper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-bootstrapper"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}}],"containers":[{"name":"edgex-security-bootstrapper","image":"openyurt/security-bootstrapper:2.1.1","envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"EDGEX_GROUP","value":"2001"},{"name":"EDGEX_USER","value":"2002"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-bootstrapper"}},"strategy":{}}},{"name":"edgex-support-notifications","service":{"ports":[{"name":"tcp-59860","protocol":"TCP","port":59860,"targetPort":59860}],"selector":{"app":"edgex-support-notifications"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-notifications"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-notifications"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/support-notifications","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-notifications","image":"openyurt/support-notifications:2.1.1","ports":[{"name":"tcp-59860","containerPort":59860,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-support-notifications"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/support-notifications"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-notifications"}},"strategy":{}}},{"name":"edgex-vault","service":{"ports":[{"name":"tcp-8200","protocol":"TCP","port":8200,"targetPort":8200}],"selector":{"app":"edgex-vault"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-vault"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-vault"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"vault-file","emptyDir":{}},{"name":"vault-logs","emptyDir":{}}],"containers":[{"name":"edgex-vault","image":"openyurt/vault:1.8.4","ports":[{"name":"tcp-8200","containerPort":8200,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"VAULT_CONFIG_DIR","value":"/vault/config"},{"name":"VAULT_ADDR","value":"http://edgex-vault:8200"},{"name":"VAULT_UI","value":"true"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/vault/config"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"vault-file","mountPath":"/vault/file"},{"name":"vault-logs","mountPath":"/vault/logs"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-vault"}},"strategy":{}}},{"name":"edgex-kong","service":{"ports":[{"name":"tcp-8000","protocol":"TCP","port":8000,"targetPort":8000},{"name":"tcp-8100","protocol":"TCP","port":8100,"targetPort":8100},{"name":"tcp-8443","protocol":"TCP","port":8443,"targetPort":8443}],"selector":{"app":"edgex-kong"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kong"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kong"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-proxy-setup","type":"DirectoryOrCreate"}},{"name":"postgres-config","emptyDir":{}},{"name":"kong","emptyDir":{}}],"containers":[{"name":"edgex-kong","image":"openyurt/kong:2.5.1","ports":[{"name":"tcp-8000","containerPort":8000,"protocol":"TCP"},{"name":"tcp-8100","containerPort":8100,"protocol":"TCP"},{"name":"tcp-8443","containerPort":8443,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"KONG_DNS_VALID_TTL","value":"1"},{"name":"KONG_SSL_CIPHER_SUITE","value":"modern"},{"name":"KONG_ADMIN_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_PROXY_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_ADMIN_ERROR_LOG","value":"/dev/stderr"},{"name":"KONG_ADMIN_LISTEN","value":"127.0.0.1:8001, 127.0.0.1:8444 ssl"},{"name":"KONG_DATABASE","value":"postgres"},{"name":"KONG_NGINX_WORKER_PROCESSES","value":"1"},{"name":"KONG_DNS_ORDER","value":"LAST,A,CNAME"},{"name":"KONG_PG_PASSWORD_FILE","value":"/tmp/postgres-config/.pgpassword"},{"name":"KONG_PROXY_ERROR_LOG","value":"/dev/stderr"},{"name":"KONG_STATUS_LISTEN","value":"0.0.0.0:8100"},{"name":"KONG_PG_HOST","value":"edgex-kong-db"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-proxy-setup"},{"name":"postgres-config","mountPath":"/tmp/postgres-config"},{"name":"kong","mountPath":"/usr/local/kong"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kong"}},"strategy":{}}},{"name":"edgex-core-consul","service":{"ports":[{"name":"tcp-8500","protocol":"TCP","port":8500,"targetPort":8500}],"selector":{"app":"edgex-core-consul"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-consul"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-consul"}},"spec":{"volumes":[{"name":"consul-config","emptyDir":{}},{"name":"consul-data","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"consul-acl-token","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/edgex-consul","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-consul","image":"openyurt/consul:1.10.3","ports":[{"name":"tcp-8500","containerPort":8500,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"STAGEGATE_REGISTRY_ACL_BOOTSTRAPTOKENPATH","value":"/tmp/edgex/secrets/consul-acl-token/bootstrap_token.json"},{"name":"EDGEX_GROUP","value":"2001"},{"name":"STAGEGATE_REGISTRY_ACL_SENTINELFILEPATH","value":"/consul/config/consul_acl_done"},{"name":"EDGEX_USER","value":"2002"},{"name":"ADD_REGISTRY_ACL_ROLES"}],"resources":{},"volumeMounts":[{"name":"consul-config","mountPath":"/consul/config"},{"name":"consul-data","mountPath":"/consul/data"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"consul-acl-token","mountPath":"/tmp/edgex/secrets/consul-acl-token"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/edgex-consul"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-consul"}},"strategy":{}}},{"name":"edgex-security-proxy-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-security-proxy-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-proxy-setup"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"consul-acl-token","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-proxy-setup","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-security-proxy-setup","image":"openyurt/security-proxy-setup:2.1.1","envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"KONGURL_SERVER","value":"edgex-kong"},{"name":"ADD_PROXY_ROUTE"},{"name":"ROUTES_CORE_DATA_HOST","value":"edgex-core-data"},{"name":"ROUTES_RULES_ENGINE_HOST","value":"edgex-kuiper"},{"name":"ROUTES_SUPPORT_NOTIFICATIONS_HOST","value":"edgex-support-notifications"},{"name":"ROUTES_CORE_CONSUL_HOST","value":"edgex-core-consul"},{"name":"ROUTES_SYS_MGMT_AGENT_HOST","value":"edgex-sys-mgmt-agent"},{"name":"ROUTES_DEVICE_VIRTUAL_HOST","value":"device-virtual"},{"name":"ROUTES_SUPPORT_SCHEDULER_HOST","value":"edgex-support-scheduler"},{"name":"ROUTES_CORE_COMMAND_HOST","value":"edgex-core-command"},{"name":"ROUTES_CORE_METADATA_HOST","value":"edgex-core-metadata"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"consul-acl-token","mountPath":"/tmp/edgex/secrets/consul-acl-token"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-proxy-setup"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-proxy-setup"}},"strategy":{}}},{"name":"edgex-security-secretstore-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-security-secretstore-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-secretstore-setup"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets","type":"DirectoryOrCreate"}},{"name":"kong","emptyDir":{}},{"name":"kuiper-sources","emptyDir":{}},{"name":"kuiper-connections","emptyDir":{}},{"name":"vault-config","emptyDir":{}}],"containers":[{"name":"edgex-security-secretstore-setup","image":"openyurt/security-secretstore-setup:2.1.1","envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"EDGEX_GROUP","value":"2001"},{"name":"EDGEX_USER","value":"2002"},{"name":"ADD_KNOWN_SECRETS","value":"redisdb[app-rules-engine],redisdb[device-rest],redisdb[device-virtual]"},{"name":"ADD_SECRETSTORE_TOKENS"},{"name":"SECUREMESSAGEBUS_TYPE","value":"redis"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/vault"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets"},{"name":"kong","mountPath":"/tmp/kong"},{"name":"kuiper-sources","mountPath":"/tmp/kuiper"},{"name":"kuiper-connections","mountPath":"/tmp/kuiper-connections"},{"name":"vault-config","mountPath":"/vault/config"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-secretstore-setup"}},"strategy":{}}},{"name":"edgex-kong-db","service":{"ports":[{"name":"tcp-5432","protocol":"TCP","port":5432,"targetPort":5432}],"selector":{"app":"edgex-kong-db"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kong-db"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kong-db"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"tmpfs-volume3","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"postgres-config","emptyDir":{}},{"name":"postgres-data","emptyDir":{}}],"containers":[{"name":"edgex-kong-db","image":"openyurt/postgres:13.4-alpine","ports":[{"name":"tcp-5432","containerPort":5432,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-jakarta"}}],"env":[{"name":"POSTGRES_USER","value":"kong"},{"name":"POSTGRES_DB","value":"kong"},{"name":"POSTGRES_PASSWORD_FILE","value":"/tmp/postgres-config/.pgpassword"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/var/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"tmpfs-volume3","mountPath":"/run"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"postgres-config","mountPath":"/tmp/postgres-config"},{"name":"postgres-data","mountPath":"/var/lib/postgresql/data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kong-db"}},"strategy":{}}}]},{"versionName":"kamakura","configMaps":[{"metadata":{"name":"common-variable-kamakura","creationTimestamp":null},"data":{"API_GATEWAY_HOST":"edgex-kong","API_GATEWAY_STATUS_PORT":"8100","CLIENTS_CORE_COMMAND_HOST":"edgex-core-command","CLIENTS_CORE_DATA_HOST":"edgex-core-data","CLIENTS_CORE_METADATA_HOST":"edgex-core-metadata","CLIENTS_SUPPORT_NOTIFICATIONS_HOST":"edgex-support-notifications","CLIENTS_SUPPORT_SCHEDULER_HOST":"edgex-support-scheduler","DATABASES_PRIMARY_HOST":"edgex-redis","EDGEX_SECURITY_SECRET_STORE":"true","MESSAGEQUEUE_HOST":"edgex-redis","PROXY_SETUP_HOST":"edgex-security-proxy-setup","REGISTRY_HOST":"edgex-core-consul","SECRETSTORE_HOST":"edgex-vault","SECRETSTORE_PORT":"8200","SPIFFE_ENDPOINTSOCKET":"/tmp/edgex/secrets/spiffe/public/api.sock","SPIFFE_TRUSTBUNDLE_PATH":"/tmp/edgex/secrets/spiffe/trust/bundle","SPIFFE_TRUSTDOMAIN":"edgexfoundry.org","STAGEGATE_BOOTSTRAPPER_HOST":"edgex-security-bootstrapper","STAGEGATE_BOOTSTRAPPER_STARTPORT":"54321","STAGEGATE_DATABASE_HOST":"edgex-redis","STAGEGATE_DATABASE_PORT":"6379","STAGEGATE_DATABASE_READYPORT":"6379","STAGEGATE_KONGDB_HOST":"edgex-kong-db","STAGEGATE_KONGDB_PORT":"5432","STAGEGATE_KONGDB_READYPORT":"54325","STAGEGATE_READY_TORUNPORT":"54329","STAGEGATE_REGISTRY_HOST":"edgex-core-consul","STAGEGATE_REGISTRY_PORT":"8500","STAGEGATE_REGISTRY_READYPORT":"54324","STAGEGATE_SECRETSTORESETUP_HOST":"edgex-security-secretstore-setup","STAGEGATE_SECRETSTORESETUP_TOKENS_READYPORT":"54322","STAGEGATE_WAITFOR_TIMEOUT":"60s"}}],"components":[{"name":"edgex-security-bootstrapper","deployment":{"selector":{"matchLabels":{"app":"edgex-security-bootstrapper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-bootstrapper"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}}],"containers":[{"name":"edgex-security-bootstrapper","image":"openyurt/security-bootstrapper:2.2.0","envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"EDGEX_GROUP","value":"2001"},{"name":"EDGEX_USER","value":"2002"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-bootstrapper"}},"strategy":{}}},{"name":"edgex-app-rules-engine","service":{"ports":[{"name":"tcp-59701","protocol":"TCP","port":59701,"targetPort":59701}],"selector":{"app":"edgex-app-rules-engine"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-app-rules-engine"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-app-rules-engine"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/app-rules-engine","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-app-rules-engine","image":"openyurt/app-service-configurable:2.2.0","ports":[{"name":"tcp-59701","containerPort":59701,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"TRIGGER_EDGEXMESSAGEBUS_SUBSCRIBEHOST_HOST","value":"edgex-redis"},{"name":"TRIGGER_EDGEXMESSAGEBUS_PUBLISHHOST_HOST","value":"edgex-redis"},{"name":"EDGEX_PROFILE","value":"rules-engine"},{"name":"SERVICE_HOST","value":"edgex-app-rules-engine"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/app-rules-engine"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-app-rules-engine"}},"strategy":{}}},{"name":"edgex-security-proxy-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-security-proxy-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-proxy-setup"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"consul-acl-token","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-proxy-setup","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-security-proxy-setup","image":"openyurt/security-proxy-setup:2.2.0","envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"ROUTES_RULES_ENGINE_HOST","value":"edgex-kuiper"},{"name":"KONGURL_SERVER","value":"edgex-kong"},{"name":"ROUTES_DEVICE_VIRTUAL_HOST","value":"device-virtual"},{"name":"ROUTES_CORE_DATA_HOST","value":"edgex-core-data"},{"name":"ROUTES_CORE_COMMAND_HOST","value":"edgex-core-command"},{"name":"ADD_PROXY_ROUTE"},{"name":"ROUTES_SYS_MGMT_AGENT_HOST","value":"edgex-sys-mgmt-agent"},{"name":"ROUTES_SUPPORT_NOTIFICATIONS_HOST","value":"edgex-support-notifications"},{"name":"ROUTES_CORE_METADATA_HOST","value":"edgex-core-metadata"},{"name":"ROUTES_CORE_CONSUL_HOST","value":"edgex-core-consul"},{"name":"ROUTES_SUPPORT_SCHEDULER_HOST","value":"edgex-support-scheduler"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"consul-acl-token","mountPath":"/tmp/edgex/secrets/consul-acl-token"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-proxy-setup"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-proxy-setup"}},"strategy":{}}},{"name":"edgex-device-rest","service":{"ports":[{"name":"tcp-59986","protocol":"TCP","port":59986,"targetPort":59986}],"selector":{"app":"edgex-device-rest"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-rest"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-rest"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/device-rest","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-device-rest","image":"openyurt/device-rest:2.2.0","ports":[{"name":"tcp-59986","containerPort":59986,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-rest"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/device-rest"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-rest"}},"strategy":{}}},{"name":"edgex-kuiper","service":{"ports":[{"name":"tcp-59720","protocol":"TCP","port":59720,"targetPort":59720}],"selector":{"app":"edgex-kuiper"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kuiper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kuiper"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"kuiper-data","emptyDir":{}},{"name":"kuiper-connections","emptyDir":{}},{"name":"kuiper-sources","emptyDir":{}}],"containers":[{"name":"edgex-kuiper","image":"openyurt/ekuiper:1.4.4-alpine","ports":[{"name":"tcp-59720","containerPort":59720,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"KUIPER__BASIC__CONSOLELOG","value":"true"},{"name":"EDGEX__DEFAULT__TOPIC","value":"rules-events"},{"name":"EDGEX__DEFAULT__PORT","value":"6379"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__PORT","value":"6379"},{"name":"KUIPER__BASIC__RESTPORT","value":"59720"},{"name":"EDGEX__DEFAULT__TYPE","value":"redis"},{"name":"EDGEX__DEFAULT__PROTOCOL","value":"redis"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__PROTOCOL","value":"redis"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__SERVER","value":"edgex-redis"},{"name":"CONNECTION__EDGEX__REDISMSGBUS__TYPE","value":"redis"},{"name":"EDGEX__DEFAULT__SERVER","value":"edgex-redis"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"kuiper-data","mountPath":"/kuiper/data"},{"name":"kuiper-connections","mountPath":"/kuiper/etc/connections"},{"name":"kuiper-sources","mountPath":"/kuiper/etc/sources"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kuiper"}},"strategy":{}}},{"name":"edgex-sys-mgmt-agent","service":{"ports":[{"name":"tcp-58890","protocol":"TCP","port":58890,"targetPort":58890}],"selector":{"app":"edgex-sys-mgmt-agent"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-sys-mgmt-agent"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-sys-mgmt-agent"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/sys-mgmt-agent","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-sys-mgmt-agent","image":"openyurt/sys-mgmt-agent:2.2.0","ports":[{"name":"tcp-58890","containerPort":58890,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"EXECUTORPATH","value":"/sys-mgmt-executor"},{"name":"SERVICE_HOST","value":"edgex-sys-mgmt-agent"},{"name":"METRICSMECHANISM","value":"executor"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/sys-mgmt-agent"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-sys-mgmt-agent"}},"strategy":{}}},{"name":"edgex-core-metadata","service":{"ports":[{"name":"tcp-59881","protocol":"TCP","port":59881,"targetPort":59881}],"selector":{"app":"edgex-core-metadata"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-metadata"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-metadata"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-metadata","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-metadata","image":"openyurt/core-metadata:2.2.0","ports":[{"name":"tcp-59881","containerPort":59881,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-metadata"},{"name":"NOTIFICATIONS_SENDER","value":"edgex-core-metadata"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-metadata"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-metadata"}},"strategy":{}}},{"name":"edgex-core-data","service":{"ports":[{"name":"tcp-5563","protocol":"TCP","port":5563,"targetPort":5563},{"name":"tcp-59880","protocol":"TCP","port":59880,"targetPort":59880}],"selector":{"app":"edgex-core-data"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-data"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-data"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-data","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-data","image":"openyurt/core-data:2.2.0","ports":[{"name":"tcp-5563","containerPort":5563,"protocol":"TCP"},{"name":"tcp-59880","containerPort":59880,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/core-data/secrets-token.json"},{"name":"SERVICE_HOST","value":"edgex-core-data"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-data"}},"strategy":{}}},{"name":"edgex-security-secretstore-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-security-secretstore-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-secretstore-setup"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets","type":"DirectoryOrCreate"}},{"name":"kong","emptyDir":{}},{"name":"kuiper-sources","emptyDir":{}},{"name":"kuiper-connections","emptyDir":{}},{"name":"vault-config","emptyDir":{}}],"containers":[{"name":"edgex-security-secretstore-setup","image":"openyurt/security-secretstore-setup:2.2.0","envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"ADD_SECRETSTORE_TOKENS"},{"name":"EDGEX_GROUP","value":"2001"},{"name":"SECUREMESSAGEBUS_TYPE","value":"redis"},{"name":"EDGEX_USER","value":"2002"},{"name":"ADD_KNOWN_SECRETS","value":"redisdb[app-rules-engine],redisdb[device-rest],redisdb[device-virtual]"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/vault"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets"},{"name":"kong","mountPath":"/tmp/kong"},{"name":"kuiper-sources","mountPath":"/tmp/kuiper"},{"name":"kuiper-connections","mountPath":"/tmp/kuiper-connections"},{"name":"vault-config","mountPath":"/vault/config"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-secretstore-setup"}},"strategy":{}}},{"name":"edgex-device-virtual","service":{"ports":[{"name":"tcp-59900","protocol":"TCP","port":59900,"targetPort":59900}],"selector":{"app":"edgex-device-virtual"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-virtual"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-virtual"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/device-virtual","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-device-virtual","image":"openyurt/device-virtual:2.2.0","ports":[{"name":"tcp-59900","containerPort":59900,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-virtual"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/device-virtual"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-virtual"}},"strategy":{}}},{"name":"edgex-ui-go","service":{"ports":[{"name":"tcp-4000","protocol":"TCP","port":4000,"targetPort":4000}],"selector":{"app":"edgex-ui-go"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-ui-go"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-ui-go"}},"spec":{"containers":[{"name":"edgex-ui-go","image":"openyurt/edgex-ui:2.2.0","ports":[{"name":"tcp-4000","containerPort":4000,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"resources":{},"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-ui-go"}},"strategy":{}}},{"name":"edgex-vault","service":{"ports":[{"name":"tcp-8200","protocol":"TCP","port":8200,"targetPort":8200}],"selector":{"app":"edgex-vault"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-vault"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-vault"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"vault-file","emptyDir":{}},{"name":"vault-logs","emptyDir":{}}],"containers":[{"name":"edgex-vault","image":"openyurt/vault:1.8.9","ports":[{"name":"tcp-8200","containerPort":8200,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"VAULT_CONFIG_DIR","value":"/vault/config"},{"name":"VAULT_ADDR","value":"http://edgex-vault:8200"},{"name":"VAULT_UI","value":"true"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/vault/config"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"vault-file","mountPath":"/vault/file"},{"name":"vault-logs","mountPath":"/vault/logs"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-vault"}},"strategy":{}}},{"name":"edgex-core-command","service":{"ports":[{"name":"tcp-59882","protocol":"TCP","port":59882,"targetPort":59882}],"selector":{"app":"edgex-core-command"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-command"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-command"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-command","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-command","image":"openyurt/core-command:2.2.0","ports":[{"name":"tcp-59882","containerPort":59882,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-command"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-command"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-command"}},"strategy":{}}},{"name":"edgex-core-consul","service":{"ports":[{"name":"tcp-8500","protocol":"TCP","port":8500,"targetPort":8500}],"selector":{"app":"edgex-core-consul"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-consul"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-consul"}},"spec":{"volumes":[{"name":"consul-config","emptyDir":{}},{"name":"consul-data","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"consul-acl-token","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/edgex-consul","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-consul","image":"openyurt/consul:1.10.10","ports":[{"name":"tcp-8500","containerPort":8500,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"ADD_REGISTRY_ACL_ROLES"},{"name":"STAGEGATE_REGISTRY_ACL_SENTINELFILEPATH","value":"/consul/config/consul_acl_done"},{"name":"STAGEGATE_REGISTRY_ACL_BOOTSTRAPTOKENPATH","value":"/tmp/edgex/secrets/consul-acl-token/bootstrap_token.json"},{"name":"EDGEX_USER","value":"2002"},{"name":"EDGEX_GROUP","value":"2001"}],"resources":{},"volumeMounts":[{"name":"consul-config","mountPath":"/consul/config"},{"name":"consul-data","mountPath":"/consul/data"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"consul-acl-token","mountPath":"/tmp/edgex/secrets/consul-acl-token"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/edgex-consul"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-consul"}},"strategy":{}}},{"name":"edgex-kong-db","service":{"ports":[{"name":"tcp-5432","protocol":"TCP","port":5432,"targetPort":5432}],"selector":{"app":"edgex-kong-db"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kong-db"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kong-db"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"tmpfs-volume3","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"postgres-config","emptyDir":{}},{"name":"postgres-data","emptyDir":{}}],"containers":[{"name":"edgex-kong-db","image":"openyurt/postgres:13.5-alpine","ports":[{"name":"tcp-5432","containerPort":5432,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"POSTGRES_USER","value":"kong"},{"name":"POSTGRES_PASSWORD_FILE","value":"/tmp/postgres-config/.pgpassword"},{"name":"POSTGRES_DB","value":"kong"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/var/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"tmpfs-volume3","mountPath":"/run"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"postgres-config","mountPath":"/tmp/postgres-config"},{"name":"postgres-data","mountPath":"/var/lib/postgresql/data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kong-db"}},"strategy":{}}},{"name":"edgex-support-scheduler","service":{"ports":[{"name":"tcp-59861","protocol":"TCP","port":59861,"targetPort":59861}],"selector":{"app":"edgex-support-scheduler"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-scheduler"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-scheduler"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/support-scheduler","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-scheduler","image":"openyurt/support-scheduler:2.2.0","ports":[{"name":"tcp-59861","containerPort":59861,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"INTERVALACTIONS_SCRUBPUSHED_HOST","value":"edgex-core-data"},{"name":"SERVICE_HOST","value":"edgex-support-scheduler"},{"name":"INTERVALACTIONS_SCRUBAGED_HOST","value":"edgex-core-data"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/support-scheduler"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-scheduler"}},"strategy":{}}},{"name":"edgex-redis","service":{"ports":[{"name":"tcp-6379","protocol":"TCP","port":6379,"targetPort":6379}],"selector":{"app":"edgex-redis"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-redis"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-redis"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"db-data","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"redis-config","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-bootstrapper-redis","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-redis","image":"openyurt/redis:6.2.6-alpine","ports":[{"name":"tcp-6379","containerPort":6379,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"DATABASECONFIG_NAME","value":"redis.conf"},{"name":"DATABASECONFIG_PATH","value":"/run/redis/conf"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"db-data","mountPath":"/data"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"redis-config","mountPath":"/run/redis/conf"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-bootstrapper-redis"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-redis"}},"strategy":{}}},{"name":"edgex-support-notifications","service":{"ports":[{"name":"tcp-59860","protocol":"TCP","port":59860,"targetPort":59860}],"selector":{"app":"edgex-support-notifications"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-notifications"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-notifications"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/support-notifications","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-notifications","image":"openyurt/support-notifications:2.2.0","ports":[{"name":"tcp-59860","containerPort":59860,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-support-notifications"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/support-notifications"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-notifications"}},"strategy":{}}},{"name":"edgex-kong","service":{"ports":[{"name":"tcp-8000","protocol":"TCP","port":8000,"targetPort":8000},{"name":"tcp-8100","protocol":"TCP","port":8100,"targetPort":8100},{"name":"tcp-8443","protocol":"TCP","port":8443,"targetPort":8443}],"selector":{"app":"edgex-kong"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kong"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kong"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-proxy-setup","type":"DirectoryOrCreate"}},{"name":"postgres-config","emptyDir":{}},{"name":"kong","emptyDir":{}}],"containers":[{"name":"edgex-kong","image":"openyurt/kong:2.6.1","ports":[{"name":"tcp-8000","containerPort":8000,"protocol":"TCP"},{"name":"tcp-8100","containerPort":8100,"protocol":"TCP"},{"name":"tcp-8443","containerPort":8443,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-kamakura"}}],"env":[{"name":"KONG_NGINX_WORKER_PROCESSES","value":"1"},{"name":"KONG_ADMIN_ERROR_LOG","value":"/dev/stderr"},{"name":"KONG_PG_PASSWORD_FILE","value":"/tmp/postgres-config/.pgpassword"},{"name":"KONG_STATUS_LISTEN","value":"0.0.0.0:8100"},{"name":"KONG_SSL_CIPHER_SUITE","value":"modern"},{"name":"KONG_DNS_ORDER","value":"LAST,A,CNAME"},{"name":"KONG_ADMIN_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_DNS_VALID_TTL","value":"1"},{"name":"KONG_DATABASE","value":"postgres"},{"name":"KONG_PROXY_ERROR_LOG","value":"/dev/stderr"},{"name":"KONG_PROXY_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_PG_HOST","value":"edgex-kong-db"},{"name":"KONG_ADMIN_LISTEN","value":"127.0.0.1:8001, 127.0.0.1:8444 ssl"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-proxy-setup"},{"name":"postgres-config","mountPath":"/tmp/postgres-config"},{"name":"kong","mountPath":"/usr/local/kong"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kong"}},"strategy":{}}}]},{"versionName":"ireland","configMaps":[{"metadata":{"name":"common-variable-ireland","creationTimestamp":null},"data":{"API_GATEWAY_HOST":"edgex-kong","API_GATEWAY_STATUS_PORT":"8100","CLIENTS_CORE_COMMAND_HOST":"edgex-core-command","CLIENTS_CORE_DATA_HOST":"edgex-core-data","CLIENTS_CORE_METADATA_HOST":"edgex-core-metadata","CLIENTS_SUPPORT_NOTIFICATIONS_HOST":"edgex-support-notifications","CLIENTS_SUPPORT_SCHEDULER_HOST":"edgex-support-scheduler","DATABASES_PRIMARY_HOST":"edgex-redis","EDGEX_SECURITY_SECRET_STORE":"true","MESSAGEQUEUE_HOST":"edgex-redis","PROXY_SETUP_HOST":"edgex-security-proxy-setup","REGISTRY_HOST":"edgex-core-consul","SECRETSTORE_HOST":"edgex-vault","SECRETSTORE_PORT":"8200","STAGEGATE_BOOTSTRAPPER_HOST":"edgex-security-bootstrapper","STAGEGATE_BOOTSTRAPPER_STARTPORT":"54321","STAGEGATE_DATABASE_HOST":"edgex-redis","STAGEGATE_DATABASE_PORT":"6379","STAGEGATE_DATABASE_READYPORT":"6379","STAGEGATE_KONGDB_HOST":"edgex-kong-db","STAGEGATE_KONGDB_PORT":"5432","STAGEGATE_KONGDB_READYPORT":"54325","STAGEGATE_READY_TORUNPORT":"54329","STAGEGATE_REGISTRY_HOST":"edgex-core-consul","STAGEGATE_REGISTRY_PORT":"8500","STAGEGATE_REGISTRY_READYPORT":"54324","STAGEGATE_SECRETSTORESETUP_HOST":"edgex-security-secretstore-setup","STAGEGATE_SECRETSTORESETUP_TOKENS_READYPORT":"54322","STAGEGATE_WAITFOR_TIMEOUT":"60s"}}],"components":[{"name":"edgex-redis","service":{"ports":[{"name":"tcp-6379","protocol":"TCP","port":6379,"targetPort":6379}],"selector":{"app":"edgex-redis"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-redis"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-redis"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"db-data","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"redis-config","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-bootstrapper-redis","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-redis","image":"openyurt/redis:6.2.4-alpine","ports":[{"name":"tcp-6379","containerPort":6379,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"DATABASECONFIG_NAME","value":"redis.conf"},{"name":"DATABASECONFIG_PATH","value":"/run/redis/conf"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"db-data","mountPath":"/data"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"redis-config","mountPath":"/run/redis/conf"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-bootstrapper-redis"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-redis"}},"strategy":{}}},{"name":"edgex-device-virtual","service":{"ports":[{"name":"tcp-59900","protocol":"TCP","port":59900,"targetPort":59900}],"selector":{"app":"edgex-device-virtual"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-virtual"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-virtual"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/device-virtual","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-device-virtual","image":"openyurt/device-virtual:2.0.0","ports":[{"name":"tcp-59900","containerPort":59900,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-virtual"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/device-virtual"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-virtual"}},"strategy":{}}},{"name":"edgex-core-metadata","service":{"ports":[{"name":"tcp-59881","protocol":"TCP","port":59881,"targetPort":59881}],"selector":{"app":"edgex-core-metadata"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-metadata"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-metadata"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-metadata","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-metadata","image":"openyurt/core-metadata:2.0.0","ports":[{"name":"tcp-59881","containerPort":59881,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-metadata"},{"name":"NOTIFICATIONS_SENDER","value":"edgex-core-metadata"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-metadata"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-metadata"}},"strategy":{}}},{"name":"edgex-app-rules-engine","service":{"ports":[{"name":"tcp-59701","protocol":"TCP","port":59701,"targetPort":59701}],"selector":{"app":"edgex-app-rules-engine"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-app-rules-engine"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-app-rules-engine"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/app-rules-engine","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-app-rules-engine","image":"openyurt/app-service-configurable:2.0.1","ports":[{"name":"tcp-59701","containerPort":59701,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"EDGEX_PROFILE","value":"rules-engine"},{"name":"TRIGGER_EDGEXMESSAGEBUS_SUBSCRIBEHOST_HOST","value":"edgex-redis"},{"name":"SERVICE_HOST","value":"edgex-app-rules-engine"},{"name":"TRIGGER_EDGEXMESSAGEBUS_PUBLISHHOST_HOST","value":"edgex-redis"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/app-rules-engine"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-app-rules-engine"}},"strategy":{}}},{"name":"edgex-core-consul","service":{"ports":[{"name":"tcp-8500","protocol":"TCP","port":8500,"targetPort":8500}],"selector":{"app":"edgex-core-consul"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-consul"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-consul"}},"spec":{"volumes":[{"name":"consul-config","emptyDir":{}},{"name":"consul-data","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"consul-acl-token","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/edgex-consul","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-consul","image":"openyurt/consul:1.9.5","ports":[{"name":"tcp-8500","containerPort":8500,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"STAGEGATE_REGISTRY_ACL_BOOTSTRAPTOKENPATH","value":"/tmp/edgex/secrets/consul-acl-token/bootstrap_token.json"},{"name":"EDGEX_GROUP","value":"2001"},{"name":"ADD_REGISTRY_ACL_ROLES"},{"name":"STAGEGATE_REGISTRY_ACL_SENTINELFILEPATH","value":"/consul/config/consul_acl_done"},{"name":"EDGEX_USER","value":"2002"}],"resources":{},"volumeMounts":[{"name":"consul-config","mountPath":"/consul/config"},{"name":"consul-data","mountPath":"/consul/data"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"consul-acl-token","mountPath":"/tmp/edgex/secrets/consul-acl-token"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/edgex-consul"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-consul"}},"strategy":{}}},{"name":"edgex-device-rest","service":{"ports":[{"name":"tcp-59986","protocol":"TCP","port":59986,"targetPort":59986}],"selector":{"app":"edgex-device-rest"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-rest"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-rest"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/device-rest","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-device-rest","image":"openyurt/device-rest:2.0.0","ports":[{"name":"tcp-59986","containerPort":59986,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-rest"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/device-rest"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-rest"}},"strategy":{}}},{"name":"edgex-vault","service":{"ports":[{"name":"tcp-8200","protocol":"TCP","port":8200,"targetPort":8200}],"selector":{"app":"edgex-vault"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-vault"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-vault"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"vault-file","emptyDir":{}},{"name":"vault-logs","emptyDir":{}}],"containers":[{"name":"edgex-vault","image":"openyurt/vault:1.7.2","ports":[{"name":"tcp-8200","containerPort":8200,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"VAULT_ADDR","value":"http://edgex-vault:8200"},{"name":"VAULT_UI","value":"true"},{"name":"VAULT_CONFIG_DIR","value":"/vault/config"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/vault/config"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"vault-file","mountPath":"/vault/file"},{"name":"vault-logs","mountPath":"/vault/logs"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-vault"}},"strategy":{}}},{"name":"edgex-core-command","service":{"ports":[{"name":"tcp-59882","protocol":"TCP","port":59882,"targetPort":59882}],"selector":{"app":"edgex-core-command"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-command"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-command"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-command","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-command","image":"openyurt/core-command:2.0.0","ports":[{"name":"tcp-59882","containerPort":59882,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-command"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-command"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-command"}},"strategy":{}}},{"name":"edgex-core-data","service":{"ports":[{"name":"tcp-5563","protocol":"TCP","port":5563,"targetPort":5563},{"name":"tcp-59880","protocol":"TCP","port":59880,"targetPort":59880}],"selector":{"app":"edgex-core-data"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-data"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-data"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/core-data","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-data","image":"openyurt/core-data:2.0.0","ports":[{"name":"tcp-5563","containerPort":5563,"protocol":"TCP"},{"name":"tcp-59880","containerPort":59880,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-data"},{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/core-data/secrets-token.json"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/core-data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-data"}},"strategy":{}}},{"name":"edgex-kuiper","service":{"ports":[{"name":"tcp-59720","protocol":"TCP","port":59720,"targetPort":59720}],"selector":{"app":"edgex-kuiper"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kuiper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kuiper"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"kuiper-data","emptyDir":{}},{"name":"kuiper-config","emptyDir":{}}],"containers":[{"name":"edgex-kuiper","image":"openyurt/ekuiper:1.3.0-alpine","ports":[{"name":"tcp-59720","containerPort":59720,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"EDGEX__DEFAULT__SERVER","value":"edgex-redis"},{"name":"EDGEX__DEFAULT__TYPE","value":"redis"},{"name":"KUIPER__BASIC__CONSOLELOG","value":"true"},{"name":"EDGEX__DEFAULT__TOPIC","value":"rules-events"},{"name":"EDGEX__DEFAULT__PORT","value":"6379"},{"name":"KUIPER__BASIC__RESTPORT","value":"59720"},{"name":"EDGEX__DEFAULT__PROTOCOL","value":"redis"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"kuiper-data","mountPath":"/kuiper/data"},{"name":"kuiper-config","mountPath":"/kuiper/etc/sources"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kuiper"}},"strategy":{}}},{"name":"edgex-security-proxy-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-security-proxy-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-proxy-setup"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"consul-acl-token","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-proxy-setup","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-security-proxy-setup","image":"openyurt/security-proxy-setup:2.0.0","envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"ROUTES_SYS_MGMT_AGENT_HOST","value":"edgex-sys-mgmt-agent"},{"name":"ROUTES_CORE_METADATA_HOST","value":"edgex-core-metadata"},{"name":"ROUTES_DEVICE_VIRTUAL_HOST","value":"device-virtual"},{"name":"ROUTES_RULES_ENGINE_HOST","value":"edgex-kuiper"},{"name":"ROUTES_SUPPORT_SCHEDULER_HOST","value":"edgex-support-scheduler"},{"name":"KONGURL_SERVER","value":"edgex-kong"},{"name":"ROUTES_CORE_DATA_HOST","value":"edgex-core-data"},{"name":"ROUTES_SUPPORT_NOTIFICATIONS_HOST","value":"edgex-support-notifications"},{"name":"ADD_PROXY_ROUTE"},{"name":"ROUTES_CORE_COMMAND_HOST","value":"edgex-core-command"},{"name":"ROUTES_CORE_CONSUL_HOST","value":"edgex-core-consul"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"consul-acl-token","mountPath":"/tmp/edgex/secrets/consul-acl-token"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-proxy-setup"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-proxy-setup"}},"strategy":{}}},{"name":"edgex-support-scheduler","service":{"ports":[{"name":"tcp-59861","protocol":"TCP","port":59861,"targetPort":59861}],"selector":{"app":"edgex-support-scheduler"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-scheduler"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-scheduler"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/support-scheduler","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-scheduler","image":"openyurt/support-scheduler:2.0.0","ports":[{"name":"tcp-59861","containerPort":59861,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"INTERVALACTIONS_SCRUBPUSHED_HOST","value":"edgex-core-data"},{"name":"SERVICE_HOST","value":"edgex-support-scheduler"},{"name":"INTERVALACTIONS_SCRUBAGED_HOST","value":"edgex-core-data"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/support-scheduler"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-scheduler"}},"strategy":{}}},{"name":"edgex-kong","service":{"ports":[{"name":"tcp-8000","protocol":"TCP","port":8000,"targetPort":8000},{"name":"tcp-8100","protocol":"TCP","port":8100,"targetPort":8100},{"name":"tcp-8443","protocol":"TCP","port":8443,"targetPort":8443}],"selector":{"app":"edgex-kong"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kong"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kong"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/security-proxy-setup","type":"DirectoryOrCreate"}},{"name":"postgres-config","emptyDir":{}},{"name":"kong","emptyDir":{}}],"containers":[{"name":"edgex-kong","image":"openyurt/kong:2.4.1-alpine","ports":[{"name":"tcp-8000","containerPort":8000,"protocol":"TCP"},{"name":"tcp-8100","containerPort":8100,"protocol":"TCP"},{"name":"tcp-8443","containerPort":8443,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"KONG_PROXY_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_DNS_VALID_TTL","value":"1"},{"name":"KONG_STATUS_LISTEN","value":"0.0.0.0:8100"},{"name":"KONG_ADMIN_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_DNS_ORDER","value":"LAST,A,CNAME"},{"name":"KONG_PG_PASSWORD_FILE","value":"/tmp/postgres-config/.pgpassword"},{"name":"KONG_PG_HOST","value":"edgex-kong-db"},{"name":"KONG_PROXY_ERROR_LOG","value":"/dev/stderr"},{"name":"KONG_ADMIN_LISTEN","value":"127.0.0.1:8001, 127.0.0.1:8444 ssl"},{"name":"KONG_DATABASE","value":"postgres"},{"name":"KONG_ADMIN_ERROR_LOG","value":"/dev/stderr"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/security-proxy-setup"},{"name":"postgres-config","mountPath":"/tmp/postgres-config"},{"name":"kong","mountPath":"/usr/local/kong"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kong"}},"strategy":{}}},{"name":"edgex-security-secretstore-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-security-secretstore-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-secretstore-setup"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets","type":"DirectoryOrCreate"}},{"name":"kong","emptyDir":{}},{"name":"kuiper-config","emptyDir":{}},{"name":"vault-config","emptyDir":{}}],"containers":[{"name":"edgex-security-secretstore-setup","image":"openyurt/security-secretstore-setup:2.0.0","envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"ADD_KNOWN_SECRETS","value":"redisdb[app-rules-engine],redisdb[device-rest],redisdb[device-virtual]"},{"name":"EDGEX_GROUP","value":"2001"},{"name":"EDGEX_USER","value":"2002"},{"name":"ADD_SECRETSTORE_TOKENS"},{"name":"SECUREMESSAGEBUS_TYPE","value":"redis"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/vault"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets"},{"name":"kong","mountPath":"/tmp/kong"},{"name":"kuiper-config","mountPath":"/tmp/kuiper"},{"name":"vault-config","mountPath":"/vault/config"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-secretstore-setup"}},"strategy":{}}},{"name":"edgex-kong-db","service":{"ports":[{"name":"tcp-5432","protocol":"TCP","port":5432,"targetPort":5432}],"selector":{"app":"edgex-kong-db"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kong-db"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kong-db"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"tmpfs-volume3","emptyDir":{}},{"name":"edgex-init","emptyDir":{}},{"name":"postgres-config","emptyDir":{}},{"name":"postgres-data","emptyDir":{}}],"containers":[{"name":"edgex-kong-db","image":"openyurt/postgres:12.3-alpine","ports":[{"name":"tcp-5432","containerPort":5432,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"POSTGRES_DB","value":"kong"},{"name":"POSTGRES_PASSWORD_FILE","value":"/tmp/postgres-config/.pgpassword"},{"name":"POSTGRES_USER","value":"kong"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/var/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"tmpfs-volume3","mountPath":"/run"},{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"postgres-config","mountPath":"/tmp/postgres-config"},{"name":"postgres-data","mountPath":"/var/lib/postgresql/data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kong-db"}},"strategy":{}}},{"name":"edgex-security-bootstrapper","deployment":{"selector":{"matchLabels":{"app":"edgex-security-bootstrapper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-bootstrapper"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}}],"containers":[{"name":"edgex-security-bootstrapper","image":"openyurt/security-bootstrapper:2.0.0","envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"EDGEX_USER","value":"2002"},{"name":"EDGEX_GROUP","value":"2001"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-bootstrapper"}},"strategy":{}}},{"name":"edgex-sys-mgmt-agent","service":{"ports":[{"name":"tcp-58890","protocol":"TCP","port":58890,"targetPort":58890}],"selector":{"app":"edgex-sys-mgmt-agent"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-sys-mgmt-agent"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-sys-mgmt-agent"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/sys-mgmt-agent","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-sys-mgmt-agent","image":"openyurt/sys-mgmt-agent:2.0.0","ports":[{"name":"tcp-58890","containerPort":58890,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"EXECUTORPATH","value":"/sys-mgmt-executor"},{"name":"SERVICE_HOST","value":"edgex-sys-mgmt-agent"},{"name":"METRICSMECHANISM","value":"executor"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/sys-mgmt-agent"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-sys-mgmt-agent"}},"strategy":{}}},{"name":"edgex-support-notifications","service":{"ports":[{"name":"tcp-59860","protocol":"TCP","port":59860,"targetPort":59860}],"selector":{"app":"edgex-support-notifications"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-notifications"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-notifications"}},"spec":{"volumes":[{"name":"edgex-init","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/support-notifications","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-notifications","image":"openyurt/support-notifications:2.0.0","ports":[{"name":"tcp-59860","containerPort":59860,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-ireland"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-support-notifications"}],"resources":{},"volumeMounts":[{"name":"edgex-init","mountPath":"/edgex-init"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/support-notifications"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-notifications"}},"strategy":{}}}]},{"versionName":"hanoi","configMaps":[{"metadata":{"name":"common-variable-hanoi","creationTimestamp":null},"data":{"CLIENTS_COMMAND_HOST":"edgex-core-command","CLIENTS_COREDATA_HOST":"edgex-core-data","CLIENTS_DATA_HOST":"edgex-core-data","CLIENTS_METADATA_HOST":"edgex-core-metadata","CLIENTS_NOTIFICATIONS_HOST":"edgex-support-notifications","CLIENTS_RULESENGINE_HOST":"edgex-kuiper","CLIENTS_SCHEDULER_HOST":"edgex-support-scheduler","CLIENTS_VIRTUALDEVICE_HOST":"edgex-device-virtual","DATABASES_PRIMARY_HOST":"edgex-redis","EDGEX_SECURITY_SECRET_STORE":"true","LOGGING_ENABLEREMOTE":"false","REGISTRY_HOST":"edgex-core-consul","SECRETSTORE_HOST":"edgex-vault","SECRETSTORE_ROOTCACERTPATH":"/tmp/edgex/secrets/ca/ca.pem","SECRETSTORE_SERVERNAME":"edgex-vault","SERVICE_SERVERBINDADDR":"0.0.0.0"}}],"components":[{"name":"edgex-device-rest","service":{"ports":[{"name":"tcp-49986","protocol":"TCP","port":49986,"targetPort":49986}],"selector":{"app":"edgex-device-rest"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-rest"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-rest"}},"spec":{"containers":[{"name":"edgex-device-rest","image":"openyurt/docker-device-rest-go:1.2.1","ports":[{"name":"tcp-49986","containerPort":49986,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-rest"}],"resources":{},"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-rest"}},"strategy":{}}},{"name":"edgex-core-command","service":{"ports":[{"name":"tcp-48082","protocol":"TCP","port":48082,"targetPort":48082}],"selector":{"app":"edgex-core-command"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-command"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-command"}},"spec":{"volumes":[{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/ca","type":"DirectoryOrCreate"}},{"name":"anonymous-volume2","hostPath":{"path":"/tmp/edgex/secrets/edgex-core-command","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-command","image":"openyurt/docker-core-command-go:1.3.1","ports":[{"name":"tcp-48082","containerPort":48082,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-command"},{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/edgex-core-command/secrets-token.json"}],"resources":{},"volumeMounts":[{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/ca"},{"name":"anonymous-volume2","mountPath":"/tmp/edgex/secrets/edgex-core-command"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-command"}},"strategy":{}}},{"name":"edgex-support-notifications","service":{"ports":[{"name":"tcp-48060","protocol":"TCP","port":48060,"targetPort":48060}],"selector":{"app":"edgex-support-notifications"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-notifications"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-notifications"}},"spec":{"volumes":[{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/ca","type":"DirectoryOrCreate"}},{"name":"anonymous-volume2","hostPath":{"path":"/tmp/edgex/secrets/edgex-support-notifications","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-notifications","image":"openyurt/docker-support-notifications-go:1.3.1","ports":[{"name":"tcp-48060","containerPort":48060,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/edgex-support-notifications/secrets-token.json"},{"name":"SERVICE_HOST","value":"edgex-support-notifications"}],"resources":{},"volumeMounts":[{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/ca"},{"name":"anonymous-volume2","mountPath":"/tmp/edgex/secrets/edgex-support-notifications"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-notifications"}},"strategy":{}}},{"name":"edgex-vault-worker","deployment":{"selector":{"matchLabels":{"app":"edgex-vault-worker"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-vault-worker"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"consul-scripts","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets","type":"DirectoryOrCreate"}},{"name":"vault-config","emptyDir":{}}],"containers":[{"name":"edgex-vault-worker","image":"openyurt/docker-security-secretstore-setup-go:1.3.1","envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SECRETSTORE_SETUP_DONE_FLAG","value":"/tmp/edgex/secrets/edgex-consul/.secretstore-setup-done"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/vault"},{"name":"consul-scripts","mountPath":"/consul/scripts"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets"},{"name":"vault-config","mountPath":"/vault/config"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-vault-worker"}},"strategy":{}}},{"name":"edgex-support-scheduler","service":{"ports":[{"name":"tcp-48085","protocol":"TCP","port":48085,"targetPort":48085}],"selector":{"app":"edgex-support-scheduler"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-support-scheduler"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-support-scheduler"}},"spec":{"volumes":[{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/ca","type":"DirectoryOrCreate"}},{"name":"anonymous-volume2","hostPath":{"path":"/tmp/edgex/secrets/edgex-support-scheduler","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-support-scheduler","image":"openyurt/docker-support-scheduler-go:1.3.1","ports":[{"name":"tcp-48085","containerPort":48085,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/edgex-support-scheduler/secrets-token.json"},{"name":"INTERVALACTIONS_SCRUBAGED_HOST","value":"edgex-core-data"},{"name":"INTERVALACTIONS_SCRUBPUSHED_HOST","value":"edgex-core-data"},{"name":"SERVICE_HOST","value":"edgex-support-scheduler"}],"resources":{},"volumeMounts":[{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/ca"},{"name":"anonymous-volume2","mountPath":"/tmp/edgex/secrets/edgex-support-scheduler"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-support-scheduler"}},"strategy":{}}},{"name":"edgex-secrets-setup","deployment":{"selector":{"matchLabels":{"app":"edgex-secrets-setup"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-secrets-setup"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"secrets-setup-cache","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets","type":"DirectoryOrCreate"}},{"name":"vault-init","emptyDir":{}}],"containers":[{"name":"edgex-secrets-setup","image":"openyurt/docker-security-secrets-setup-go:1.3.1","envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/tmp"},{"name":"tmpfs-volume2","mountPath":"/run"},{"name":"secrets-setup-cache","mountPath":"/etc/edgex/pki"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets"},{"name":"vault-init","mountPath":"/vault/init"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-secrets-setup"}},"strategy":{}}},{"name":"edgex-vault","service":{"ports":[{"name":"tcp-8200","protocol":"TCP","port":8200,"targetPort":8200}],"selector":{"app":"edgex-vault"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-vault"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-vault"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/edgex-vault","type":"DirectoryOrCreate"}},{"name":"vault-file","emptyDir":{}},{"name":"vault-init","emptyDir":{}},{"name":"vault-logs","emptyDir":{}}],"containers":[{"name":"edgex-vault","image":"openyurt/vault:1.5.3","ports":[{"name":"tcp-8200","containerPort":8200,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"VAULT_CONFIG_DIR","value":"/vault/config"},{"name":"VAULT_UI","value":"true"},{"name":"VAULT_ADDR","value":"https://edgex-vault:8200"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/vault/config"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/edgex-vault"},{"name":"vault-file","mountPath":"/vault/file"},{"name":"vault-init","mountPath":"/vault/init"},{"name":"vault-logs","mountPath":"/vault/logs"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-vault"}},"strategy":{}}},{"name":"edgex-core-data","service":{"ports":[{"name":"tcp-5563","protocol":"TCP","port":5563,"targetPort":5563},{"name":"tcp-48080","protocol":"TCP","port":48080,"targetPort":48080}],"selector":{"app":"edgex-core-data"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-data"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-data"}},"spec":{"volumes":[{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/ca","type":"DirectoryOrCreate"}},{"name":"anonymous-volume2","hostPath":{"path":"/tmp/edgex/secrets/edgex-core-data","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-data","image":"openyurt/docker-core-data-go:1.3.1","ports":[{"name":"tcp-5563","containerPort":5563,"protocol":"TCP"},{"name":"tcp-48080","containerPort":48080,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-data"},{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/edgex-core-data/secrets-token.json"}],"resources":{},"volumeMounts":[{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/ca"},{"name":"anonymous-volume2","mountPath":"/tmp/edgex/secrets/edgex-core-data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-data"}},"strategy":{}}},{"name":"kong","service":{"ports":[{"name":"tcp-8000","protocol":"TCP","port":8000,"targetPort":8000},{"name":"tcp-8001","protocol":"TCP","port":8001,"targetPort":8001},{"name":"tcp-8443","protocol":"TCP","port":8443,"targetPort":8443},{"name":"tcp-8444","protocol":"TCP","port":8444,"targetPort":8444}],"selector":{"app":"kong"}},"deployment":{"selector":{"matchLabels":{"app":"kong"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"kong"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"consul-scripts","emptyDir":{}},{"name":"kong","emptyDir":{}}],"containers":[{"name":"kong","image":"openyurt/kong:2.0.5","ports":[{"name":"tcp-8000","containerPort":8000,"protocol":"TCP"},{"name":"tcp-8001","containerPort":8001,"protocol":"TCP"},{"name":"tcp-8443","containerPort":8443,"protocol":"TCP"},{"name":"tcp-8444","containerPort":8444,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"KONG_ADMIN_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_ADMIN_ERROR_LOG","value":"/dev/stderr"},{"name":"KONG_ADMIN_LISTEN","value":"0.0.0.0:8001, 0.0.0.0:8444 ssl"},{"name":"KONG_DATABASE","value":"postgres"},{"name":"KONG_PG_HOST","value":"kong-db"},{"name":"KONG_PG_PASSWORD","value":"kong"},{"name":"KONG_PROXY_ACCESS_LOG","value":"/dev/stdout"},{"name":"KONG_PROXY_ERROR_LOG","value":"/dev/stderr"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"consul-scripts","mountPath":"/consul/scripts"},{"name":"kong","mountPath":"/usr/local/kong"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"kong"}},"strategy":{}}},{"name":"","deployment":{"selector":{"matchLabels":{"app":""}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":""}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"consul-scripts","emptyDir":{}}],"containers":[{"name":"","image":"openyurt/kong:2.0.5","envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"KONG_PG_PASSWORD","value":"kong"},{"name":"KONG_DATABASE","value":"postgres"},{"name":"KONG_PG_HOST","value":"kong-db"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/tmp"},{"name":"consul-scripts","mountPath":"/consul/scripts"}],"imagePullPolicy":"IfNotPresent"}]}},"strategy":{}}},{"name":"edgex-core-consul","service":{"ports":[{"name":"tcp-8500","protocol":"TCP","port":8500,"targetPort":8500}],"selector":{"app":"edgex-core-consul"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-consul"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-consul"}},"spec":{"volumes":[{"name":"consul-config","emptyDir":{}},{"name":"consul-data","emptyDir":{}},{"name":"consul-scripts","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/ca","type":"DirectoryOrCreate"}},{"name":"anonymous-volume2","hostPath":{"path":"/tmp/edgex/secrets/edgex-consul","type":"DirectoryOrCreate"}},{"name":"anonymous-volume3","hostPath":{"path":"/tmp/edgex/secrets/edgex-kong","type":"DirectoryOrCreate"}},{"name":"anonymous-volume4","hostPath":{"path":"/tmp/edgex/secrets/edgex-vault","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-consul","image":"openyurt/docker-edgex-consul:1.3.0","ports":[{"name":"tcp-8500","containerPort":8500,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SECRETSTORE_SETUP_DONE_FLAG","value":"/tmp/edgex/secrets/edgex-consul/.secretstore-setup-done"},{"name":"EDGEX_DB","value":"redis"},{"name":"EDGEX_SECURE","value":"true"}],"resources":{},"volumeMounts":[{"name":"consul-config","mountPath":"/consul/config"},{"name":"consul-data","mountPath":"/consul/data"},{"name":"consul-scripts","mountPath":"/consul/scripts"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/ca"},{"name":"anonymous-volume2","mountPath":"/tmp/edgex/secrets/edgex-consul"},{"name":"anonymous-volume3","mountPath":"/tmp/edgex/secrets/edgex-kong"},{"name":"anonymous-volume4","mountPath":"/tmp/edgex/secrets/edgex-vault"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-consul"}},"strategy":{}}},{"name":"edgex-security-bootstrap-database","deployment":{"selector":{"matchLabels":{"app":"edgex-security-bootstrap-database"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-security-bootstrap-database"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/ca","type":"DirectoryOrCreate"}},{"name":"anonymous-volume2","hostPath":{"path":"/tmp/edgex/secrets/edgex-security-bootstrap-redis","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-security-bootstrap-database","image":"openyurt/docker-security-bootstrap-redis-go:1.3.1","envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-security-bootstrap-database"},{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/edgex-security-bootstrap-redis/secrets-token.json"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/run"},{"name":"tmpfs-volume2","mountPath":"/vault"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/ca"},{"name":"anonymous-volume2","mountPath":"/tmp/edgex/secrets/edgex-security-bootstrap-redis"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-security-bootstrap-database"}},"strategy":{}}},{"name":"edgex-redis","service":{"ports":[{"name":"tcp-6379","protocol":"TCP","port":6379,"targetPort":6379}],"selector":{"app":"edgex-redis"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-redis"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-redis"}},"spec":{"volumes":[{"name":"db-data","emptyDir":{}}],"containers":[{"name":"edgex-redis","image":"openyurt/redis:6.0.9-alpine","ports":[{"name":"tcp-6379","containerPort":6379,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"resources":{},"volumeMounts":[{"name":"db-data","mountPath":"/data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-redis"}},"strategy":{}}},{"name":"edgex-device-virtual","service":{"ports":[{"name":"tcp-49990","protocol":"TCP","port":49990,"targetPort":49990}],"selector":{"app":"edgex-device-virtual"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-device-virtual"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-device-virtual"}},"spec":{"containers":[{"name":"edgex-device-virtual","image":"openyurt/docker-device-virtual-go:1.3.1","ports":[{"name":"tcp-49990","containerPort":49990,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-device-virtual"}],"resources":{},"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-device-virtual"}},"strategy":{}}},{"name":"kong-db","service":{"ports":[{"name":"tcp-5432","protocol":"TCP","port":5432,"targetPort":5432}],"selector":{"app":"kong-db"}},"deployment":{"selector":{"matchLabels":{"app":"kong-db"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"kong-db"}},"spec":{"volumes":[{"name":"tmpfs-volume1","emptyDir":{}},{"name":"tmpfs-volume2","emptyDir":{}},{"name":"tmpfs-volume3","emptyDir":{}},{"name":"postgres-data","emptyDir":{}}],"containers":[{"name":"kong-db","image":"openyurt/postgres:12.3-alpine","ports":[{"name":"tcp-5432","containerPort":5432,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"POSTGRES_DB","value":"kong"},{"name":"POSTGRES_PASSWORD","value":"kong"},{"name":"POSTGRES_USER","value":"kong"}],"resources":{},"volumeMounts":[{"name":"tmpfs-volume1","mountPath":"/var/run"},{"name":"tmpfs-volume2","mountPath":"/tmp"},{"name":"tmpfs-volume3","mountPath":"/run"},{"name":"postgres-data","mountPath":"/var/lib/postgresql/data"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"kong-db"}},"strategy":{}}},{"name":"edgex-sys-mgmt-agent","service":{"ports":[{"name":"tcp-48090","protocol":"TCP","port":48090,"targetPort":48090}],"selector":{"app":"edgex-sys-mgmt-agent"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-sys-mgmt-agent"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-sys-mgmt-agent"}},"spec":{"containers":[{"name":"edgex-sys-mgmt-agent","image":"openyurt/docker-sys-mgmt-agent-go:1.3.1","ports":[{"name":"tcp-48090","containerPort":48090,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-sys-mgmt-agent"},{"name":"EXECUTORPATH","value":"/sys-mgmt-executor"},{"name":"METRICSMECHANISM","value":"executor"}],"resources":{},"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-sys-mgmt-agent"}},"strategy":{}}},{"name":"edgex-kuiper","service":{"ports":[{"name":"tcp-20498","protocol":"TCP","port":20498,"targetPort":20498},{"name":"tcp-48075","protocol":"TCP","port":48075,"targetPort":48075}],"selector":{"app":"edgex-kuiper"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-kuiper"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-kuiper"}},"spec":{"containers":[{"name":"edgex-kuiper","image":"openyurt/kuiper:1.1.1-alpine","ports":[{"name":"tcp-20498","containerPort":20498,"protocol":"TCP"},{"name":"tcp-48075","containerPort":48075,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"EDGEX__DEFAULT__SERVICESERVER","value":"http://edgex-core-data:48080"},{"name":"EDGEX__DEFAULT__TOPIC","value":"events"},{"name":"KUIPER__BASIC__CONSOLELOG","value":"true"},{"name":"KUIPER__BASIC__RESTPORT","value":"48075"},{"name":"EDGEX__DEFAULT__PORT","value":"5566"},{"name":"EDGEX__DEFAULT__PROTOCOL","value":"tcp"},{"name":"EDGEX__DEFAULT__SERVER","value":"edgex-app-service-configurable-rules"}],"resources":{},"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-kuiper"}},"strategy":{}}},{"name":"edgex-proxy","deployment":{"selector":{"matchLabels":{"app":"edgex-proxy"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-proxy"}},"spec":{"volumes":[{"name":"consul-scripts","emptyDir":{}},{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/ca","type":"DirectoryOrCreate"}},{"name":"anonymous-volume2","hostPath":{"path":"/tmp/edgex/secrets/edgex-security-proxy-setup","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-proxy","image":"openyurt/docker-security-proxy-setup-go:1.3.1","envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SECRETSERVICE_SERVER","value":"edgex-vault"},{"name":"KONGURL_SERVER","value":"kong"},{"name":"SECRETSERVICE_SNIS","value":"edgex-kong"},{"name":"SECRETSERVICE_CACERTPATH","value":"/tmp/edgex/secrets/ca/ca.pem"},{"name":"SECRETSERVICE_TOKENPATH","value":"/tmp/edgex/secrets/edgex-security-proxy-setup/secrets-token.json"}],"resources":{},"volumeMounts":[{"name":"consul-scripts","mountPath":"/consul/scripts"},{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/ca"},{"name":"anonymous-volume2","mountPath":"/tmp/edgex/secrets/edgex-security-proxy-setup"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-proxy"}},"strategy":{}}},{"name":"edgex-app-service-configurable-rules","service":{"ports":[{"name":"tcp-48100","protocol":"TCP","port":48100,"targetPort":48100}],"selector":{"app":"edgex-app-service-configurable-rules"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-app-service-configurable-rules"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-app-service-configurable-rules"}},"spec":{"containers":[{"name":"edgex-app-service-configurable-rules","image":"openyurt/docker-app-service-configurable:1.3.1","ports":[{"name":"tcp-48100","containerPort":48100,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"BINDING_PUBLISHTOPIC","value":"events"},{"name":"SERVICE_HOST","value":"edgex-app-service-configurable-rules"},{"name":"MESSAGEBUS_SUBSCRIBEHOST_HOST","value":"edgex-core-data"},{"name":"SERVICE_PORT","value":"48100"},{"name":"EDGEX_PROFILE","value":"rules-engine"}],"resources":{},"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-app-service-configurable-rules"}},"strategy":{}}},{"name":"edgex-core-metadata","service":{"ports":[{"name":"tcp-48081","protocol":"TCP","port":48081,"targetPort":48081}],"selector":{"app":"edgex-core-metadata"}},"deployment":{"selector":{"matchLabels":{"app":"edgex-core-metadata"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"edgex-core-metadata"}},"spec":{"volumes":[{"name":"anonymous-volume1","hostPath":{"path":"/tmp/edgex/secrets/ca","type":"DirectoryOrCreate"}},{"name":"anonymous-volume2","hostPath":{"path":"/tmp/edgex/secrets/edgex-core-metadata","type":"DirectoryOrCreate"}}],"containers":[{"name":"edgex-core-metadata","image":"openyurt/docker-core-metadata-go:1.3.1","ports":[{"name":"tcp-48081","containerPort":48081,"protocol":"TCP"}],"envFrom":[{"configMapRef":{"name":"common-variable-hanoi"}}],"env":[{"name":"SERVICE_HOST","value":"edgex-core-metadata"},{"name":"SECRETSTORE_TOKENFILE","value":"/tmp/edgex/secrets/edgex-core-metadata/secrets-token.json"},{"name":"NOTIFICATIONS_SENDER","value":"edgex-core-metadata"}],"resources":{},"volumeMounts":[{"name":"anonymous-volume1","mountPath":"/tmp/edgex/secrets/ca"},{"name":"anonymous-volume2","mountPath":"/tmp/edgex/secrets/edgex-core-metadata"}],"imagePullPolicy":"IfNotPresent"}],"hostname":"edgex-core-metadata"}},"strategy":{}}}]}]} \ No newline at end of file diff --git a/pkg/controller/iot/config/EdgeXConfig/manifest.yaml b/pkg/controller/iot/config/EdgeXConfig/manifest.yaml new file mode 100644 index 00000000000..e079f05652f --- /dev/null +++ b/pkg/controller/iot/config/EdgeXConfig/manifest.yaml @@ -0,0 +1,9 @@ +updated: "false" +count: 5 +latestVersion: levski +versions: +- levski +- jakarta +- kamakura +- ireland +- hanoi diff --git a/pkg/controller/iot/config/types.go b/pkg/controller/iot/config/types.go new file mode 100644 index 00000000000..4cdea0e0c41 --- /dev/null +++ b/pkg/controller/iot/config/types.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 config + +import ( + "embed" + "encoding/json" + "path/filepath" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/klog/v2" +) + +type EdgeXConfig struct { + Versions []*Version `yaml:"versions,omitempty" json:"versions,omitempty"` +} + +type Version struct { + Name string `yaml:"versionName" json:"versionName"` + ConfigMaps []corev1.ConfigMap `yaml:"configMaps,omitempty" json:"configMaps,omitempty"` + Components []*Component `yaml:"components,omitempty" json:"components,omitempty"` +} + +type Component struct { + Name string `yaml:"name" json:"name"` + Service *corev1.ServiceSpec `yaml:"service,omitempty" json:"service,omitempty"` + Deployment *appsv1.DeploymentSpec `yaml:"deployment,omitempty" json:"deployment,omitempty"` +} + +var ( + //go:embed EdgeXConfig + EdgeXFS embed.FS + ManifestPath = filepath.Join(folder, "manifest.yaml") +) + +var ( + folder = "EdgeXConfig/" + securityFile = filepath.Join(folder, "config.json") + nosectyFile = filepath.Join(folder, "config-nosecty.json") +) + +// IoTControllerConfiguration contains elements describing IoTController. +type IoTControllerConfiguration struct { + SecurityComponents map[string][]*Component + NoSectyComponents map[string][]*Component + SecurityConfigMaps map[string][]corev1.ConfigMap + NoSectyConfigMaps map[string][]corev1.ConfigMap +} + +func NewIoTControllerConfiguration() *IoTControllerConfiguration { + var ( + edgexconfig = EdgeXConfig{} + edgexnosectyconfig = EdgeXConfig{} + conf = IoTControllerConfiguration{ + SecurityComponents: make(map[string][]*Component), + NoSectyComponents: make(map[string][]*Component), + SecurityConfigMaps: make(map[string][]corev1.ConfigMap), + NoSectyConfigMaps: make(map[string][]corev1.ConfigMap), + } + ) + + securityContent, err := EdgeXFS.ReadFile(securityFile) + if err != nil { + klog.Errorf("Fail to open the embed EdgeX security config: %v", err) + return nil + } + nosectyContent, err := EdgeXFS.ReadFile(nosectyFile) + if err != nil { + klog.Errorf("Fail to open the embed EdgeX nosecty config: %v", err) + return nil + } + + if err = json.Unmarshal(securityContent, &edgexconfig); err != nil { + klog.Errorf("Fail to unmarshal the embed EdgeX security config: %v", err) + return nil + } + for _, version := range edgexconfig.Versions { + conf.SecurityComponents[version.Name] = version.Components + conf.SecurityConfigMaps[version.Name] = version.ConfigMaps + } + + if err := json.Unmarshal(nosectyContent, &edgexnosectyconfig); err != nil { + klog.Errorf("Fail to unmarshal the embed EdgeX nosecty config: %v", err) + return nil + } + for _, version := range edgexnosectyconfig.Versions { + conf.NoSectyComponents[version.Name] = version.Components + conf.NoSectyConfigMaps[version.Name] = version.ConfigMaps + } + + return &conf +} diff --git a/pkg/controller/iot/iot_controller.go b/pkg/controller/iot/iot_controller.go new file mode 100644 index 00000000000..e2f682d51d0 --- /dev/null +++ b/pkg/controller/iot/iot_controller.go @@ -0,0 +1,566 @@ +/* +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 iot + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "time" + + appconfig "github.com/openyurtio/openyurt/cmd/yurt-manager/app/config" + unitv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + devicev1alpha1 "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha1" + devicev1alpha2 "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha2" + "github.com/openyurtio/openyurt/pkg/controller/iot/config" + util "github.com/openyurtio/openyurt/pkg/controller/iot/utils" + utilclient "github.com/openyurtio/openyurt/pkg/util/client" + utildiscovery "github.com/openyurtio/openyurt/pkg/util/discovery" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + "k8s.io/klog/v2" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +func init() { + flag.IntVar(&concurrentReconciles, "iot-workers", concurrentReconciles, "Max concurrent workers for IoT controller.") +} + +var ( + concurrentReconciles = 3 + controllerKind = devicev1alpha2.SchemeGroupVersion.WithKind("IoT") +) + +const ( + ControllerName = "IoT" + + LabelConfigmap = "Configmap" + LabelService = "Service" + LabelDeployment = "Deployment" + + AnnotationServiceTopologyKey = "openyurt.io/topologyKeys" + AnnotationServiceTopologyValueNodePool = "openyurt.io/nodepool" + + ConfigMapName = "common-variables" +) + +func Format(format string, args ...interface{}) string { + s := fmt.Sprintf(format, args...) + return fmt.Sprintf("%s: %s", ControllerName, s) +} + +// ReconcileIoT reconciles a IoT object +type ReconcileIoT struct { + client.Client + scheme *runtime.Scheme + recorder record.EventRecorder + Configration config.IoTControllerConfiguration +} + +var _ reconcile.Reconciler = &ReconcileIoT{} + +// Add creates a new IoT Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller +// and Start it when the Manager is Started. +func Add(c *appconfig.CompletedConfig, mgr manager.Manager) error { + if !utildiscovery.DiscoverGVK(controllerKind) { + return nil + } + + klog.Infof("iot-controller add controller %s", controllerKind.String()) + return add(mgr, newReconciler(c, mgr)) +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) reconcile.Reconciler { + return &ReconcileIoT{ + Client: utilclient.NewClientFromManager(mgr, ControllerName), + scheme: mgr.GetScheme(), + recorder: mgr.GetEventRecorderFor(ControllerName), + Configration: c.ComponentConfig.IoTController, + } +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + c, err := controller.New(ControllerName, mgr, controller.Options{ + Reconciler: r, MaxConcurrentReconciles: concurrentReconciles, + }) + if err != nil { + return err + } + + // Watch for changes to IoT + err = c.Watch(&source.Kind{Type: &devicev1alpha2.IoT{}}, &handler.EnqueueRequestForObject{}) + if err != nil { + return err + } + + err = c.Watch(&source.Kind{Type: &corev1.ConfigMap{}}, &handler.EnqueueRequestForOwner{ + IsController: false, + OwnerType: &devicev1alpha2.IoT{}, + }) + if err != nil { + return err + } + + err = c.Watch(&source.Kind{Type: &corev1.Service{}}, &handler.EnqueueRequestForOwner{ + IsController: false, + OwnerType: &devicev1alpha2.IoT{}, + }) + if err != nil { + return err + } + + err = c.Watch(&source.Kind{Type: &unitv1alpha1.YurtAppSet{}}, &handler.EnqueueRequestForOwner{ + IsController: false, + OwnerType: &devicev1alpha2.IoT{}, + }) + if err != nil { + return err + } + + klog.Info("registering the field indexers of iot controller") + if err := util.RegisterFieldIndexers(mgr.GetFieldIndexer()); err != nil { + klog.Errorf("failed to register field indexers for iot controller, %v", err) + return nil + } + + return nil +} + +// +kubebuilder:rbac:groups=device.openyurt.io,resources=iots,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=device.openyurt.io,resources=iots/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=device.openyurt.io,resources=iots/finalizers,verbs=update +// +kubebuilder:rbac:groups=apps.openyurt.io,resources=yurtappsets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps.openyurt.io,resources=yurtappsets/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=core,resources=configmaps;services,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=configmaps/status;services/status,verbs=get;update;patch + +// Reconcile reads that state of the cluster for a IoT object and makes changes based on the state read +// and what is in the IoT.Spec +func (r *ReconcileIoT) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { + klog.Infof(Format("Reconcile IoT %s/%s", request.Namespace, request.Name)) + + // Fetch the IoT instance + iot := &devicev1alpha2.IoT{} + if err := r.Get(ctx, request.NamespacedName, iot); err != nil { + if apierrors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return reconcile.Result{}, err + } + + defer func() { + if err := r.Status().Update(ctx, iot); err != nil { + klog.Errorf(Format("Patch status to IoT %s error %v", klog.KObj(iot), err)) + } + }() + + if iot.DeletionTimestamp != nil { + return r.reconcileDelete(ctx, iot) + } + + return r.reconcileNormal(ctx, iot) +} + +func (r *ReconcileIoT) reconcileDelete(ctx context.Context, iot *devicev1alpha2.IoT) (reconcile.Result, error) { + ud := &unitv1alpha1.YurtAppSet{} + var desiredComponents []*config.Component + if iot.Spec.Security { + desiredComponents = r.Configration.SecurityComponents[iot.Spec.Version] + } else { + desiredComponents = r.Configration.NoSectyComponents[iot.Spec.Version] + } + + additionalComponents, err := annotationToComponent(iot.Annotations) + if err != nil { + return reconcile.Result{}, err + } + desiredComponents = append(desiredComponents, additionalComponents...) + + //TODO: handle iot.Spec.Components + + for _, dc := range desiredComponents { + if err := r.Get( + ctx, + types.NamespacedName{Namespace: iot.Namespace, Name: dc.Name}, + ud); err != nil { + continue + } + + for i, pool := range ud.Spec.Topology.Pools { + if pool.Name == iot.Spec.PoolName { + ud.Spec.Topology.Pools[i] = ud.Spec.Topology.Pools[len(ud.Spec.Topology.Pools)-1] + ud.Spec.Topology.Pools = ud.Spec.Topology.Pools[:len(ud.Spec.Topology.Pools)-1] + } + } + if err := r.Update(ctx, ud); err != nil { + return reconcile.Result{}, err + } + } + + controllerutil.RemoveFinalizer(iot, devicev1alpha2.IoTFinalizer) + + return reconcile.Result{}, nil +} + +func (r *ReconcileIoT) reconcileNormal(ctx context.Context, iot *devicev1alpha2.IoT) (reconcile.Result, error) { + klog.Infof(Format("ReconcileNormal IoT %s/%s", iot.Namespace, iot.Name)) + controllerutil.AddFinalizer(iot, devicev1alpha2.IoTFinalizer) + + iot.Status.Initialized = true + klog.Infof(Format("ReconcileConfigmap IoT %s/%s", iot.Namespace, iot.Name)) + if ok, err := r.reconcileConfigmap(ctx, iot); !ok { + if err != nil { + util.SetIoTCondition(&iot.Status, util.NewIoTCondition(devicev1alpha2.ConfigmapAvailableCondition, corev1.ConditionFalse, devicev1alpha2.ConfigmapProvisioningFailedReason, err.Error())) + return reconcile.Result{}, errors.Wrapf(err, + "unexpected error while reconciling configmap for %s", iot.Namespace+"/"+iot.Name) + } + util.SetIoTCondition(&iot.Status, util.NewIoTCondition(devicev1alpha2.ConfigmapAvailableCondition, corev1.ConditionFalse, devicev1alpha2.ConfigmapProvisioningReason, "")) + return reconcile.Result{RequeueAfter: 10 * time.Second}, nil + } + util.SetIoTCondition(&iot.Status, util.NewIoTCondition(devicev1alpha2.ConfigmapAvailableCondition, corev1.ConditionTrue, "", "")) + + klog.Infof(Format("ReconcileComponent IoT %s/%s", iot.Namespace, iot.Name)) + if ok, err := r.reconcileComponent(ctx, iot); !ok { + if err != nil { + util.SetIoTCondition(&iot.Status, util.NewIoTCondition(devicev1alpha2.ComponentAvailableCondition, corev1.ConditionFalse, devicev1alpha2.ComponentProvisioningReason, err.Error())) + return reconcile.Result{}, errors.Wrapf(err, + "unexpected error while reconciling Component for %s", iot.Namespace+"/"+iot.Name) + } + util.SetIoTCondition(&iot.Status, util.NewIoTCondition(devicev1alpha2.ComponentAvailableCondition, corev1.ConditionFalse, devicev1alpha2.ComponentProvisioningReason, "")) + return reconcile.Result{RequeueAfter: 10 * time.Second}, nil + } + util.SetIoTCondition(&iot.Status, util.NewIoTCondition(devicev1alpha2.ConfigmapAvailableCondition, corev1.ConditionTrue, "", "")) + + iot.Status.Ready = true + + return reconcile.Result{}, nil +} + +func (r *ReconcileIoT) removeOwner(ctx context.Context, iot *devicev1alpha2.IoT, obj client.Object) error { + owners := obj.GetOwnerReferences() + for i, owner := range owners { + if owner.UID == iot.UID { + owners[i] = owners[len(owners)-1] + owners = owners[:len(owners)-1] + + if len(owners) == 0 { + return r.Delete(ctx, obj) + } else { + obj.SetOwnerReferences(owners) + return r.Update(ctx, obj) + } + } + } + return nil +} + +func (r *ReconcileIoT) reconcileConfigmap(ctx context.Context, iot *devicev1alpha2.IoT) (bool, error) { + var configmaps []corev1.ConfigMap + needConfigMaps := make(map[string]struct{}) + + if iot.Spec.Security { + configmaps = r.Configration.SecurityConfigMaps[iot.Spec.Version] + } else { + configmaps = r.Configration.NoSectyConfigMaps[iot.Spec.Version] + } + for _, configmap := range configmaps { + // Supplement runtime information + configmap.Namespace = iot.Namespace + configmap.Labels = make(map[string]string) + configmap.Labels[devicev1alpha2.LabelIoTGenerate] = LabelConfigmap + + _, err := controllerutil.CreateOrUpdate(ctx, r.Client, &configmap, func() error { + return controllerutil.SetOwnerReference(iot, &configmap, (r.Scheme())) + }) + if err != nil { + return false, err + } + + needConfigMaps[configmap.Name] = struct{}{} + } + + configmaplist := &corev1.ConfigMapList{} + if err := r.List(ctx, configmaplist, client.InNamespace(iot.Namespace), client.MatchingLabels{devicev1alpha2.LabelIoTGenerate: LabelConfigmap}); err == nil { + for _, c := range configmaplist.Items { + if _, ok := needConfigMaps[c.Name]; !ok { + r.removeOwner(ctx, iot, &c) + } + } + } + + return true, nil +} + +func (r *ReconcileIoT) reconcileComponent(ctx context.Context, iot *devicev1alpha2.IoT) (bool, error) { + var desireComponents []*config.Component + needComponents := make(map[string]struct{}) + var readyComponent int32 = 0 + + if iot.Spec.Security { + desireComponents = r.Configration.SecurityComponents[iot.Spec.Version] + } else { + desireComponents = r.Configration.NoSectyComponents[iot.Spec.Version] + } + + additionalComponents, err := annotationToComponent(iot.Annotations) + if err != nil { + return false, err + } + desireComponents = append(desireComponents, additionalComponents...) + + //TODO: handle iot.Spec.Components + + defer func() { + iot.Status.ReadyComponentNum = readyComponent + iot.Status.UnreadyComponentNum = int32(len(desireComponents)) - readyComponent + }() + +NextC: + for _, desireComponent := range desireComponents { + readyService := false + readyDeployment := false + needComponents[desireComponent.Name] = struct{}{} + + if _, err := r.handleService(ctx, iot, desireComponent); err != nil { + return false, err + } + readyService = true + + ud := &unitv1alpha1.YurtAppSet{} + err := r.Get( + ctx, + types.NamespacedName{ + Namespace: iot.Namespace, + Name: desireComponent.Name}, + ud) + if err != nil { + if !apierrors.IsNotFound(err) { + return false, err + } + _, err = r.handleYurtAppSet(ctx, iot, desireComponent) + if err != nil { + return false, err + } + } else { + if _, ok := ud.Status.PoolReplicas[iot.Spec.PoolName]; ok { + if ud.Status.ReadyReplicas == ud.Status.Replicas { + readyDeployment = true + if readyDeployment && readyService { + readyComponent++ + } + } + continue NextC + } + pool := unitv1alpha1.Pool{ + Name: iot.Spec.PoolName, + Replicas: pointer.Int32Ptr(1), + } + pool.NodeSelectorTerm.MatchExpressions = append(pool.NodeSelectorTerm.MatchExpressions, + corev1.NodeSelectorRequirement{ + Key: unitv1alpha1.LabelCurrentNodePool, + Operator: corev1.NodeSelectorOpIn, + Values: []string{iot.Spec.PoolName}, + }) + flag := false + for _, up := range ud.Spec.Topology.Pools { + if up.Name == pool.Name { + flag = true + break + } + } + if !flag { + ud.Spec.Topology.Pools = append(ud.Spec.Topology.Pools, pool) + } + if err := controllerutil.SetOwnerReference(iot, ud, r.Scheme()); err != nil { + return false, err + } + if err := r.Update(ctx, ud); err != nil { + return false, err + } + } + } + + // Remove the service owner that we do not need + servicelist := &corev1.ServiceList{} + if err := r.List(ctx, servicelist, client.InNamespace(iot.Namespace), client.MatchingLabels{devicev1alpha2.LabelIoTGenerate: LabelService}); err == nil { + for _, s := range servicelist.Items { + if _, ok := needComponents[s.Name]; !ok { + r.removeOwner(ctx, iot, &s) + } + } + } + + // Remove the yurtappset owner that we do not need + yurtappsetlist := &unitv1alpha1.YurtAppSetList{} + if err := r.List(ctx, yurtappsetlist, client.InNamespace(iot.Namespace), client.MatchingLabels{devicev1alpha2.LabelIoTGenerate: LabelDeployment}); err == nil { + for _, s := range yurtappsetlist.Items { + if _, ok := needComponents[s.Name]; !ok { + r.removeOwner(ctx, iot, &s) + } + } + } + + return readyComponent == int32(len(desireComponents)), nil +} + +func (r *ReconcileIoT) handleService(ctx context.Context, iot *devicev1alpha2.IoT, component *config.Component) (*corev1.Service, error) { + // It is possible that the component does not need service. + // Therefore, you need to be careful when calling this function. + // It is still possible for service to be nil when there is no error! + if component.Service == nil { + return nil, nil + } + + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Labels: make(map[string]string), + Annotations: make(map[string]string), + Name: component.Name, + Namespace: iot.Namespace, + }, + Spec: *component.Service, + } + service.Labels[devicev1alpha2.LabelIoTGenerate] = LabelService + service.Annotations[AnnotationServiceTopologyKey] = AnnotationServiceTopologyValueNodePool + + _, err := controllerutil.CreateOrUpdate( + ctx, + r.Client, + service, + func() error { + return controllerutil.SetOwnerReference(iot, service, r.Scheme()) + }, + ) + + if err != nil { + return nil, err + } + return service, nil +} + +func (r *ReconcileIoT) handleYurtAppSet(ctx context.Context, iot *devicev1alpha2.IoT, component *config.Component) (*unitv1alpha1.YurtAppSet, error) { + ud := &unitv1alpha1.YurtAppSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: make(map[string]string), + Annotations: make(map[string]string), + Name: component.Name, + Namespace: iot.Namespace, + }, + Spec: unitv1alpha1.YurtAppSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": component.Name}, + }, + WorkloadTemplate: unitv1alpha1.WorkloadTemplate{ + DeploymentTemplate: &unitv1alpha1.DeploymentTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": component.Name}, + }, + Spec: *component.Deployment, + }, + }, + }, + } + + ud.Labels[devicev1alpha2.LabelIoTGenerate] = LabelDeployment + pool := unitv1alpha1.Pool{ + Name: iot.Spec.PoolName, + Replicas: pointer.Int32Ptr(1), + } + pool.NodeSelectorTerm.MatchExpressions = append(pool.NodeSelectorTerm.MatchExpressions, + corev1.NodeSelectorRequirement{ + Key: unitv1alpha1.LabelCurrentNodePool, + Operator: corev1.NodeSelectorOpIn, + Values: []string{iot.Spec.PoolName}, + }) + ud.Spec.Topology.Pools = append(ud.Spec.Topology.Pools, pool) + if err := controllerutil.SetControllerReference(iot, ud, r.Scheme()); err != nil { + return nil, err + } + if err := r.Create(ctx, ud); err != nil { + return nil, err + } + return ud, nil +} + +// For version compatibility, v1alpha1's additionalservice and additionaldeployment are placed in +// v2alpha2's annotation, this function is to convert the annotation to component. +func annotationToComponent(annotation map[string]string) ([]*config.Component, error) { + var components []*config.Component = []*config.Component{} + var additionalDeployments []devicev1alpha1.DeploymentTemplateSpec = make([]devicev1alpha1.DeploymentTemplateSpec, 0) + if _, ok := annotation["AdditionalDeployments"]; ok { + err := json.Unmarshal([]byte(annotation["AdditionalDeployments"]), &additionalDeployments) + if err != nil { + return nil, err + } + } + var additionalServices []devicev1alpha1.ServiceTemplateSpec = make([]devicev1alpha1.ServiceTemplateSpec, 0) + if _, ok := annotation["AdditionalServices"]; ok { + err := json.Unmarshal([]byte(annotation["AdditionalServices"]), &additionalServices) + if err != nil { + return nil, err + } + } + if len(additionalDeployments) == 0 && len(additionalServices) == 0 { + return components, nil + } + var services map[string]*corev1.ServiceSpec = make(map[string]*corev1.ServiceSpec) + var usedServices map[string]struct{} = make(map[string]struct{}) + for _, additionalservice := range additionalServices { + services[additionalservice.Name] = &additionalservice.Spec + } + for _, additionalDeployment := range additionalDeployments { + var component config.Component + component.Name = additionalDeployment.Name + component.Deployment = &additionalDeployment.Spec + service, ok := services[component.Name] + if ok { + component.Service = service + usedServices[component.Name] = struct{}{} + } + components = append(components, &component) + } + if len(usedServices) < len(services) { + for name, service := range services { + _, ok := usedServices[name] + if ok { + continue + } + var component config.Component + component.Name = name + component.Service = service + components = append(components, &component) + } + } + + return components, nil +} diff --git a/pkg/controller/iot/utils/fieldindexer.go b/pkg/controller/iot/utils/fieldindexer.go new file mode 100644 index 00000000000..df178c71b3d --- /dev/null +++ b/pkg/controller/iot/utils/fieldindexer.go @@ -0,0 +1,50 @@ +/* +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" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha2" +) + +const ( + IndexerPathForNodepool = "spec.poolName" +) + +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(), &v1alpha2.IoT{}, IndexerPathForNodepool, func(rawObj client.Object) []string { + edgex, ok := rawObj.(*v1alpha2.IoT) + if ok { + return []string{edgex.Spec.PoolName} + } + return []string{} + }); err != nil { + return + } + }) + + return err +} diff --git a/pkg/controller/iot/utils/util.go b/pkg/controller/iot/utils/util.go new file mode 100644 index 00000000000..5282e32f808 --- /dev/null +++ b/pkg/controller/iot/utils/util.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" + + devicev1alpha2 "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha2" +) + +// NewIoTCondition creates a new IoT condition. +func NewIoTCondition(condType devicev1alpha2.IoTConditionType, status corev1.ConditionStatus, reason, message string) *devicev1alpha2.IoTCondition { + return &devicev1alpha2.IoTCondition{ + Type: condType, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + } +} + +// GetIoTCondition returns the condition with the provided type. +func GetIoTCondition(status devicev1alpha2.IoTStatus, condType devicev1alpha2.IoTConditionType) *devicev1alpha2.IoTCondition { + for i := range status.Conditions { + c := status.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} + +// SetIoTCondition updates the IoT 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 SetIoTCondition(status *devicev1alpha2.IoTStatus, condition *devicev1alpha2.IoTCondition) { + currentCond := GetIoTCondition(*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 []devicev1alpha2.IoTCondition, condType devicev1alpha2.IoTConditionType) []devicev1alpha2.IoTCondition { + var newConditions []devicev1alpha2.IoTCondition + for _, c := range conditions { + if c.Type == condType { + continue + } + newConditions = append(newConditions, c) + } + return newConditions +} diff --git a/pkg/webhook/iot/v1alpha1/iot_default.go b/pkg/webhook/iot/v1alpha1/iot_default.go new file mode 100644 index 00000000000..4efab31dd68 --- /dev/null +++ b/pkg/webhook/iot/v1alpha1/iot_default.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 v1alpha1 + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha1" +) + +// Default satisfies the defaulting webhook interface. +func (webhook *IoTHandler) Default(ctx context.Context, obj runtime.Object) error { + np, ok := obj.(*v1alpha1.IoT) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a IoT but got a %T", obj)) + } + + v1alpha1.SetDefaultsIoT(np) + + if np.Spec.Version == "" { + np.Spec.Version = webhook.Manifests.LatestVersion + } + + return nil +} diff --git a/pkg/webhook/iot/v1alpha1/iot_handler.go b/pkg/webhook/iot/v1alpha1/iot_handler.go new file mode 100644 index 00000000000..f73467a9b38 --- /dev/null +++ b/pkg/webhook/iot/v1alpha1/iot_handler.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 v1alpha1 + +import ( + "gopkg.in/yaml.v3" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha1" + "github.com/openyurtio/openyurt/pkg/controller/iot/config" + "github.com/openyurtio/openyurt/pkg/webhook/util" +) + +type Manifest struct { + Updated string `yaml:"updated"` + Count int `yaml:"count"` + LatestVersion string `yaml:"latestVersion"` + Versions []string `yaml:"versions"` +} + +// SetupWebhookWithManager sets up Cluster webhooks. mutate path, validatepath, error +func (webhook *IoTHandler) SetupWebhookWithManager(mgr ctrl.Manager) (string, string, error) { + // init + webhook.Client = mgr.GetClient() + + gvk, err := apiutil.GVKForObject(&v1alpha1.IoT{}, mgr.GetScheme()) + if err != nil { + return "", "", err + } + + if err := webhook.initManifest(); err != nil { + return "", "", err + } + + return util.GenerateMutatePath(gvk), + util.GenerateValidatePath(gvk), + ctrl.NewWebhookManagedBy(mgr). + For(&v1alpha1.IoT{}). + WithDefaulter(webhook). + WithValidator(webhook). + Complete() +} + +func (webhook *IoTHandler) initManifest() error { + webhook.Manifests = &Manifest{} + + manifestContent, err := config.EdgeXFS.ReadFile(config.ManifestPath) + if err != nil { + klog.Error(err, "File to open the embed EdgeX manifest file") + return err + } + + if err := yaml.Unmarshal(manifestContent, webhook.Manifests); err != nil { + klog.Error(err, "Error manifest edgeX configuration file") + return err + } + + return nil +} + +// Cluster implements a validating and defaulting webhook for Cluster. +type IoTHandler struct { + Client client.Client + Manifests *Manifest +} + +var _ webhook.CustomDefaulter = &IoTHandler{} +var _ webhook.CustomValidator = &IoTHandler{} diff --git a/pkg/webhook/iot/v1alpha1/iot_validation.go b/pkg/webhook/iot/v1alpha1/iot_validation.go new file mode 100644 index 00000000000..67cffd14d10 --- /dev/null +++ b/pkg/webhook/iot/v1alpha1/iot_validation.go @@ -0,0 +1,119 @@ +/* +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 ( + "context" + "fmt" + + unitv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha1" + util "github.com/openyurtio/openyurt/pkg/controller/iot/utils" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *IoTHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error { + np, ok := obj.(*v1alpha1.IoT) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a IoT but got a %T", obj)) + } + + //validate + if allErrs := webhook.validate(ctx, np); len(allErrs) > 0 { + return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), np.Name, allErrs) + } + + return nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *IoTHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { + newNp, ok := newObj.(*v1alpha1.IoT) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a IoT but got a %T", newObj)) + } + oldNp, ok := oldObj.(*v1alpha1.IoT) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a IoT} but got a %T", oldObj)) + } + + // validate + newErrorList := webhook.validate(ctx, newNp) + oldErrorList := webhook.validate(ctx, oldNp) + if allErrs := append(newErrorList, oldErrorList...); len(allErrs) > 0 { + return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("IoT").GroupKind(), newNp.Name, allErrs) + } + return nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *IoTHandler) ValidateDelete(_ context.Context, obj runtime.Object) error { + return nil +} + +func (webhook *IoTHandler) validate(ctx context.Context, iot *v1alpha1.IoT) field.ErrorList { + + // verify that the poolname nodepool + if nodePoolErrs := webhook.validateIoTWithNodePools(ctx, iot); nodePoolErrs != nil { + return nodePoolErrs + } + return nil +} + +func (webhook *IoTHandler) validateIoTWithNodePools(ctx context.Context, iot *v1alpha1.IoT) field.ErrorList { + // verify that the poolname is a right nodepool name + nodePools := &unitv1alpha1.NodePoolList{} + if err := webhook.Client.List(ctx, nodePools); err != nil { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), iot.Spec.PoolName, "can not list nodepools, cause"+err.Error()), + } + } + ok := false + for _, nodePool := range nodePools.Items { + if nodePool.ObjectMeta.Name == iot.Spec.PoolName { + ok = true + break + } + } + if !ok { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), iot.Spec.PoolName, "can not find the nodePool"), + } + } + // verify that no other edgex in the nodepool + var iots v1alpha1.IoTList + listOptions := client.MatchingFields{util.IndexerPathForNodepool: iot.Spec.PoolName} + if err := webhook.Client.List(ctx, &iots, listOptions); err != nil { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), iot.Spec.PoolName, "can not list edgexes, cause"+err.Error()), + } + } + for _, other := range iots.Items { + if iot.Name != other.Name { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), iot.Spec.PoolName, "already used by other edgex instance,"), + } + } + } + + return nil + +} diff --git a/pkg/webhook/iot/v1alpha2/iot_default.go b/pkg/webhook/iot/v1alpha2/iot_default.go new file mode 100644 index 00000000000..36f8574388f --- /dev/null +++ b/pkg/webhook/iot/v1alpha2/iot_default.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 v1alpha2 + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha2" +) + +// Default satisfies the defaulting webhook interface. +func (webhook *IoTHandler) Default(ctx context.Context, obj runtime.Object) error { + iot, ok := obj.(*v1alpha2.IoT) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a IoT but got a %T", obj)) + } + + v1alpha2.SetDefaultsIoT(iot) + + if iot.Spec.Version == "" { + iot.Spec.Version = webhook.Manifests.LatestVersion + } + + if iot.Spec.Platform == "" { + iot.Spec.Platform = v1alpha2.IoTPlatformEdgeX + } + + return nil +} diff --git a/pkg/webhook/iot/v1alpha2/iot_handler.go b/pkg/webhook/iot/v1alpha2/iot_handler.go new file mode 100644 index 00000000000..847d8b5f312 --- /dev/null +++ b/pkg/webhook/iot/v1alpha2/iot_handler.go @@ -0,0 +1,89 @@ +/* +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 v1alpha2 + +import ( + "gopkg.in/yaml.v3" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha2" + "github.com/openyurtio/openyurt/pkg/controller/iot/config" + "github.com/openyurtio/openyurt/pkg/webhook/util" +) + +type Manifest struct { + Updated string `yaml:"updated"` + Count int `yaml:"count"` + LatestVersion string `yaml:"latestVersion"` + Versions []string `yaml:"versions"` +} + +// SetupWebhookWithManager sets up Cluster webhooks. mutate path, validatepath, error +func (webhook *IoTHandler) SetupWebhookWithManager(mgr ctrl.Manager) (string, string, error) { + // init + webhook.Client = mgr.GetClient() + + gvk, err := apiutil.GVKForObject(&v1alpha2.IoT{}, mgr.GetScheme()) + if err != nil { + return "", "", err + } + + if err := webhook.initManifest(); err != nil { + return "", "", err + } + + return util.GenerateMutatePath(gvk), + util.GenerateValidatePath(gvk), + ctrl.NewWebhookManagedBy(mgr). + For(&v1alpha2.IoT{}). + WithDefaulter(webhook). + WithValidator(webhook). + Complete() +} + +func (webhook *IoTHandler) initManifest() error { + webhook.Manifests = &Manifest{} + + manifestContent, err := config.EdgeXFS.ReadFile(config.ManifestPath) + if err != nil { + klog.Error(err, "File to open the embed EdgeX manifest file") + return err + } + + if err := yaml.Unmarshal(manifestContent, webhook.Manifests); err != nil { + klog.Error(err, "Error manifest edgeX configuration file") + return err + } + + return nil +} + +// +kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha2-iot,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1,groups=device.openyurt.io,resources=iots,verbs=create;update,versions=v1alpha2,name=viot.kb.io +// +kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha2-iot,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1,groups=device.openyurt.io,resources=iots,verbs=create;update,versions=v1alpha2,name=miot.kb.io + +// Cluster implements a validating and defaulting webhook for Cluster. +type IoTHandler struct { + Client client.Client + Manifests *Manifest +} + +var _ webhook.CustomDefaulter = &IoTHandler{} +var _ webhook.CustomValidator = &IoTHandler{} diff --git a/pkg/webhook/iot/v1alpha2/iot_validation.go b/pkg/webhook/iot/v1alpha2/iot_validation.go new file mode 100644 index 00000000000..6851d603c41 --- /dev/null +++ b/pkg/webhook/iot/v1alpha2/iot_validation.go @@ -0,0 +1,144 @@ +/* +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 v1alpha2 + +import ( + "context" + "fmt" + "strings" + + unitv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "github.com/openyurtio/openyurt/pkg/apis/device/v1alpha2" + util "github.com/openyurtio/openyurt/pkg/controller/iot/utils" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *IoTHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error { + np, ok := obj.(*v1alpha2.IoT) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a IoT but got a %T", obj)) + } + + //validate + if allErrs := webhook.validate(ctx, np); len(allErrs) > 0 { + return apierrors.NewInvalid(v1alpha2.GroupVersion.WithKind("EdgeX").GroupKind(), np.Name, allErrs) + } + + return nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *IoTHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { + newNp, ok := newObj.(*v1alpha2.IoT) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a IoT but got a %T", newObj)) + } + oldNp, ok := oldObj.(*v1alpha2.IoT) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a IoT} but got a %T", oldObj)) + } + + // validate + newErrorList := webhook.validate(ctx, newNp) + oldErrorList := webhook.validate(ctx, oldNp) + if allErrs := append(newErrorList, oldErrorList...); len(allErrs) > 0 { + return apierrors.NewInvalid(v1alpha2.GroupVersion.WithKind("IoT").GroupKind(), newNp.Name, allErrs) + } + return nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *IoTHandler) ValidateDelete(_ context.Context, obj runtime.Object) error { + return nil +} + +func (webhook *IoTHandler) validate(ctx context.Context, iot *v1alpha2.IoT) field.ErrorList { + + // verify the version + if specErrs := webhook.validateIoTSpec(iot); specErrs != nil { + return specErrs + } + // verify that the poolname nodepool + if nodePoolErrs := webhook.validateIoTWithNodePools(ctx, iot); nodePoolErrs != nil { + return nodePoolErrs + } + return nil +} + +func (webhook *IoTHandler) validateIoTSpec(iot *v1alpha2.IoT) field.ErrorList { + // TODO: Need to divert traffic based on the type of platform + + // Verify that the platform is supported + if iot.Spec.Platform != v1alpha2.IoTPlatformEdgeX { + return field.ErrorList{field.Invalid(field.NewPath("spec", "platform"), iot.Spec.Version, "must be "+v1alpha2.IoTPlatformEdgeX)} + } + + // Verify that it is a supported edgex version + for _, version := range webhook.Manifests.Versions { + if iot.Spec.Version == version { + return nil + } + } + + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "version"), iot.Spec.Version, "must be one of"+strings.Join(webhook.Manifests.Versions, ",")), + } +} + +func (webhook *IoTHandler) validateIoTWithNodePools(ctx context.Context, iot *v1alpha2.IoT) field.ErrorList { + // verify that the poolname is a right nodepool name + nodePools := &unitv1alpha1.NodePoolList{} + if err := webhook.Client.List(ctx, nodePools); err != nil { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), iot.Spec.PoolName, "can not list nodepools, cause"+err.Error()), + } + } + ok := false + for _, nodePool := range nodePools.Items { + if nodePool.ObjectMeta.Name == iot.Spec.PoolName { + ok = true + break + } + } + if !ok { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), iot.Spec.PoolName, "can not find the nodePool"), + } + } + // verify that no other edgex in the nodepool + var iots v1alpha2.IoTList + listOptions := client.MatchingFields{util.IndexerPathForNodepool: iot.Spec.PoolName} + if err := webhook.Client.List(ctx, &iots, listOptions); err != nil { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), iot.Spec.PoolName, "can not list edgexes, cause "+err.Error()), + } + } + for _, other := range iots.Items { + if iot.Name != other.Name { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), iot.Spec.PoolName, "already used by other edgex instance,"), + } + } + } + + return nil + +} diff --git a/pkg/webhook/server.go b/pkg/webhook/server.go index 74a83b8e005..f368312ba50 100644 --- a/pkg/webhook/server.go +++ b/pkg/webhook/server.go @@ -21,11 +21,8 @@ import ( "fmt" "time" - "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/manager" - "github.com/openyurtio/openyurt/cmd/yurt-manager/app/config" + "github.com/openyurtio/openyurt/pkg/controller/iot" "github.com/openyurtio/openyurt/pkg/controller/nodepool" "github.com/openyurtio/openyurt/pkg/controller/raven" ctrlutil "github.com/openyurtio/openyurt/pkg/controller/util" @@ -33,6 +30,8 @@ import ( "github.com/openyurtio/openyurt/pkg/controller/yurtappset" "github.com/openyurtio/openyurt/pkg/controller/yurtstaticset" v1alpha1gateway "github.com/openyurtio/openyurt/pkg/webhook/gateway/v1alpha1" + v1alpha1iot "github.com/openyurtio/openyurt/pkg/webhook/iot/v1alpha1" + v1alpha2iot "github.com/openyurtio/openyurt/pkg/webhook/iot/v1alpha2" v1alpha1nodepool "github.com/openyurtio/openyurt/pkg/webhook/nodepool/v1alpha1" v1beta1nodepool "github.com/openyurtio/openyurt/pkg/webhook/nodepool/v1beta1" v1pod "github.com/openyurtio/openyurt/pkg/webhook/pod/v1" @@ -41,6 +40,9 @@ import ( v1alpha1yurtappdaemon "github.com/openyurtio/openyurt/pkg/webhook/yurtappdaemon/v1alpha1" v1alpha1yurtappset "github.com/openyurtio/openyurt/pkg/webhook/yurtappset/v1alpha1" v1alpha1yurtstaticset "github.com/openyurtio/openyurt/pkg/webhook/yurtstaticset/v1alpha1" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/manager" ) type SetupWebhookWithManager interface { @@ -75,6 +77,8 @@ func init() { addControllerWebhook(yurtstaticset.ControllerName, &v1alpha1yurtstaticset.YurtStaticSetHandler{}) addControllerWebhook(yurtappset.ControllerName, &v1alpha1yurtappset.YurtAppSetHandler{}) addControllerWebhook(yurtappdaemon.ControllerName, &v1alpha1yurtappdaemon.YurtAppDaemonHandler{}) + addControllerWebhook(iot.ControllerName, &v1alpha1iot.IoTHandler{}) + addControllerWebhook(iot.ControllerName, &v1alpha2iot.IoTHandler{}) independentWebhooks[v1pod.WebhookName] = &v1pod.PodHandler{} }