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
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@
},
{
"name": "EtcdSharding"
},
{
"name": "OSStreams"
}
],
"enabled": [
Expand All @@ -57,6 +54,9 @@
},
{
"name": "NetworkDiagnosticsConfig"
},
{
"name": "OSStreams"
}
],
"version": ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@
},
{
"name": "EtcdSharding"
},
{
"name": "OSStreams"
}
],
"enabled": [
Expand All @@ -57,6 +54,9 @@
},
{
"name": "NetworkDiagnosticsConfig"
},
{
"name": "OSStreams"
}
],
"version": ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@
},
{
"name": "EtcdSharding"
},
{
"name": "OSStreams"
}
],
"enabled": [
Expand All @@ -57,6 +54,9 @@
},
{
"name": "NetworkDiagnosticsConfig"
},
{
"name": "OSStreams"
}
],
"version": ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@
},
{
"name": "EtcdSharding"
},
{
"name": "OSStreams"
}
],
"enabled": [
Expand All @@ -57,6 +54,9 @@
},
{
"name": "NetworkDiagnosticsConfig"
},
{
"name": "OSStreams"
}
],
"version": ""
Expand Down

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

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ require (
github.com/onsi/gomega v1.42.1
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/openshift/api v0.0.0-20260805160557-b61243060d5f
github.com/openshift/api v0.0.0-20260820183036-3db6c4b03286
github.com/openshift/client-go v0.0.0-20260715172546-dac61734e0ec
github.com/openshift/cloud-credential-operator v0.0.0-20250225003505-216fd1a30ec3
github.com/openshift/cluster-api-provider-agent/api v0.0.0-20260120122324-898e638ec7d1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -576,8 +576,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/selinux v1.13.0 h1:Zza88GWezyT7RLql12URvoxsbLfjFx988+LGaWfbL84=
github.com/opencontainers/selinux v1.13.0/go.mod h1:XxWTed+A/s5NNq4GmYScVy+9jzXhGBVEOAyucdRUY8s=
github.com/openshift/api v0.0.0-20260805160557-b61243060d5f h1:NU7ltJhtFhqAJC+77DcNk6jmIpsJ3a+mebZIceiPW+U=
github.com/openshift/api v0.0.0-20260805160557-b61243060d5f/go.mod h1:k6qH5QOVa5GDln2VVm8Jz4NV3Z7R2SATHFLwGS6Wh3M=
github.com/openshift/api v0.0.0-20260820183036-3db6c4b03286 h1:zz+Bfuhq9gAlbICRli8wk9CKgGmRMe5yQSxzvnmX73Q=
github.com/openshift/api v0.0.0-20260820183036-3db6c4b03286/go.mod h1:k6qH5QOVa5GDln2VVm8Jz4NV3Z7R2SATHFLwGS6Wh3M=
github.com/openshift/aws-karpenter-provider-aws v0.0.0-20260722223016-abcf7d1e3417 h1:b4vLxDvDmwESkz21ezCxIZOLRE8zk68gDjn4P4gvtbo=
github.com/openshift/aws-karpenter-provider-aws v0.0.0-20260722223016-abcf7d1e3417/go.mod h1:K2x7H8k8k07WechkBHQ+sOr+6gPAkwILzVc7hYzODfY=
github.com/openshift/client-go v0.0.0-20260715172546-dac61734e0ec h1:UDjX+mot5IVLpcChyBqLXG1oSB29s4UkqFmgNb0Xsqc=
Expand Down
2 changes: 1 addition & 1 deletion hypershift-operator/controllers/nodepool/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func TestAWSMachineTemplateSpec(t *testing.T) {
}),
},
{
name: "When no sg is specified and no cluster sg is available, it should return NotReady error",
name: "When no sg is specified and no cluster sg is available, it should return a NotReady error",
clusterStatus: &hyperv1.HostedClusterStatus{Platform: &hyperv1.PlatformStatus{AWS: &hyperv1.AWSPlatformStatus{DefaultWorkerSecurityGroupID: ""}}},
checkError: func(t *testing.T, err error) {
var notReadyErr *NotReadyError
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,14 +379,6 @@ func (r *NodePoolReconciler) reconcile(ctx context.Context, hcluster *hyperv1.Ho
})
return ctrl.Result{}, fmt.Errorf("failed to resolve RHEL stream for boot image: %w", err)
}
// TODO(jparrill): remove debug log before merge
log.Info("Resolved RHEL stream for boot image",
"stream", resolvedRHELStream,
"osStreamsEnabled", osStreamsEnabled,
"specOSImageStream", nodePool.Spec.OSImageStream.Name,
"statusOSImageStream", nodePool.Status.OSImageStream.Name,
"releaseVersion", releaseImage.Version())

if err := r.setPlatformConditions(ctx, hcluster, nodePool, controlPlaneNamespace, releaseImage, resolvedRHELStream); err != nil {
return ctrl.Result{}, err
}
Expand Down
22 changes: 3 additions & 19 deletions hypershift-operator/controllers/nodepool/osstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,33 +85,17 @@ func usesRuncRuntime(ctx context.Context, c client.Client, nodePool *hyperv1.Nod
//
// Resolution order:
// 1. spec.osImageStream.Name — explicit user choice, always honored
// 2. status.osImageStream.Name — what existing nodes are running;
// preserves the current stream across upgrades to avoid unintended
// rollouts (e.g., upgrading from OCP 4.x to 5.0 keeps rhel-9)
// 3. When the OSStreams feature gate is disabled: always rhel-9
// 2. When the OSStreams feature gate is disabled: always rhel-9
// (backwards-compatible with upstream behavior)
// 4. When the OSStreams feature gate is enabled: version-derived default
// via GetRHELStream — for brand-new NodePools with no status yet
// (rhel-9 for <5.0, rhel-10 for >=5.0)
// 3. When the OSStreams feature gate is enabled: version-derived default
// via GetRHELStream (rhel-9 for <5.0, rhel-10 for >=5.0)
func GetRHELStreamForBootImage(ctx context.Context, c client.Client, nodePool *hyperv1.NodePool, releaseImage *releaseinfo.ReleaseImage, osStreamsEnabled bool) (string, error) {
// Explicit user choice takes precedence.
explicitStream := nodePool.Spec.OSImageStream.Name

if explicitStream == "" {
// Preserve the stream that nodes are already running to avoid a
// spurious rollout on upgrade (e.g., 4.x→5.0 keeps rhel-9).
if nodePool.Status.OSImageStream.Name != "" {
return nodePool.Status.OSImageStream.Name, nil
}
if !osStreamsEnabled {
// Feature gate off: match upstream behavior (hardcoded rhel-9).
// Without this guard a 5.x NodePool would resolve to rhel-10
// while the MCO still installs rhel-9, causing a template flip
// after machines report their actual OS.
return StreamRHEL9, nil
}
// Feature gate on, no status yet: fall through to version-derived
// default so brand-new OCP 5.0+ NodePools get rhel-10.
}

version, err := semver.Parse(releaseImage.Version())
Expand Down
10 changes: 5 additions & 5 deletions hypershift-operator/controllers/nodepool/osstream_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func TestGetRHELStreamForBootImage(t *testing.T) {
expectedStream: "rhel-9",
},
{
name: "When spec is empty and status has rhel-9 on OCP 5.0 upgrade, it should preserve rhel-9",
name: "When spec is empty and status has rhel-9 on OCP 5.0 upgrade, it should return version-derived rhel-10",
nodePool: &hyperv1.NodePool{
Spec: hyperv1.NodePoolSpec{},
Status: hyperv1.NodePoolStatus{
Expand All @@ -150,10 +150,10 @@ func TestGetRHELStreamForBootImage(t *testing.T) {
ImageStream: &imageapi.ImageStream{ObjectMeta: metav1.ObjectMeta{Name: "5.0.0"}},
},
osStreamsEnabled: true,
expectedStream: "rhel-9",
expectedStream: "rhel-10",
},
{
name: "When spec is empty and FG off and status has rhel-9 on OCP 5.0, it should preserve rhel-9",
name: "When spec is empty and FG off and status has rhel-9 on OCP 5.0, it should return rhel-9",
nodePool: &hyperv1.NodePool{
Spec: hyperv1.NodePoolSpec{},
Status: hyperv1.NodePoolStatus{
Expand All @@ -167,7 +167,7 @@ func TestGetRHELStreamForBootImage(t *testing.T) {
expectedStream: "rhel-9",
},
{
name: "When spec is empty and status has rhel-10 on OCP 5.0, it should preserve rhel-10",
name: "When spec is empty and status has rhel-10 on OCP 5.0, it should return version-derived rhel-10",
nodePool: &hyperv1.NodePool{
Spec: hyperv1.NodePoolSpec{},
Status: hyperv1.NodePoolStatus{
Expand Down Expand Up @@ -213,7 +213,7 @@ func TestGetRHELStreamForBootImage(t *testing.T) {
expectedStream: "rhel-9",
},
{
name: "When spec is empty and status has rhel-9 with runc config, it should preserve rhel-9 without checking runc",
name: "When spec is empty and status has rhel-9 with runc config on OCP 5.0, it should return rhel-9 due to runc",
nodePool: &hyperv1.NodePool{
ObjectMeta: metav1.ObjectMeta{
Name: "test-np",
Expand Down
4 changes: 2 additions & 2 deletions hypershift-operator/featuregate/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const (
// (e.g., OCP 5.0+ defaults to rhel-10). When disabled, boot images always use rhel-9.
// owner: @jparrill
// alpha: v0.1.49
// beta: x.y.z
// default: OCP 5.0
OSStreams featuregate.Feature = "OSStreams"
)

Expand All @@ -66,7 +66,7 @@ var (
hcpEtcdBackupFeature = featuregates.NewFeature(HCPEtcdBackup, featuregates.WithEnableForFeatureSets(configv1.TechPreviewNoUpgrade))
karpenterOperatorFeature = featuregates.NewFeature(KarpenterOperator, featuregates.WithEnableForFeatureSets(configv1.TechPreviewNoUpgrade))
etcdShardingFeature = featuregates.NewFeature(EtcdSharding, featuregates.WithEnableForFeatureSets(configv1.TechPreviewNoUpgrade))
osStreamsFeature = featuregates.NewFeature(OSStreams, featuregates.WithEnableForFeatureSets(configv1.TechPreviewNoUpgrade))
osStreamsFeature = featuregates.NewFeature(OSStreams, featuregates.WithEnableForFeatureSets(configv1.TechPreviewNoUpgrade, configv1.Default))
)

func init() {
Expand Down
10 changes: 10 additions & 0 deletions hypershift-operator/featuregate/feature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func TestAllHypershiftOperatorFeatureGates(t *testing.T) {
"GCPPlatform": false,
"HCPEtcdBackup": false,
"KarpenterOperator": false,
"OSStreams": true,
},
},
{
Expand All @@ -113,6 +114,7 @@ func TestAllHypershiftOperatorFeatureGates(t *testing.T) {
"GCPPlatform": true,
"HCPEtcdBackup": true,
"KarpenterOperator": true,
"OSStreams": true,
},
},
{
Expand All @@ -124,6 +126,7 @@ func TestAllHypershiftOperatorFeatureGates(t *testing.T) {
"GCPPlatform": false,
"HCPEtcdBackup": false,
"KarpenterOperator": false,
"OSStreams": false,
},
},
}
Expand Down Expand Up @@ -162,6 +165,12 @@ func TestAllHypershiftOperatorFeatureGates(t *testing.T) {
assert.Equal(t, tc.expected["KarpenterOperator"], actualKarpenterOperator,
"KarpenterOperator should be %v for feature set %s",
tc.expected["KarpenterOperator"], tc.featureSet)

// Test OSStreams
actualOSStreams := featuregate.Gate().Enabled(featuregate.OSStreams)
assert.Equal(t, tc.expected["OSStreams"], actualOSStreams,
"OSStreams should be %v for feature set %s",
tc.expected["OSStreams"], tc.featureSet)
})
}
}
Expand All @@ -173,4 +182,5 @@ func TestFeatureGateConstants(t *testing.T) {
assert.Equal(t, "GCPPlatform", string(featuregate.GCPPlatform))
assert.Equal(t, "HCPEtcdBackup", string(featuregate.HCPEtcdBackup))
assert.Equal(t, "KarpenterOperator", string(featuregate.KarpenterOperator))
assert.Equal(t, "OSStreams", string(featuregate.OSStreams))
}
Loading
Loading