Skip to content
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion cmd/machine-config-controller/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func runbootstrapCmd(_ *cobra.Command, _ []string) {
klog.Fatalf("--dest-dir or --manifest-dir not set")
}

if err := bootstrap.New(rootOpts.templates, bootstrapOpts.manifestsDir, bootstrapOpts.pullSecretFile).Run(bootstrapOpts.destinationDir); err != nil {
if err := bootstrap.New(rootOpts.templates, bootstrapOpts.manifestsDir, bootstrapOpts.pullSecretFile, nil).Run(bootstrapOpts.destinationDir); err != nil {
klog.Fatalf("error running MCC[BOOTSTRAP]: %v", err)
}
}
12 changes: 9 additions & 3 deletions cmd/machine-config-controller/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func runStartCmd(_ *cobra.Command, _ []string) {
return
}

var inspectorFactory osimagestream.ImagesInspectorFactory
var inspectionCache *imageutils.FileInspectionCache
if startOpts.streamsCache != "" {
inspectionCache = imageutils.NewFileInspectionCache(path.Join(startOpts.streamsCache, "image-inspection.json"), 48*time.Hour)
Expand All @@ -105,7 +106,6 @@ func runStartCmd(_ *cobra.Command, _ []string) {
// controller starts, since render, node, and template depend on it
// for OS image URLs.
if osimagestream.IsFeatureEnabled(ctrlctx.FeatureGatesHandler) {
var inspectorFactory osimagestream.ImagesInspectorFactory
if inspectionCache != nil {
inspectorFactory = osimagestream.NewCachedImagesInspectorFactory(
&osimagestream.DefaultImagesInspectorFactory{},
Expand Down Expand Up @@ -150,7 +150,7 @@ func runStartCmd(_ *cobra.Command, _ []string) {

go ctrlcommon.StartMetricsListener(startOpts.promMetricsListenAddress, ctrlctx.Stop, ctrlcommon.RegisterMCCMetrics, startOpts.tlsMinVersion, startOpts.tlsCipherSuites)

controllers := createControllers(ctrlctx, inspectionCache)
controllers := createControllers(ctrlctx, inspectionCache, inspectorFactory)
draincontroller := drain.New(
drain.DefaultConfig(),
ctrlctx.KubeInformerFactory.Core().V1().Nodes(),
Expand Down Expand Up @@ -272,7 +272,7 @@ func runStartCmd(_ *cobra.Command, _ []string) {
panic("unreachable")
}

func createControllers(ctx *ctrlcommon.ControllerContext, inspectionCache *imageutils.FileInspectionCache) []ctrlcommon.Controller {
func createControllers(ctx *ctrlcommon.ControllerContext, inspectionCache *imageutils.FileInspectionCache, inspectorFactory osimagestream.ImagesInspectorFactory) []ctrlcommon.Controller {
// Only watch IRI informers when the feature gate is enabled. The
// InternalReleaseImages CRD is not installed on clusters where the gate is
// off, so the informer list call would fail and WaitForCacheSync in the
Expand All @@ -292,9 +292,15 @@ func createControllers(ctx *ctrlcommon.ControllerContext, inspectionCache *image
ctx.InformerFactory.Machineconfiguration().V1().KubeletConfigs(),
ctx.OperatorInformerFactory.Operator().V1().MachineConfigurations(),
ctx.InformerFactory.Machineconfiguration().V1().OSImageStreams(),
ctx.OpenShiftConfigKubeNamespacedInformerFactory.Core().V1().Secrets(),
ctx.OperatorInformerFactory.Operator().V1alpha1().ImageContentSourcePolicies(),
ctx.ConfigInformerFactory.Config().V1().ImageDigestMirrorSets(),
ctx.ConfigInformerFactory.Config().V1().ImageTagMirrorSets(),
ctx.ConfigInformerFactory.Config().V1().Images(),
ctx.ClientBuilder.KubeClientOrDie("render-controller"),
ctx.ClientBuilder.MachineConfigClientOrDie("render-controller"),
ctx.FeatureGatesHandler,
inspectorFactory,
)
if inspectionCache != nil {
inspectionCache.RegisterEvicter(renderCtrl)
Expand Down
128 changes: 63 additions & 65 deletions go.mod

Large diffs are not rendered by default.

293 changes: 142 additions & 151 deletions go.sum

Large diffs are not rendered by default.

247 changes: 247 additions & 0 deletions pkg/controller/bootimage/boot_image_controller_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package bootimage

import (
"bytes"
"context"
"os"
"testing"

"github.com/coreos/stream-metadata-go/stream"
"github.com/coreos/stream-metadata-go/stream/rhcos"
osconfigv1 "github.com/openshift/api/config/v1"
machinev1 "github.com/openshift/api/machine/v1"
machinev1beta1 "github.com/openshift/api/machine/v1beta1"
opv1 "github.com/openshift/api/operator/v1"
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1"
Expand All @@ -16,9 +19,12 @@ import (
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
corelisterv1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog/v2"
ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common"
)

Expand Down Expand Up @@ -1076,3 +1082,244 @@ func TestResetClusterBootImage(t *testing.T) {
})
}
}

func TestSyncMAPIMachineSetOSStreamLabel(t *testing.T) {
// Uses AWS platform so that when the controller does NOT skip (supported/no-label),
// it continues into reconcilePlatform and fails on the empty provider spec.
// This proves the skip path works: unsupported streams return early with no error
// and log the skip message, while supported streams continue processing.
newTestController := func() *Controller {
cv := &osconfigv1.ClusterVersion{
ObjectMeta: v1.ObjectMeta{Name: "version"},
Status: osconfigv1.ClusterVersionStatus{
Desired: osconfigv1.Release{Architecture: ""},
History: []osconfigv1.UpdateHistory{
{State: osconfigv1.CompletedUpdate, Version: "4.18.0"},
},
},
}
cvIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
require.NoError(t, cvIndexer.Add(cv))

infra := &osconfigv1.Infrastructure{
ObjectMeta: v1.ObjectMeta{Name: "cluster"},
Status: osconfigv1.InfrastructureStatus{
PlatformStatus: &osconfigv1.PlatformStatus{
Type: osconfigv1.AWSPlatformType,
},
},
}
infraIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
require.NoError(t, infraIndexer.Add(infra))

return &Controller{
kubeClient: fake.NewClientset(),
clusterVersionLister: configlistersv1.NewClusterVersionLister(cvIndexer),
infraLister: configlistersv1.NewInfrastructureLister(infraIndexer),
mapiBootImageState: map[string]BootImageState{},
fgHandler: ctrlcommon.NewFeatureGatesHardcodedHandler(nil, nil),
}
}

configMap := &corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{
Name: ctrlcommon.BootImagesConfigMapName,
Namespace: ctrlcommon.MCONamespace,
},
}

cases := []struct {
name string
labels map[string]string
expectErr bool
expectLog string
rejectLog string
}{
{
name: "unsupported stream rhel-10 should skip",
labels: map[string]string{OSStreamLabelKey: "rhel-10"},
expectErr: false,
expectLog: "has unsupported stream: rhel-10, skipping boot image update",
},
{
name: "unsupported custom stream should skip",
labels: map[string]string{OSStreamLabelKey: "custom-stream"},
expectErr: false,
expectLog: "has unsupported stream: custom-stream, skipping boot image update",
},
{
name: "supported stream rhel-9 should continue processing",
labels: map[string]string{OSStreamLabelKey: SupportedOSStream},
expectErr: true,
rejectLog: "skipping boot image update",
},
{
name: "no osstream label should continue processing",
labels: map[string]string{},
expectErr: true,
rejectLog: "skipping boot image update",
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var buf bytes.Buffer
klog.LogToStderr(false)
klog.SetOutput(&buf)
defer func() {
klog.SetOutput(os.Stderr)
klog.LogToStderr(true)
}()

ctrl := newTestController()

ms := &machinev1beta1.MachineSet{
ObjectMeta: v1.ObjectMeta{
Name: "test-machineset",
Labels: tc.labels,
Annotations: map[string]string{
MachineSetArchAnnotationKey: "kubernetes.io/arch=amd64",
},
},
Spec: machinev1beta1.MachineSetSpec{
Template: machinev1beta1.MachineTemplateSpec{
Spec: machinev1beta1.MachineSpec{
ProviderSpec: machinev1beta1.ProviderSpec{
Value: &runtime.RawExtension{Raw: []byte("{}")},
},
},
},
},
}

_, _, err := ctrl.syncMAPIMachineSet(ms, configMap)
klog.Flush()
output := buf.String()

if tc.expectErr {
assert.Error(t, err, "supported/no-label stream should continue processing and error on empty provider spec")
} else {
assert.NoError(t, err, "unsupported stream should be skipped without error")
}
if tc.expectLog != "" {
assert.Contains(t, output, tc.expectLog)
}
if tc.rejectLog != "" {
assert.NotContains(t, output, tc.rejectLog)
}
})
}
}

func TestSyncControlPlaneMachineSetOSStreamLabel(t *testing.T) {
newTestController := func() *Controller {
infra := &osconfigv1.Infrastructure{
ObjectMeta: v1.ObjectMeta{Name: "cluster"},
Status: osconfigv1.InfrastructureStatus{
PlatformStatus: &osconfigv1.PlatformStatus{
Type: osconfigv1.AWSPlatformType,
},
},
}
infraIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
require.NoError(t, infraIndexer.Add(infra))

cm := &corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{
Name: ctrlcommon.BootImagesConfigMapName,
Namespace: ctrlcommon.MCONamespace,
},
}
cmIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
require.NoError(t, cmIndexer.Add(cm))

return &Controller{
kubeClient: fake.NewClientset(),
infraLister: configlistersv1.NewInfrastructureLister(infraIndexer),
mcoCmLister: corelisterv1.NewConfigMapLister(cmIndexer),
cpmsBootImageState: map[string]BootImageState{},
fgHandler: ctrlcommon.NewFeatureGatesHardcodedHandler(nil, nil),
}
}

cases := []struct {
name string
labels map[string]string
expectErr bool
expectLog string
rejectLog string
}{
{
name: "unsupported stream rhel-10 should skip",
labels: map[string]string{OSStreamLabelKey: "rhel-10"},
expectErr: false,
expectLog: "has unsupported stream: rhel-10, skipping boot image update",
},
{
name: "unsupported custom stream should skip",
labels: map[string]string{OSStreamLabelKey: "custom-stream"},
expectErr: false,
expectLog: "has unsupported stream: custom-stream, skipping boot image update",
},
{
name: "supported stream rhel-9 should continue processing",
labels: map[string]string{OSStreamLabelKey: SupportedOSStream},
expectErr: true,
rejectLog: "skipping boot image update",
},
{
name: "no osstream label should continue processing",
labels: map[string]string{},
expectErr: true,
rejectLog: "skipping boot image update",
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var buf bytes.Buffer
klog.LogToStderr(false)
klog.SetOutput(&buf)
defer func() {
klog.SetOutput(os.Stderr)
klog.LogToStderr(true)
}()

ctrl := newTestController()

cpms := &machinev1.ControlPlaneMachineSet{
ObjectMeta: v1.ObjectMeta{
Name: "test-cpms",
Labels: tc.labels,
},
Spec: machinev1.ControlPlaneMachineSetSpec{
Template: machinev1.ControlPlaneMachineSetTemplate{
OpenShiftMachineV1Beta1Machine: &machinev1.OpenShiftMachineV1Beta1MachineTemplate{
Spec: machinev1beta1.MachineSpec{
ProviderSpec: machinev1beta1.ProviderSpec{
Value: &kruntime.RawExtension{Raw: []byte("{}")},
},
},
},
},
},
}

err := ctrl.syncControlPlaneMachineSet(cpms)
klog.Flush()
output := buf.String()

if tc.expectErr {
assert.Error(t, err, "supported/no-label stream should continue processing and error on empty provider spec")
} else {
assert.NoError(t, err, "unsupported stream should be skipped without error")
}
if tc.expectLog != "" {
assert.Contains(t, output, tc.expectLog)
}
if tc.rejectLog != "" {
assert.NotContains(t, output, tc.rejectLog)
}
})
}
}
Loading