Skip to content

Commit 42a1eb2

Browse files
authoredNov 1, 2023
Merge pull request #410 from application-stacks/topology
Add Topology Spread Constraints support
2 parents 96eb9e6 + 5a08518 commit 42a1eb2

10 files changed

+880
-1
lines changed
 

‎api/v1/runtimecomponent_types.go

+33
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,20 @@ type RuntimeComponentSpec struct {
146146
// Security context for the application container.
147147
// +operator-sdk:csv:customresourcedefinitions:order=25,type=spec,displayName="Security Context"
148148
SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"`
149+
150+
// +operator-sdk:csv:customresourcedefinitions:order=26,type=spec,displayName="Topology Spread Constraints"
151+
TopologySpreadConstraints *RuntimeComponentTopologySpreadConstraints `json:"topologySpreadConstraints,omitempty"`
152+
}
153+
154+
// Defines the topology spread constraints
155+
type RuntimeComponentTopologySpreadConstraints struct {
156+
// The list of TopologySpreadConstraints for the application pod.
157+
// +operator-sdk:csv:customresourcedefinitions:order=1,type=spec,displayName="Constraints"
158+
Constraints *[]corev1.TopologySpreadConstraint `json:"constraints,omitempty"`
159+
160+
// Whether the operator should disable its default set of TopologySpreadConstraints. Defaults to false.
161+
// +operator-sdk:csv:customresourcedefinitions:order=1,type=spec,displayName="Disable Operator Defaults",xDescriptors="urn:alm:descriptor:com.tectonic.ui:booleanSwitch"
162+
DisableOperatorDefaults *bool `json:"disableOperatorDefaults,omitempty"`
149163
}
150164

151165
// Defines the service account
@@ -917,6 +931,25 @@ func (cr *RuntimeComponent) GetSecurityContext() *corev1.SecurityContext {
917931
return cr.Spec.SecurityContext
918932
}
919933

934+
// GetTopologySpreadConstraints returns the pod topology spread constraints configuration
935+
func (cr *RuntimeComponent) GetTopologySpreadConstraints() common.BaseComponentTopologySpreadConstraints {
936+
if cr.Spec.TopologySpreadConstraints == nil {
937+
return nil
938+
}
939+
return cr.Spec.TopologySpreadConstraints
940+
}
941+
942+
func (cr *RuntimeComponentTopologySpreadConstraints) GetConstraints() *[]corev1.TopologySpreadConstraint {
943+
if cr.Constraints == nil {
944+
return nil
945+
}
946+
return cr.Constraints
947+
}
948+
949+
func (cr *RuntimeComponentTopologySpreadConstraints) GetDisableOperatorDefaults() *bool {
950+
return cr.DisableOperatorDefaults
951+
}
952+
920953
// Initialize the RuntimeComponent instance
921954
func (cr *RuntimeComponent) Initialize() {
922955
if cr.Spec.PullPolicy == nil {

‎api/v1/zz_generated.deepcopy.go

+36
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎bundle/manifests/rc.app.stacks_runtimecomponents.yaml

+183
Original file line numberDiff line numberDiff line change
@@ -5428,6 +5428,189 @@ spec:
54285428
type: string
54295429
type: object
54305430
type: object
5431+
topologySpreadConstraints:
5432+
description: Defines the topology spread constraints
5433+
properties:
5434+
constraints:
5435+
description: The list of TopologySpreadConstraints for the application
5436+
pod.
5437+
items:
5438+
description: TopologySpreadConstraint specifies how to spread
5439+
matching pods among the given topology.
5440+
properties:
5441+
labelSelector:
5442+
description: LabelSelector is used to find matching pods.
5443+
Pods that match this label selector are counted to determine
5444+
the number of pods in their corresponding topology domain.
5445+
properties:
5446+
matchExpressions:
5447+
description: matchExpressions is a list of label selector
5448+
requirements. The requirements are ANDed.
5449+
items:
5450+
description: A label selector requirement is a selector
5451+
that contains values, a key, and an operator that
5452+
relates the key and values.
5453+
properties:
5454+
key:
5455+
description: key is the label key that the selector
5456+
applies to.
5457+
type: string
5458+
operator:
5459+
description: operator represents a key's relationship
5460+
to a set of values. Valid operators are In,
5461+
NotIn, Exists and DoesNotExist.
5462+
type: string
5463+
values:
5464+
description: values is an array of string values.
5465+
If the operator is In or NotIn, the values array
5466+
must be non-empty. If the operator is Exists
5467+
or DoesNotExist, the values array must be empty.
5468+
This array is replaced during a strategic merge
5469+
patch.
5470+
items:
5471+
type: string
5472+
type: array
5473+
required:
5474+
- key
5475+
- operator
5476+
type: object
5477+
type: array
5478+
matchLabels:
5479+
additionalProperties:
5480+
type: string
5481+
description: matchLabels is a map of {key,value} pairs.
5482+
A single {key,value} in the matchLabels map is equivalent
5483+
to an element of matchExpressions, whose key field
5484+
is "key", the operator is "In", and the values array
5485+
contains only "value". The requirements are ANDed.
5486+
type: object
5487+
type: object
5488+
x-kubernetes-map-type: atomic
5489+
matchLabelKeys:
5490+
description: MatchLabelKeys is a set of pod label keys to
5491+
select the pods over which spreading will be calculated.
5492+
The keys are used to lookup values from the incoming pod
5493+
labels, those key-value labels are ANDed with labelSelector
5494+
to select the group of existing pods over which spreading
5495+
will be calculated for the incoming pod. Keys that don't
5496+
exist in the incoming pod labels will be ignored. A null
5497+
or empty list means only match against labelSelector.
5498+
items:
5499+
type: string
5500+
type: array
5501+
x-kubernetes-list-type: atomic
5502+
maxSkew:
5503+
description: 'MaxSkew describes the degree to which pods
5504+
may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`,
5505+
it is the maximum permitted difference between the number
5506+
of matching pods in the target topology and the global
5507+
minimum. The global minimum is the minimum number of matching
5508+
pods in an eligible domain or zero if the number of eligible
5509+
domains is less than MinDomains. For example, in a 3-zone
5510+
cluster, MaxSkew is set to 1, and pods with the same labelSelector
5511+
spread as 2/2/1: In this case, the global minimum is 1.
5512+
| zone1 | zone2 | zone3 | | P P | P P | P | -
5513+
if MaxSkew is 1, incoming pod can only be scheduled to
5514+
zone3 to become 2/2/2; scheduling it onto zone1(zone2)
5515+
would make the ActualSkew(3-1) on zone1(zone2) violate
5516+
MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled
5517+
onto any zone. When `whenUnsatisfiable=ScheduleAnyway`,
5518+
it is used to give higher precedence to topologies that
5519+
satisfy it. It''s a required field. Default value is 1
5520+
and 0 is not allowed.'
5521+
format: int32
5522+
type: integer
5523+
minDomains:
5524+
description: "MinDomains indicates a minimum number of eligible
5525+
domains. When the number of eligible domains with matching
5526+
topology keys is less than minDomains, Pod Topology Spread
5527+
treats \"global minimum\" as 0, and then the calculation
5528+
of Skew is performed. And when the number of eligible
5529+
domains with matching topology keys equals or greater
5530+
than minDomains, this value has no effect on scheduling.
5531+
As a result, when the number of eligible domains is less
5532+
than minDomains, scheduler won't schedule more than maxSkew
5533+
Pods to those domains. If value is nil, the constraint
5534+
behaves as if MinDomains is equal to 1. Valid values are
5535+
integers greater than 0. When value is not nil, WhenUnsatisfiable
5536+
must be DoNotSchedule. \n For example, in a 3-zone cluster,
5537+
MaxSkew is set to 2, MinDomains is set to 5 and pods with
5538+
the same labelSelector spread as 2/2/2: | zone1 | zone2
5539+
| zone3 | | P P | P P | P P | The number of domains
5540+
is less than 5(MinDomains), so \"global minimum\" is treated
5541+
as 0. In this situation, new pod with the same labelSelector
5542+
cannot be scheduled, because computed skew will be 3(3
5543+
- 0) if new Pod is scheduled to any of the three zones,
5544+
it will violate MaxSkew. \n This is a beta field and requires
5545+
the MinDomainsInPodTopologySpread feature gate to be enabled
5546+
(enabled by default)."
5547+
format: int32
5548+
type: integer
5549+
nodeAffinityPolicy:
5550+
description: "NodeAffinityPolicy indicates how we will treat
5551+
Pod's nodeAffinity/nodeSelector when calculating pod topology
5552+
spread skew. Options are: - Honor: only nodes matching
5553+
nodeAffinity/nodeSelector are included in the calculations.
5554+
- Ignore: nodeAffinity/nodeSelector are ignored. All nodes
5555+
are included in the calculations. \n If this value is
5556+
nil, the behavior is equivalent to the Honor policy. This
5557+
is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread
5558+
feature flag."
5559+
type: string
5560+
nodeTaintsPolicy:
5561+
description: "NodeTaintsPolicy indicates how we will treat
5562+
node taints when calculating pod topology spread skew.
5563+
Options are: - Honor: nodes without taints, along with
5564+
tainted nodes for which the incoming pod has a toleration,
5565+
are included. - Ignore: node taints are ignored. All nodes
5566+
are included. \n If this value is nil, the behavior is
5567+
equivalent to the Ignore policy. This is a alpha-level
5568+
feature enabled by the NodeInclusionPolicyInPodTopologySpread
5569+
feature flag."
5570+
type: string
5571+
topologyKey:
5572+
description: TopologyKey is the key of node labels. Nodes
5573+
that have a label with this key and identical values are
5574+
considered to be in the same topology. We consider each
5575+
<key, value> as a "bucket", and try to put balanced number
5576+
of pods into each bucket. We define a domain as a particular
5577+
instance of a topology. Also, we define an eligible domain
5578+
as a domain whose nodes meet the requirements of nodeAffinityPolicy
5579+
and nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname",
5580+
each Node is a domain of that topology. And, if TopologyKey
5581+
is "topology.kubernetes.io/zone", each zone is a domain
5582+
of that topology. It's a required field.
5583+
type: string
5584+
whenUnsatisfiable:
5585+
description: 'WhenUnsatisfiable indicates how to deal with
5586+
a pod if it doesn''t satisfy the spread constraint. -
5587+
DoNotSchedule (default) tells the scheduler not to schedule
5588+
it. - ScheduleAnyway tells the scheduler to schedule the
5589+
pod in any location, but giving higher precedence to topologies
5590+
that would help reduce the skew. A constraint is considered
5591+
"Unsatisfiable" for an incoming pod if and only if every
5592+
possible node assignment for that pod would violate "MaxSkew"
5593+
on some topology. For example, in a 3-zone cluster, MaxSkew
5594+
is set to 1, and pods with the same labelSelector spread
5595+
as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P |
5596+
If WhenUnsatisfiable is set to DoNotSchedule, incoming
5597+
pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2)
5598+
as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1).
5599+
In other words, the cluster can still be imbalanced, but
5600+
scheduler won''t make it *more* imbalanced. It''s a required
5601+
field.'
5602+
type: string
5603+
required:
5604+
- maxSkew
5605+
- topologyKey
5606+
- whenUnsatisfiable
5607+
type: object
5608+
type: array
5609+
disableOperatorDefaults:
5610+
description: Whether the operator should disable its default set
5611+
of TopologySpreadConstraints. Defaults to false.
5612+
type: boolean
5613+
type: object
54315614
volumeMounts:
54325615
description: Represents where to mount the volumes into the application
54335616
container.

‎bundle/manifests/runtime-component.clusterserviceversion.yaml

+12-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ metadata:
7171
categories: Application Runtime
7272
certified: "true"
7373
containerImage: icr.io/appcafe/runtime-component-operator:daily
74-
createdAt: "2023-10-27T14:38:16Z"
74+
createdAt: "2023-11-01T20:37:23Z"
7575
description: Deploys any runtime component with dynamic and auto-tuning configuration
7676
olm.skipRange: '>=0.8.0 <1.3.0'
7777
operators.openshift.io/infrastructure-features: '["disconnected"]'
@@ -142,6 +142,15 @@ spec:
142142
path: serviceAccount.mountToken
143143
x-descriptors:
144144
- urn:alm:descriptor:com.tectonic.ui:booleanSwitch
145+
- description: The list of TopologySpreadConstraints for the application pod.
146+
displayName: Constraints
147+
path: topologySpreadConstraints.constraints
148+
- description: Whether the operator should disable its default set of TopologySpreadConstraints.
149+
Defaults to false.
150+
displayName: Disable Operator Defaults
151+
path: topologySpreadConstraints.disableOperatorDefaults
152+
x-descriptors:
153+
- urn:alm:descriptor:com.tectonic.ui:booleanSwitch
145154
- description: Name of the application. Defaults to the name of this custom
146155
resource.
147156
displayName: Application Name
@@ -347,6 +356,8 @@ spec:
347356
path: statefulSet.storage.className
348357
x-descriptors:
349358
- urn:alm:descriptor:com.tectonic.ui:text
359+
- displayName: Topology Spread Constraints
360+
path: topologySpreadConstraints
350361
- description: The directory inside the container where this persisted storage
351362
will be bound to.
352363
displayName: Storage Mount Path

‎common/types.go

+6
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ type BaseComponentServiceAccount interface {
190190
GetName() *string
191191
}
192192

193+
type BaseComponentTopologySpreadConstraints interface {
194+
GetConstraints() *[]corev1.TopologySpreadConstraint
195+
GetDisableOperatorDefaults() *bool
196+
}
197+
193198
// BaseComponent represents basic kubernetes application
194199
type BaseComponent interface {
195200
GetApplicationImage() string
@@ -222,6 +227,7 @@ type BaseComponent interface {
222227
GetGroupName() string
223228
GetRoute() BaseComponentRoute
224229
GetAffinity() BaseComponentAffinity
230+
GetTopologySpreadConstraints() BaseComponentTopologySpreadConstraints
225231
GetSecurityContext() *corev1.SecurityContext
226232
GetManageTLS() *bool
227233
}

0 commit comments

Comments
 (0)
Please sign in to comment.