From dc3ab9e8b96d1ef36a9d33d6f9951da2dff3323b Mon Sep 17 00:00:00 2001 From: David Grove Date: Thu, 30 Nov 2023 11:14:49 -0500 Subject: [PATCH] add CRD fields to Spec from upstream types (#48) * align Spec definitions to upstream CRDs * remove validation:EmbeddedResource from GenericTemplate Annotation is present in the upstream, but causes test failures here. Leave it out for now. --- api/v1beta1/appwrapper_types.go | 60 ++- api/v1beta1/zz_generated.deepcopy.go | 62 ++- .../workload.codeflare.dev_appwrappers.yaml | 443 +++++++++++++++++- 3 files changed, 543 insertions(+), 22 deletions(-) diff --git a/api/v1beta1/appwrapper_types.go b/api/v1beta1/appwrapper_types.go index 27d5a52..c693e95 100644 --- a/api/v1beta1/appwrapper_types.go +++ b/api/v1beta1/appwrapper_types.go @@ -31,34 +31,61 @@ type AppWrapperSpec struct { // Priority slope DoNotUsePrioritySlope resource.Quantity `json:"priorityslope,omitempty"` - // Scheduling specification - Scheduling SchedulingSpec `json:"schedulingSpec,omitempty"` + DoNotUseService AppWrapperService `json:"service,omitempty"` // Wrapped resources Resources AppWrapperResources `json:"resources"` + + DoNotUseSelector *metav1.LabelSelector `json:"selector,omitempty"` + + // Scheduling specifies the parameters used for scheduling the wrapped resources. + // It defines the policy for requeuing jobs based on the number of running pods. + Scheduling SchedulingSpec `json:"schedulingSpec,omitempty"` } type SchedulingSpec struct { + DoNotUseNodeSelector map[string]string `json:"nodeSelector,omitempty"` + // Minimum number of expected running and successful pods MinAvailable int32 `json:"minAvailable,omitempty"` // Requeuing specification Requeuing RequeuingSpec `json:"requeuing,omitempty"` + + DoNotUseDispatchDuration DoNotUseDispatchDurationSpec `json:"dispatchDuration,omitempty"` +} + +type DoNotUseDispatchDurationSpec struct { + Expected int32 `json:"expected,omitempty"` + Limit int32 `json:"limit,omitempty"` + Overrun bool `json:"overrun,omitempty"` } type RequeuingSpec struct { + DoNotUseInitialTimeInSeconds int64 `json:"initialTimeInSeconds,omitempty"` + // Initial waiting time before requeuing conditions are checked // +kubebuilder:default=300 TimeInSeconds int64 `json:"timeInSeconds,omitempty"` + // +kubebuilder:default=0 + DoNotUseMaxTimeInSeconds int64 `json:"maxTimeInSeconds,omitempty"` + + // +kubebuilder:default=exponential + DoNotUseGrowthType string `json:"growthType,omitempty"` + + // +kubebuilder:default=0 + DoNotUseNumRequeuings int32 `json:"numRequeuings,omitempty"` + + // Max requeuings permitted (infinite if zero) + // +kubebuilder:default=0 + MaxNumRequeuings int32 `json:"maxNumRequeuings,omitempty"` + // Enable forced deletion after delay if nonzero ForceDeletionTimeInSeconds int64 `json:"forceDeletionTimeInSeconds,omitempty"` // Wait time before trying to dispatch again after requeuing PauseTimeInSeconds int64 `json:"pauseTimeInSeconds,omitempty"` - - // Max requeuings permitted (infinite if zero) - MaxNumRequeuings int32 `json:"maxNumRequeuings,omitempty"` } // AppWrapperStatus defines the observed state of AppWrapper @@ -120,24 +147,37 @@ const ( Deleting AppWrapperStep = "deleting" ) +// AppWrapperService +type AppWrapperService struct { + Spec v1.ServiceSpec `json:"spec"` +} + // AppWrapper resources type AppWrapperResources struct { - // Array of GenericItems - GenericItems []GenericItem `json:"GenericItems"` + GenericItems []GenericItem `json:"GenericItems,omitempty"` } // AppWrapper resource type GenericItem struct { DoNotUseReplicas int32 `json:"replicas,omitempty"` + DoNotUseMinAvailable *int32 `json:"minavailable,omitempty"` + + DoNotUseAllocated int32 `json:"allocated,omitempty"` + + DoNotUsePriority int32 `json:"priority,omitempty"` + + DoNotUsePrioritySlope resource.Quantity `json:"priorityslope,omitempty"` + + // The template for the resource + // +kubebuilder:pruning:PreserveUnknownFields + GenericTemplate runtime.RawExtension `json:"generictemplate,omitempty"` + // Array of resource requests CustomPodResources []CustomPodResource `json:"custompodresources,omitempty"` // A comma-separated list of keywords to match against condition types CompletionStatus string `json:"completionstatus,omitempty"` - - // Resource template - GenericTemplate runtime.RawExtension `json:"generictemplate"` } // Resource requests diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index bef42bf..aee4323 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -22,7 +22,8 @@ limitations under the License. package v1beta1 import ( - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -107,12 +108,34 @@ func (in *AppWrapperResources) DeepCopy() *AppWrapperResources { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AppWrapperService) DeepCopyInto(out *AppWrapperService) { + *out = *in + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppWrapperService. +func (in *AppWrapperService) DeepCopy() *AppWrapperService { + if in == nil { + return nil + } + out := new(AppWrapperService) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AppWrapperSpec) DeepCopyInto(out *AppWrapperSpec) { *out = *in out.DoNotUsePrioritySlope = in.DoNotUsePrioritySlope.DeepCopy() - out.Scheduling = in.Scheduling + in.DoNotUseService.DeepCopyInto(&out.DoNotUseService) in.Resources.DeepCopyInto(&out.Resources) + if in.DoNotUseSelector != nil { + in, out := &in.DoNotUseSelector, &out.DoNotUseSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + in.Scheduling.DeepCopyInto(&out.Scheduling) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppWrapperSpec. @@ -170,14 +193,14 @@ func (in *CustomPodResource) DeepCopyInto(out *CustomPodResource) { *out = *in if in.Requests != nil { in, out := &in.Requests, &out.Requests - *out = make(v1.ResourceList, len(*in)) + *out = make(corev1.ResourceList, len(*in)) for key, val := range *in { (*out)[key] = val.DeepCopy() } } if in.DoNotUseLimits != nil { in, out := &in.DoNotUseLimits, &out.DoNotUseLimits - *out = make(v1.ResourceList, len(*in)) + *out = make(corev1.ResourceList, len(*in)) for key, val := range *in { (*out)[key] = val.DeepCopy() } @@ -194,9 +217,31 @@ func (in *CustomPodResource) DeepCopy() *CustomPodResource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DoNotUseDispatchDurationSpec) DeepCopyInto(out *DoNotUseDispatchDurationSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DoNotUseDispatchDurationSpec. +func (in *DoNotUseDispatchDurationSpec) DeepCopy() *DoNotUseDispatchDurationSpec { + if in == nil { + return nil + } + out := new(DoNotUseDispatchDurationSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GenericItem) DeepCopyInto(out *GenericItem) { *out = *in + if in.DoNotUseMinAvailable != nil { + in, out := &in.DoNotUseMinAvailable, &out.DoNotUseMinAvailable + *out = new(int32) + **out = **in + } + out.DoNotUsePrioritySlope = in.DoNotUsePrioritySlope.DeepCopy() + in.GenericTemplate.DeepCopyInto(&out.GenericTemplate) if in.CustomPodResources != nil { in, out := &in.CustomPodResources, &out.CustomPodResources *out = make([]CustomPodResource, len(*in)) @@ -204,7 +249,6 @@ func (in *GenericItem) DeepCopyInto(out *GenericItem) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.GenericTemplate.DeepCopyInto(&out.GenericTemplate) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericItem. @@ -235,7 +279,15 @@ func (in *RequeuingSpec) DeepCopy() *RequeuingSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SchedulingSpec) DeepCopyInto(out *SchedulingSpec) { *out = *in + if in.DoNotUseNodeSelector != nil { + in, out := &in.DoNotUseNodeSelector, &out.DoNotUseNodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } out.Requeuing = in.Requeuing + out.DoNotUseDispatchDuration = in.DoNotUseDispatchDuration } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchedulingSpec. diff --git a/config/crd/bases/workload.codeflare.dev_appwrappers.yaml b/config/crd/bases/workload.codeflare.dev_appwrappers.yaml index 23c5f87..8180d03 100644 --- a/config/crd/bases/workload.codeflare.dev_appwrappers.yaml +++ b/config/crd/bases/workload.codeflare.dev_appwrappers.yaml @@ -59,10 +59,12 @@ spec: description: Wrapped resources properties: GenericItems: - description: Array of GenericItems items: description: AppWrapper resource properties: + allocated: + format: int32 + type: integer completionstatus: description: A comma-separated list of keywords to match against condition types @@ -100,27 +102,52 @@ spec: type: object type: array generictemplate: - description: Resource template + description: The template for the resource type: object x-kubernetes-preserve-unknown-fields: true + minavailable: + format: int32 + type: integer + priority: + format: int32 + type: integer + priorityslope: + 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 replicas: format: int32 type: integer - required: - - generictemplate type: object type: array - required: - - GenericItems type: object schedulingSpec: - description: Scheduling specification + description: Scheduling specifies the parameters used for scheduling + the wrapped resources. It defines the policy for requeuing jobs + based on the number of running pods. properties: + dispatchDuration: + properties: + expected: + format: int32 + type: integer + limit: + format: int32 + type: integer + overrun: + type: boolean + type: object minAvailable: description: Minimum number of expected running and successful pods format: int32 type: integer + nodeSelector: + additionalProperties: + type: string + type: object requeuing: description: Requeuing specification properties: @@ -128,10 +155,25 @@ spec: description: Enable forced deletion after delay if nonzero format: int64 type: integer + growthType: + default: exponential + type: string + initialTimeInSeconds: + format: int64 + type: integer maxNumRequeuings: + default: 0 description: Max requeuings permitted (infinite if zero) format: int32 type: integer + maxTimeInSeconds: + default: 0 + format: int64 + type: integer + numRequeuings: + default: 0 + format: int32 + type: integer pauseTimeInSeconds: description: Wait time before trying to dispatch again after requeuing @@ -145,6 +187,393 @@ spec: type: integer type: object type: object + selector: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + 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 + x-kubernetes-map-type: atomic + service: + description: AppWrapperService + properties: + 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. + 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. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + 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. This field will be wiped when updating + a Service to type ExternalName. If this field is not specified, + it will be initialized from the clusterIP field. If this + field is specified, clients must ensure that clusterIPs[0] + and clusterIP have the same value. \n This field may hold + a maximum of two entries (dual-stack IPs, in either order). + These IPs must correspond to the values of the ipFamilies + field. Both clusterIPs and ipFamilies are governed by the + ipFamilyPolicy field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies" + 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 describes how nodes distribute + service traffic they receive on one of the Service's "externally-facing" + addresses (NodePorts, ExternalIPs, and LoadBalancer IPs). + If set to "Local", the proxy will configure the service + in a way that assumes that external load balancers will + take care of balancing the service traffic between nodes, + and so each node will deliver traffic only to the node-local + endpoints of the service, without masquerading the client + source IP. (Traffic mistakenly sent to a node with no endpoints + will be dropped.) The default value, "Cluster", uses the + standard behavior of routing to all endpoints evenly (possibly + modified by topology and other features). Note that traffic + sent to an External IP or LoadBalancer IP from within the + cluster will always get "Cluster" semantics, but clients + sending to a NodePort from within the cluster may need to + take traffic policy into account when picking a node. + 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). This field cannot + be updated once set. + format: int32 + type: integer + internalTrafficPolicy: + description: InternalTrafficPolicy describes how nodes distribute + service traffic they receive on the ClusterIP. If set to + "Local", the proxy will assume that pods only want to talk + to endpoints of the service on the same node as the pod, + dropping the traffic if there are no local endpoints. The + default value, "Cluster", uses the standard behavior of + routing to all endpoints evenly (possibly modified by topology + and other features). + type: string + ipFamilies: + description: "IPFamilies is a list of IP families (e.g. IPv4, + IPv6) assigned to this service. 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. Both + clusterIPs and ipFamilies are governed by the ipFamilyPolicy + field." + 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. 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. + 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. Deprecated: This field was + under-specified and its meaning varies across implementations, + and it cannot support dual-stack. As of Kubernetes v1.24, + users are encouraged to use implementation-specific annotations + when available. This field may be removed in a future API + version.' + 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 https://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.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + required: + - spec + type: object required: - resources type: object