Skip to content
Open
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: 1 addition & 3 deletions hyperfleet-operator/internal/render/nodepool.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ func NodePoolResource(nodePool *hyperfleetv1alpha1.NodePool, cluster *hyperfleet
npSpec.Management.AutoRepair = true
}

if len(nodePool.Spec.Labels) > 0 {
npSpec.NodeLabels = nodePool.Spec.Labels
}
npSpec.NodeLabels = nodePool.Spec.Labels

if npSpec.Replicas == nil {
npSpec.Replicas = ptr.To(int32(2))
Expand Down
2 changes: 1 addition & 1 deletion platform-api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.36.0
k8s.io/apimachinery v0.36.0
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3
sigs.k8s.io/controller-runtime v0.24.1
)

Expand Down Expand Up @@ -117,7 +118,6 @@ require (
k8s.io/client-go v0.36.0 // indirect
k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260706235625-cdb1db5517a0 // indirect
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.4.1 // indirect
Expand Down
12 changes: 12 additions & 0 deletions platform-api/pkg/clients/hyperfleetdb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ func NodePoolCRToPlatform(cr *hyperfleetv1alpha1.NodePool) *types.NodePool {
UpdatedAt: metaTime(cr),
}

// Sync top-level autoRepair → passthrough management.autoRepair so the
// response is internally consistent. The operator defaults to true when
// autoRepair is unset, so we mirror that here.
if np.Spec.AutoRepair != nil {
np.Spec.NodePool.Management.AutoRepair = *np.Spec.AutoRepair
} else {
np.Spec.NodePool.Management.AutoRepair = true
}

// Sync top-level labels → passthrough nodeLabels (unconditional to clear stale values).
np.Spec.NodePool.NodeLabels = np.Spec.Labels

if phase := cr.Status.Phase; phase != "" {
np.Status = &types.NodePoolStatusInfo{
ObservedGeneration: cr.Status.ObservedGeneration,
Expand Down
94 changes: 94 additions & 0 deletions platform-api/pkg/clients/hyperfleetdb/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

hyperfleetv1alpha1 "github.com/openshift-online/rosa-hyperfleet-api/api/v1alpha1"
hypershiftv1beta1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
"k8s.io/utils/ptr"

"github.com/openshift-online/rosa-hyperfleet-api/platform-api/pkg/types"
)
Expand Down Expand Up @@ -47,6 +48,99 @@ func TestPlatformCreateToNodePoolCR_SetsAccountLabel(t *testing.T) {
}
}

func TestNodePoolCRToPlatform_AutoRepair(t *testing.T) {
tests := []struct {
name string
autoRepair *bool
wantTop *bool
wantMgmt bool
}{
{
name: "nil defaults to true in passthrough",
autoRepair: nil,
wantTop: nil,
wantMgmt: true,
},
{
name: "explicit true propagates to passthrough",
autoRepair: ptr.To(true),
wantTop: ptr.To(true),
wantMgmt: true,
},
{
name: "explicit false propagates to passthrough",
autoRepair: ptr.To(false),
wantTop: ptr.To(false),
wantMgmt: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cr := &hyperfleetv1alpha1.NodePool{
Spec: hyperfleetv1alpha1.NodePoolSpec{
AutoRepair: tt.autoRepair,
},
}
np := NodePoolCRToPlatform(cr)

if tt.wantTop == nil {
if np.Spec.AutoRepair != nil {
t.Errorf("Spec.AutoRepair = %v, want nil", *np.Spec.AutoRepair)
}
} else {
if np.Spec.AutoRepair == nil {
t.Fatalf("Spec.AutoRepair = nil, want %v", *tt.wantTop)
}
if *np.Spec.AutoRepair != *tt.wantTop {
t.Errorf("Spec.AutoRepair = %v, want %v", *np.Spec.AutoRepair, *tt.wantTop)
}
}

if np.Spec.NodePool.Management.AutoRepair != tt.wantMgmt {
t.Errorf("Spec.NodePool.Management.AutoRepair = %v, want %v",
np.Spec.NodePool.Management.AutoRepair, tt.wantMgmt)
}
})
}
}

func TestNodePoolCRToPlatform_Labels(t *testing.T) {
labels := map[string]string{"env": "staging", "team": "platform"}

cr := &hyperfleetv1alpha1.NodePool{
Spec: hyperfleetv1alpha1.NodePoolSpec{
Labels: labels,
},
}
np := NodePoolCRToPlatform(cr)

if len(np.Spec.NodePool.NodeLabels) != len(labels) {
t.Fatalf("NodeLabels len = %d, want %d", len(np.Spec.NodePool.NodeLabels), len(labels))
}
for k, v := range labels {
if np.Spec.NodePool.NodeLabels[k] != v {
t.Errorf("NodeLabels[%q] = %q, want %q", k, np.Spec.NodePool.NodeLabels[k], v)
}
}
}

func TestNodePoolCRToPlatform_LabelsEmpty(t *testing.T) {
cr := &hyperfleetv1alpha1.NodePool{
Spec: hyperfleetv1alpha1.NodePoolSpec{
Labels: nil,
NodePool: hyperfleetv1alpha1.NodePoolSpecPassthrough{
NodeLabels: map[string]string{"stale": "value"},
},
},
}
np := NodePoolCRToPlatform(cr)

if len(np.Spec.NodePool.NodeLabels) != 0 {
t.Errorf("NodeLabels = %v, want empty", np.Spec.NodePool.NodeLabels)
}
}

func TestPlatformCreateToClusterCR_SetsAccountLabel(t *testing.T) {
req := &types.ClusterCreateRequest{
Name: "my-cluster",
Expand Down