Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ require (
github.com/onsi/ginkgo/v2 v2.28.1
github.com/onsi/gomega v1.39.1
github.com/openshift-eng/openshift-tests-extension v0.0.0-20260127124016-0fed2b824818
github.com/openshift/api v0.0.0-20260317165824-54a3998d81eb
github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1
github.com/openshift/api v0.0.0-20260429211050-21ed4c20b122
github.com/openshift/client-go v0.0.0-20260429123927-c81f86abfa6a
github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d
github.com/openshift/cluster-control-plane-machine-set-operator v0.0.0-20251029084908-344babe6a957
github.com/openshift/controller-runtime-common v0.0.0-20260318085703-1812aed6dbd2
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -463,10 +463,10 @@ github.com/opencontainers/selinux v1.13.0 h1:Zza88GWezyT7RLql12URvoxsbLfjFx988+L
github.com/opencontainers/selinux v1.13.0/go.mod h1:XxWTed+A/s5NNq4GmYScVy+9jzXhGBVEOAyucdRUY8s=
github.com/openshift-eng/openshift-tests-extension v0.0.0-20260127124016-0fed2b824818 h1:jJLE/aCAqDf8U4wc3bE1IEKgIxbb0ICjCNVFA49x/8s=
github.com/openshift-eng/openshift-tests-extension v0.0.0-20260127124016-0fed2b824818/go.mod h1:6gkP5f2HL0meusT0Aim8icAspcD1cG055xxBZ9yC68M=
github.com/openshift/api v0.0.0-20260317165824-54a3998d81eb h1:iwBR3mzmyE3EMFx7R3CQ9lOccTS0dNht8TW82aGITg0=
github.com/openshift/api v0.0.0-20260317165824-54a3998d81eb/go.mod h1:pyVjK0nZ4sRs4fuQVQ4rubsJdahI1PB94LnQ8sGdvxo=
github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1 h1:Hr/R38eg5ZJXfbiaHumjJIN1buDZwhsm4ys4npVCXH0=
github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1/go.mod h1:Za51LlH76ALiQ/aKGBYJXmyJNkA//IDJ+I///30CA2M=
github.com/openshift/api v0.0.0-20260429211050-21ed4c20b122 h1:Lsw4zRFaiYSDpTHaC5V7vDGLJsroJl62HpkZv4AFQfQ=
github.com/openshift/api v0.0.0-20260429211050-21ed4c20b122/go.mod h1:pyVjK0nZ4sRs4fuQVQ4rubsJdahI1PB94LnQ8sGdvxo=
github.com/openshift/client-go v0.0.0-20260429123927-c81f86abfa6a h1:4GR6seHvlfv0rADe+LCQx63FqSExx6gaSo8uNiyWq+c=
github.com/openshift/client-go v0.0.0-20260429123927-c81f86abfa6a/go.mod h1:Lm7X7aYbAaKhGsNhgYaowP7hiLKwfN/w0r+Q6VlQoI8=
github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d h1:+sqUThLi/lmgT5/scmmjnS6+RZFtbdxRAscNfCPyLPI=
github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d/go.mod h1:9+FWWWLkVrnBo1eYhA/0Ehlq5JMgIAHtcB0IF+qV1AA=
github.com/openshift/cluster-control-plane-machine-set-operator v0.0.0-20251029084908-344babe6a957 h1:eVnkMTFnirnoUOlAUT3Hy8WriIi1JoSrilWym3Dl8Q4=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,15 @@ spec:
labels of the machine template of the MachineSet.
format: int32
type: integer
labelSelector:
description: |-
labelSelector is a label selector, in string format, for Machines corresponding to the MachineSet.
It is exposed via the scale subresource as status.selector.
When omitted, the MachineSet controller has not yet reconciled spec.selector into status.labelSelector.
When present, it must not be empty and must not exceed 4096 characters.
maxLength: 4096
minLength: 1
type: string
observedGeneration:
description: observedGeneration reflects the generation of the most
recently observed MachineSet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,15 @@ spec:
labels of the machine template of the MachineSet.
format: int32
type: integer
labelSelector:
description: |-
labelSelector is a label selector, in string format, for Machines corresponding to the MachineSet.
It is exposed via the scale subresource as status.selector.
When omitted, the MachineSet controller has not yet reconciled spec.selector into status.labelSelector.
When present, it must not be empty and must not exceed 4096 characters.
maxLength: 4096
minLength: 1
type: string
observedGeneration:
description: observedGeneration reflects the generation of the most
recently observed MachineSet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,15 @@ spec:
labels of the machine template of the MachineSet.
format: int32
type: integer
labelSelector:
description: |-
labelSelector is a label selector, in string format, for Machines corresponding to the MachineSet.
It is exposed via the scale subresource as status.selector.
When omitted, the MachineSet controller has not yet reconciled spec.selector into status.labelSelector.
When present, it must not be empty and must not exceed 4096 characters.
maxLength: 4096
minLength: 1
type: string
observedGeneration:
description: observedGeneration reflects the generation of the most
recently observed MachineSet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,15 @@ spec:
labels of the machine template of the MachineSet.
format: int32
type: integer
labelSelector:
description: |-
labelSelector is a label selector, in string format, for Machines corresponding to the MachineSet.
It is exposed via the scale subresource as status.selector.
When omitted, the MachineSet controller has not yet reconciled spec.selector into status.labelSelector.
When present, it must not be empty and must not exceed 4096 characters.
maxLength: 4096
minLength: 1
type: string
observedGeneration:
description: observedGeneration reflects the generation of the most
recently observed MachineSet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,15 @@ spec:
labels of the machine template of the MachineSet.
format: int32
type: integer
labelSelector:
description: |-
labelSelector is a label selector, in string format, for Machines corresponding to the MachineSet.
It is exposed via the scale subresource as status.selector.
When omitted, the MachineSet controller has not yet reconciled spec.selector into status.labelSelector.
When present, it must not be empty and must not exceed 4096 characters.
maxLength: 4096
minLength: 1
type: string
observedGeneration:
description: observedGeneration reflects the generation of the most
recently observed MachineSet.
Expand Down
21 changes: 13 additions & 8 deletions pkg/controller/machineset/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package machineset
import (
"context"
"errors"
"fmt"
"reflect"

machinev1 "github.com/openshift/api/machine/v1beta1"
Expand Down Expand Up @@ -67,6 +66,7 @@ func (c *ReconcileMachineSet) calculateStatus(ms *machinev1.MachineSet, filtered
newStatus.FullyLabeledReplicas = int32(fullyLabeledReplicasCount)
newStatus.ReadyReplicas = int32(readyReplicasCount)
newStatus.AvailableReplicas = int32(availableReplicasCount)
newStatus.LabelSelector = metav1.FormatLabelSelector(&ms.Spec.Selector)
return newStatus
}

Expand All @@ -80,6 +80,7 @@ func updateMachineSetStatus(c client.Client, ms *machinev1.MachineSet, newStatus
ms.Status.FullyLabeledReplicas == newStatus.FullyLabeledReplicas &&
ms.Status.ReadyReplicas == newStatus.ReadyReplicas &&
ms.Status.AvailableReplicas == newStatus.AvailableReplicas &&
ms.Status.LabelSelector == newStatus.LabelSelector &&
reflect.DeepEqual(ms.Status.Conditions, newStatus.Conditions) &&
ms.Generation == ms.Status.ObservedGeneration {
return ms, nil
Expand All @@ -97,13 +98,17 @@ func updateMachineSetStatus(c client.Client, ms *machinev1.MachineSet, newStatus
if ms.Spec.Replicas != nil {
replicas = *ms.Spec.Replicas
}
klog.V(4).Infof("%s", fmt.Sprintf("Updating status for %v: %s/%s, ", ms.Kind, ms.Namespace, ms.Name)+
fmt.Sprintf("replicas %d->%d (need %d), ", ms.Status.Replicas, newStatus.Replicas, replicas)+
fmt.Sprintf("fullyLabeledReplicas %d->%d, ", ms.Status.FullyLabeledReplicas, newStatus.FullyLabeledReplicas)+
fmt.Sprintf("readyReplicas %d->%d, ", ms.Status.ReadyReplicas, newStatus.ReadyReplicas)+
fmt.Sprintf("availableReplicas %d->%d, ", ms.Status.AvailableReplicas, newStatus.AvailableReplicas)+
fmt.Sprintf("sequence No: %v->%v", ms.Status.ObservedGeneration, newStatus.ObservedGeneration)+
fmt.Sprintf("conditions: %v->%v", ms.Status.Conditions, newStatus.Conditions))
klog.V(4).Infof(
"Updating status for %v: %s/%s, replicas %d->%d (need %d), fullyLabeledReplicas %d->%d, readyReplicas %d->%d, availableReplicas %d->%d, labelSelector %q->%q, sequence No: %v->%v, conditions: %v->%v",
ms.Kind, ms.Namespace, ms.Name,
ms.Status.Replicas, newStatus.Replicas, replicas,
ms.Status.FullyLabeledReplicas, newStatus.FullyLabeledReplicas,
ms.Status.ReadyReplicas, newStatus.ReadyReplicas,
ms.Status.AvailableReplicas, newStatus.AvailableReplicas,
ms.Status.LabelSelector, newStatus.LabelSelector,
ms.Status.ObservedGeneration, newStatus.ObservedGeneration,
ms.Status.Conditions, newStatus.Conditions,
)

ms.Status = newStatus
patchErr = c.Status().Patch(context.Background(), ms, client.MergeFrom(machineSetCopy))
Expand Down
114 changes: 114 additions & 0 deletions pkg/controller/machineset/status_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
Copyright 2026 The Kubernetes 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 machineset

import (
"context"
"testing"

. "github.com/onsi/gomega"
machinev1 "github.com/openshift/api/machine/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

// TestMachineSetStatusLabelSelectorMatchesScaleSubresource verifies that status.labelSelector is the
// serialized label selector string expected by the CRD scale subresource (labelSelectorPath
// -> .status.labelSelector), which the apiserver maps to autoscaling/v1 Scale status.selector.
func TestMachineSetStatusLabelSelectorMatchesScaleSubresource(t *testing.T) {
tests := []struct {
name string
spec metav1.LabelSelector
}{
{
name: "matchLabels",
spec: metav1.LabelSelector{
MatchLabels: map[string]string{
"machine.openshift.io/cluster-api-cluster": "cluster-id",
"machine.openshift.io/cluster-api-machineset": "workers",
},
},
},
{
name: "matchExpressions",
spec: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{Key: "role", Operator: metav1.LabelSelectorOpIn, Values: []string{"worker", "infra"}},
},
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)

ms := &machinev1.MachineSet{
Spec: machinev1.MachineSetSpec{
Selector: tc.spec,
},
}

got := (&ReconcileMachineSet{}).calculateStatus(ms, nil).LabelSelector
want := metav1.FormatLabelSelector(&ms.Spec.Selector)
g.Expect(got).To(Equal(want), "status.labelSelector must match Scale status.selector for HPA")
})
}
}

func TestUpdateMachineSetStatusUpdatesLabelSelectorWithoutReplicaChanges(t *testing.T) {
t.Helper()

g := NewWithT(t)

ms := &machinev1.MachineSet{
ObjectMeta: metav1.ObjectMeta{
Name: "machineset-test",
Namespace: "openshift-machine-api",
Generation: 1,
},
Status: machinev1.MachineSetStatus{
Replicas: 0,
FullyLabeledReplicas: 0,
ReadyReplicas: 0,
AvailableReplicas: 0,
ObservedGeneration: 1,
},
}

cl := fake.NewClientBuilder().
WithScheme(scheme.Scheme).
WithRuntimeObjects(ms.DeepCopy()).
WithStatusSubresource(&machinev1.MachineSet{}).
Build()

current := &machinev1.MachineSet{}
key := client.ObjectKeyFromObject(ms)
g.Expect(cl.Get(context.Background(), key, current)).To(Succeed(), "failed to fetch machineset")

newStatus := current.Status
newStatus.LabelSelector = "machine.openshift.io/cluster-api-cluster=test-cluster"

updated, err := updateMachineSetStatus(cl, current, newStatus)
g.Expect(err).NotTo(HaveOccurred(), "failed to update machineset status")
g.Expect(updated.Status.LabelSelector).To(Equal(newStatus.LabelSelector))

stored := &machinev1.MachineSet{}
g.Expect(cl.Get(context.Background(), key, stored)).To(Succeed(), "failed to refetch machineset")
g.Expect(stored.Status.LabelSelector).To(Equal(newStatus.LabelSelector))
}
6 changes: 6 additions & 0 deletions vendor/github.com/openshift/api/.golangci.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions vendor/github.com/openshift/api/config/v1/types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions vendor/github.com/openshift/api/config/v1/types_apiserver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading