Skip to content

Commit

Permalink
Merge pull request openshift#3898 from csrwng/request_serving_nodesel…
Browse files Browse the repository at this point in the history
…ector_414

HOSTEDCP-1526: [release-4.14] Support additional node selectors for request serving nodes
  • Loading branch information
openshift-merge-bot[bot] committed Apr 17, 2024
2 parents 96b62a5 + c7bc57b commit 83a47ba
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 83a47ba

Please sign in to comment.