Skip to content

Commit

Permalink
Support additional node selectors for request serving nodes
Browse files Browse the repository at this point in the history
Adds annotation to specify additional node selectors. This is needed for
request serving nodes to request the corresponding size label.
  • Loading branch information
csrwng committed Apr 16, 2024
1 parent 96b62a5 commit c7bc57b
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 12 deletions.
4 changes: 4 additions & 0 deletions api/v1beta1/hostedcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ const (
// components should be scheduled on dedicated nodes in the management cluster.
DedicatedRequestServingComponentsTopology = "dedicated-request-serving-components"

// RequestServingNodeAdditionalSelectorAnnotation is used to specify an additional node selector for
// request serving nodes. The value is a comma-separated list of key=value pairs.
RequestServingNodeAdditionalSelectorAnnotation = "hypershift.openshift.io/request-serving-node-additional-selector"

// AllowGuestWebhooksServiceLabel marks a service deployed in the control plane as a valid target
// for validating/mutating webhooks running in the guest cluster.
AllowGuestWebhooksServiceLabel = "hypershift.openshift.io/allow-guest-webhooks"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1754,6 +1754,7 @@ func reconcileHostedControlPlane(hcp *hyperv1.HostedControlPlane, hcluster *hype
hyperv1.OLMCatalogsISRegistryOverridesAnnotation,
hyperv1.KubeAPIServerGOGCAnnotation,
hyperv1.KubeAPIServerGOMemoryLimitAnnotation,
hyperv1.RequestServingNodeAdditionalSelectorAnnotation,
}
for _, key := range mirroredAnnotations {
val, hasVal := hcluster.Annotations[key]
Expand Down
37 changes: 25 additions & 12 deletions support/config/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type DeploymentConfig struct {
DebugDeployments sets.String
ResourceRequestOverrides ResourceOverrides
IsolateAsRequestServing bool

AdditionalRequestServingNodeSelector map[string]string
}

func (c *DeploymentConfig) SetContainerResourcesIfPresent(container *corev1.Container) {
Expand Down Expand Up @@ -296,21 +298,29 @@ func (c *DeploymentConfig) setControlPlaneIsolation(hcp *hyperv1.HostedControlPl
}

if c.IsolateAsRequestServing {
nodeSelectorRequirements := []corev1.NodeSelectorRequirement{
{
Key: hyperv1.RequestServingComponentLabel,
Operator: corev1.NodeSelectorOpIn,
Values: []string{"true"},
},
{
Key: hyperv1.HostedClusterLabel,
Operator: corev1.NodeSelectorOpIn,
Values: []string{clusterKey(hcp)},
},
}
for key, value := range c.AdditionalRequestServingNodeSelector {
nodeSelectorRequirements = append(nodeSelectorRequirements, corev1.NodeSelectorRequirement{
Key: key,
Operator: corev1.NodeSelectorOpIn,
Values: []string{value},
})
}
c.Scheduling.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &corev1.NodeSelector{
NodeSelectorTerms: []corev1.NodeSelectorTerm{
{
MatchExpressions: []corev1.NodeSelectorRequirement{
{
Key: hyperv1.RequestServingComponentLabel,
Operator: corev1.NodeSelectorOpIn,
Values: []string{"true"},
},
{
Key: hyperv1.HostedClusterLabel,
Operator: corev1.NodeSelectorOpIn,
Values: []string{clusterKey(hcp)},
},
},
MatchExpressions: nodeSelectorRequirements,
},
},
}
Expand Down Expand Up @@ -357,6 +367,9 @@ func (c *DeploymentConfig) SetRequestServingDefaults(hcp *hyperv1.HostedControlP
if hcp.Annotations[hyperv1.TopologyAnnotation] == hyperv1.DedicatedRequestServingComponentsTopology {
c.IsolateAsRequestServing = true
}
if hcp.Annotations[hyperv1.RequestServingNodeAdditionalSelectorAnnotation] != "" {
c.AdditionalRequestServingNodeSelector = util.ParseNodeSelector(hcp.Annotations[hyperv1.RequestServingNodeAdditionalSelectorAnnotation])
}
c.SetDefaults(hcp, multiZoneSpreadLabels, replicas)
if c.AdditionalLabels == nil {
c.AdditionalLabels = map[string]string{}
Expand Down
20 changes: 20 additions & 0 deletions support/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,23 @@ func FirstUsableIP(cidr string) (string, error) {
ip[len(ipNet.IP)-1]++
return ip.String(), nil
}

// ParseNodeSelector parses a comma separated string of key=value pairs into a map
func ParseNodeSelector(str string) map[string]string {
if len(str) == 0 {
return nil
}
parts := strings.Split(str, ",")
result := make(map[string]string)
for _, part := range parts {
kv := strings.SplitN(part, "=", 2)
if len(kv) != 2 {
continue
}
if len(kv[0]) == 0 || len(kv[1]) == 0 {
continue
}
result[kv[0]] = kv[1]
}
return result
}
60 changes: 60 additions & 0 deletions support/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,63 @@ func TestFirstUsableIP(t *testing.T) {
})
}
}

func TestParseNodeSelector(t *testing.T) {
tests := []struct {
name string
str string
want map[string]string
}{
{
name: "Given a valid node selector string, it should return a map of key value pairs",
str: "key1=value1,key2=value2,key3=value3",
want: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
{
name: "Given a valid node selector string with empty values, it should return a map of key value pairs",
str: "key1=,key2=value2,key3=",
want: map[string]string{
"key2": "value2",
},
},
{
name: "Given a valid node selector string with empty keys, it should return a map of key value pairs",
str: "=value1,key2=value2,=value3",
want: map[string]string{
"key2": "value2",
},
},
{
name: "Given a valid node selector string with empty string, it should return an empty map",
str: "",
want: nil,
},
{
name: "Given a valid node selector string with invalid key value pairs, it should return a map of key value pairs",
str: "key1=value1,key2,key3=value3",
want: map[string]string{
"key1": "value1",
"key3": "value3",
},
},
{
name: "Given a valid node selector string with values that include =, it should return a map of key value pairs",
str: "key1=value1=one,key2,key3=value3=three",
want: map[string]string{
"key1": "value1=one",
"key3": "value3=three",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
got := ParseNodeSelector(tt.str)
g.Expect(got).To(Equal(tt.want))
})
}
}

0 comments on commit c7bc57b

Please sign in to comment.