Skip to content
Draft
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
14 changes: 10 additions & 4 deletions cmd/machine-config-controller/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ func runStartCmd(_ *cobra.Command, _ []string) {

ctrlctx := ctrlcommon.CreateControllerContext(ctx, cb)

// Early start the config informer because feature gate depends on it
ctrlctx.ConfigInformerFactory.Start(ctrlctx.Stop)
if fgErr := ctrlctx.FeatureGatesHandler.Connect(ctx); fgErr != nil {
klog.Error(fmt.Errorf("failed to connect to feature gates %w", fgErr))
runCancel()
<-ctx.Done()
return
}

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

controllers := createControllers(ctrlctx)
Expand Down Expand Up @@ -111,10 +120,6 @@ func runStartCmd(_ *cobra.Command, _ []string) {

close(ctrlctx.InformersStarted)

if fgErr := ctrlctx.FeatureGatesHandler.Connect(ctx); fgErr != nil {
klog.Fatal(fmt.Errorf("failed to connect to feature gates %w", fgErr))
}

if ctrlctx.FeatureGatesHandler.Enabled(features.FeatureGatePinnedImages) && ctrlctx.FeatureGatesHandler.Enabled(features.FeatureGateMachineConfigNodes) {
pinnedImageSet := pinnedimageset.New(
ctrlctx.InformerFactory.Machineconfiguration().V1().PinnedImageSets(),
Expand Down Expand Up @@ -246,6 +251,7 @@ func createControllers(ctx *ctrlcommon.ControllerContext) []ctrlcommon.Controlle
ctx.InformerFactory.Machineconfiguration().V1().ContainerRuntimeConfigs(),
ctx.InformerFactory.Machineconfiguration().V1().KubeletConfigs(),
ctx.OperatorInformerFactory.Operator().V1().MachineConfigurations(),
ctx.InformerFactory.Machineconfiguration().V1alpha1().OSImageStreams(),
ctx.ClientBuilder.KubeClientOrDie("render-controller"),
ctx.ClientBuilder.MachineConfigClientOrDie("render-controller"),
ctx.FeatureGatesHandler,
Expand Down
12 changes: 8 additions & 4 deletions cmd/machine-config-operator/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ func runStartCmd(_ *cobra.Command, _ []string) {
go common.SignalHandler(runCancel)
ctrlctx := ctrlcommon.CreateControllerContext(ctx, cb)

// Early start the config informer because feature gate depends on it
ctrlctx.ConfigInformerFactory.Start(ctrlctx.Stop)
if fgErr := ctrlctx.FeatureGatesHandler.Connect(ctx); fgErr != nil {
klog.Fatal(fmt.Errorf("failed to connect to feature gates %w", fgErr))
}

controller := operator.New(
ctrlcommon.MCONamespace, componentName,
startOpts.imagesFile,
Expand Down Expand Up @@ -107,6 +113,8 @@ func runStartCmd(_ *cobra.Command, _ []string) {
ctrlctx.ConfigInformerFactory.Config().V1().Nodes(),
ctrlctx.ConfigInformerFactory.Config().V1().APIServers(),
ctrlctx.NamespacedInformerFactory.Machineconfiguration().V1().MachineOSConfigs(),
ctrlctx.ConfigInformerFactory.Config().V1().ClusterVersions(),
ctrlctx.InformerFactory.Machineconfiguration().V1alpha1().OSImageStreams(),
ctrlctx,
)

Expand All @@ -124,10 +132,6 @@ func runStartCmd(_ *cobra.Command, _ []string) {

close(ctrlctx.InformersStarted)

if fgErr := ctrlctx.FeatureGatesHandler.Connect(ctx); fgErr != nil {
klog.Fatal(fmt.Errorf("failed to connect to feature gates %w", fgErr))
}

go controller.Run(2, ctrlctx.Stop)

// wait here in this function until the context gets cancelled (which tells us whe were being shut down)
Expand Down
66 changes: 60 additions & 6 deletions pkg/controller/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package bootstrap

import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"time"

"github.com/openshift/machine-config-operator/pkg/osimagestream"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

corev1 "k8s.io/api/core/v1"
Expand All @@ -23,6 +26,7 @@ import (
apicfgv1 "github.com/openshift/api/config/v1"
apicfgv1alpha1 "github.com/openshift/api/config/v1alpha1"
"github.com/openshift/api/features"
imagev1 "github.com/openshift/api/image/v1"
mcfgv1 "github.com/openshift/api/machineconfiguration/v1"
mcfgv1alpha1 "github.com/openshift/api/machineconfiguration/v1alpha1"
apioperatorsv1alpha1 "github.com/openshift/api/operator/v1alpha1"
Expand Down Expand Up @@ -70,7 +74,7 @@ func (b *Bootstrap) Run(destDir string) error {
return err
}

psraw, err := getPullSecretFromSecret(psfraw)
pullSecret, err := getValidatePullSecretFromBytes(psfraw)
if err != nil {
return err
}
Expand All @@ -82,8 +86,13 @@ func (b *Bootstrap) Run(destDir string) error {
apicfgv1.Install(scheme)
apicfgv1alpha1.Install(scheme)
corev1.AddToScheme(scheme)
imagev1.AddToScheme(scheme)
codecFactory := serializer.NewCodecFactory(scheme)
decoder := codecFactory.UniversalDecoder(mcfgv1.GroupVersion, apioperatorsv1alpha1.GroupVersion, apicfgv1.GroupVersion, apicfgv1alpha1.GroupVersion, corev1.SchemeGroupVersion, mcfgv1alpha1.GroupVersion)
decoder := codecFactory.UniversalDecoder(
mcfgv1.GroupVersion, apioperatorsv1alpha1.GroupVersion,
apicfgv1.GroupVersion, apicfgv1alpha1.GroupVersion,
corev1.SchemeGroupVersion, mcfgv1alpha1.GroupVersion,
imagev1.SchemeGroupVersion)

var (
cconfig *mcfgv1.ControllerConfig
Expand All @@ -101,6 +110,7 @@ func (b *Bootstrap) Run(destDir string) error {
imagePolicies []*apicfgv1.ImagePolicy
imgCfg *apicfgv1.Image
apiServer *apicfgv1.APIServer
imageStream *imagev1.ImageStream
iri *mcfgv1alpha1.InternalReleaseImage
)
for _, info := range infos {
Expand Down Expand Up @@ -171,6 +181,17 @@ func (b *Bootstrap) Run(destDir string) error {
if obj.GetName() == ctrlcommon.InternalReleaseImageInstanceName {
iri = obj
}
case *imagev1.ImageStream:
for _, tag := range obj.Spec.Tags {
if tag.Name == "machine-config-operator" {
if imageStream != nil {
klog.Infof("multiple ImageStream found. Previous ImageStream %s replaced by %s", imageStream.Name, obj.Name)
}
imageStream = obj

}
}
// It's an ImageStream that doesn't look like the Release one (doesn't have our tag)
default:
klog.Infof("skipping %q [%d] manifest because of unhandled %T", file.Name(), idx+1, obji)
}
Expand All @@ -191,7 +212,40 @@ func (b *Bootstrap) Run(destDir string) error {
return fmt.Errorf("error creating feature gates handler: %w", err)
}

iconfigs, err := template.RunBootstrap(b.templatesDir, cconfig, psraw, apiServer)
var osImageStream *mcfgv1alpha1.OSImageStream
// Enable OSImageStreams if the FeatureGate is active and the deployment is not OKD
if osimagestream.IsFeatureEnabled(fgHandler) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

osImageStream, err = osimagestream.BuildOsImageStreamBootstrap(ctx,
pullSecret,
cconfig,
imageStream,
&osimagestream.OSImageTuple{
OSImage: cconfig.Spec.BaseOSContainerImage,
OSExtensionsImage: cconfig.Spec.BaseOSExtensionsContainerImage,
},
osimagestream.NewDefaultStreamSourceFactory(nil, &osimagestream.DefaultImagesInspectorFactory{}),
)
if err != nil {
return fmt.Errorf("error inspecting available OSImageStreams: %w", err)
}

// If no error happened override the ControllerConfig URLs with the default stream ones
if err == nil {
defaultStreamSet, err := osimagestream.GetOSImageStreamSetByName(osImageStream, "")
if err != nil {
// Should never happen
return fmt.Errorf("error getting default OSImageStreamSet: %w", err)
}
cconfig.Spec.BaseOSContainerImage = string(defaultStreamSet.OSImage)
cconfig.Spec.BaseOSExtensionsContainerImage = string(defaultStreamSet.OSExtensionsImage)
}
}

pullSecretBytes := pullSecret.Data[corev1.DockerConfigJsonKey]
iconfigs, err := template.RunBootstrap(b.templatesDir, cconfig, pullSecretBytes, apiServer)
if err != nil {
return err
}
Expand Down Expand Up @@ -274,7 +328,7 @@ func (b *Bootstrap) Run(destDir string) error {
klog.Infof("Successfully created %d pre-built image component MachineConfigs for hybrid OCL.", len(preBuiltImageMCs))
}

fpools, gconfigs, err := render.RunBootstrap(pools, configs, cconfig)
fpools, gconfigs, err := render.RunBootstrap(pools, configs, cconfig, osImageStream)
if err != nil {
return err
}
Expand Down Expand Up @@ -357,7 +411,7 @@ func (b *Bootstrap) Run(destDir string) error {

}

func getPullSecretFromSecret(sData []byte) ([]byte, error) {
func getValidatePullSecretFromBytes(sData []byte) (*corev1.Secret, error) {
obji, err := runtime.Decode(kscheme.Codecs.UniversalDecoder(corev1.SchemeGroupVersion), sData)
if err != nil {
return nil, err
Expand All @@ -369,7 +423,7 @@ func getPullSecretFromSecret(sData []byte) ([]byte, error) {
if s.Type != corev1.SecretTypeDockerConfigJson {
return nil, fmt.Errorf("expected secret type %s found %s", corev1.SecretTypeDockerConfigJson, s.Type)
}
return s.Data[corev1.DockerConfigJsonKey], nil
return s, nil
}

type manifest struct {
Expand Down
2 changes: 2 additions & 0 deletions pkg/controller/common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const (
// OSImageURLOverriddenKey is used to tag a rendered machineconfig when OSImageURL has been overridden from default using machineconfig
OSImageURLOverriddenKey = "machineconfiguration.openshift.io/os-image-url-overridden"

RenderedMachineConfigPrefix = "rendered-"

// ControllerConfigName is the name of the ControllerConfig object that controllers use
ControllerConfigName = "machine-config-controller"

Expand Down
32 changes: 26 additions & 6 deletions pkg/controller/common/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
ign2types "github.com/coreos/ignition/config/v2_2/types"
validate2 "github.com/coreos/ignition/config/validate"
ign3error "github.com/coreos/ignition/v2/config/shared/errors"
"github.com/openshift/api/machineconfiguration/v1alpha1"

ign3 "github.com/coreos/ignition/v2/config/v3_5"
ign3types "github.com/coreos/ignition/v2/config/v3_5/types"
Expand Down Expand Up @@ -68,7 +69,7 @@ func boolToPtr(b bool) *bool {
// It uses the Ignition config from first object as base and appends all the rest.
// Kernel arguments are concatenated.
// It defaults to the OSImageURL provided by the CVO but allows a MC provided OSImageURL to take precedence.
func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, cconfig *mcfgv1.ControllerConfig) (*mcfgv1.MachineConfig, error) {
func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, cconfig *mcfgv1.ControllerConfig, imageStream *v1alpha1.OSImageStreamSet) (*mcfgv1.MachineConfig, error) {
if len(configs) == 0 {
return nil, nil
}
Expand Down Expand Up @@ -187,16 +188,24 @@ func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, cconfig *mcfgv1.Contro
// For layering, we want to let the user override OSImageURL again
// The template configs always match what's in controllerconfig because they get rendered from there,
// so the only way we get an override here is if the user adds something different
osImageURL := GetDefaultBaseImageContainer(&cconfig.Spec)
osImageURL := GetBaseImageContainer(&cconfig.Spec, imageStream)
for _, cfg := range configs {
// Ignore generated MCs, only the rendered MC or a user provided MC can set this field
if cfg.Annotations[GeneratedByControllerVersionAnnotationKey] != "" {
continue
}
if cfg.Spec.OSImageURL != "" {
osImageURL = cfg.Spec.OSImageURL
}
}

// Allow overriding the extensions container
baseOSExtensionsContainerImage := cconfig.Spec.BaseOSExtensionsContainerImage
baseOSExtensionsContainerImage := GetBaseExtensionsImageContainer(&cconfig.Spec, imageStream)
for _, cfg := range configs {
// Ignore generated MCs, only the rendered MC or a user provided MC can set this field
if cfg.Annotations[GeneratedByControllerVersionAnnotationKey] != "" {
continue
}
if cfg.Spec.BaseOSExtensionsContainerImage != "" {
baseOSExtensionsContainerImage = cfg.Spec.BaseOSExtensionsContainerImage
}
Expand Down Expand Up @@ -1035,9 +1044,20 @@ func GetIgnitionFileDataByPath(config *ign3types.Config, path string) ([]byte, e
return nil, nil
}

// GetDefaultBaseImageContainer returns the default bootable host base image.
func GetDefaultBaseImageContainer(cconfigspec *mcfgv1.ControllerConfigSpec) string {
return cconfigspec.BaseOSContainerImage
// GetBaseImageContainer returns the default bootable host base image.
func GetBaseImageContainer(cconfigspec *mcfgv1.ControllerConfigSpec, imageStream *v1alpha1.OSImageStreamSet) string {
if imageStream == nil {
return cconfigspec.BaseOSContainerImage
}
return string(imageStream.OSImage)
}

// GetBaseExtensionsImageContainer returns the default bootable host base image.
func GetBaseExtensionsImageContainer(cconfigspec *mcfgv1.ControllerConfigSpec, imageStream *v1alpha1.OSImageStreamSet) string {
if imageStream == nil {
return cconfigspec.BaseOSExtensionsContainerImage
}
return string(imageStream.OSExtensionsImage)
}

// Configures common template FuncMaps used across all renderers.
Expand Down
10 changes: 5 additions & 5 deletions pkg/controller/common/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ func TestMergeMachineConfigs(t *testing.T) {
},
}
inMachineConfigs := []*mcfgv1.MachineConfig{machineConfigFIPS}
mergedMachineConfig, err := MergeMachineConfigs(inMachineConfigs, cconfig)
mergedMachineConfig, err := MergeMachineConfigs(inMachineConfigs, cconfig, nil)
require.Nil(t, err)

// check that the outgoing config does have the version string set,
Expand All @@ -397,7 +397,7 @@ func TestMergeMachineConfigs(t *testing.T) {
require.Nil(t, err)
expectedMachineConfig := &mcfgv1.MachineConfig{
Spec: mcfgv1.MachineConfigSpec{
OSImageURL: GetDefaultBaseImageContainer(&cconfig.Spec),
OSImageURL: GetBaseImageContainer(&cconfig.Spec, nil),
KernelArguments: []string{},
Config: runtime.RawExtension{
Raw: rawOutIgn,
Expand Down Expand Up @@ -504,7 +504,7 @@ func TestMergeMachineConfigs(t *testing.T) {
machineConfigIgnV2Merge,
}

mergedMachineConfig, err = MergeMachineConfigs(inMachineConfigs, cconfig)
mergedMachineConfig, err = MergeMachineConfigs(inMachineConfigs, cconfig, nil)
require.Nil(t, err)

expectedMachineConfig = &mcfgv1.MachineConfig{
Expand Down Expand Up @@ -588,7 +588,7 @@ func TestMergeMachineConfigs(t *testing.T) {
}

cconfig = &mcfgv1.ControllerConfig{}
mergedMachineConfig, err = MergeMachineConfigs(inMachineConfigs, cconfig)
mergedMachineConfig, err = MergeMachineConfigs(inMachineConfigs, cconfig, nil)
require.Nil(t, err)

// The expectation here is that the merged config contains the MCs with name bbb (overrides aaa due to name) and ccc (overrides ddd due to pool)
Expand Down Expand Up @@ -795,7 +795,7 @@ func TestSetDefaultFileOverwrite(t *testing.T) {
require.Nil(t, err)

cconfig := &mcfgv1.ControllerConfig{}
mergedMachineConfig, err := MergeMachineConfigs([]*mcfgv1.MachineConfig{machineConfigPreMerge}, cconfig)
mergedMachineConfig, err := MergeMachineConfigs([]*mcfgv1.MachineConfig{machineConfigPreMerge}, cconfig, nil)
require.Nil(t, err)

// Convert and create the expected post-merge config
Expand Down
7 changes: 7 additions & 0 deletions pkg/controller/node/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ func (ctrl *Controller) calculateStatus(mcns []*mcfgv1.MachineConfigNode, cconfi
unavailableMachineCount == 0 &&
!isLayeredPoolBuilding(isLayeredPool, mosc, mosb)
if allUpdated {
// When the pool is fully updated & the `OSStreams` FeatureGate is enabled, set the
// `OSImageStream` reference in the MCP status to be consistent to what is defined in the
// MCP spec
if ctrl.fgHandler.Enabled(features.FeatureGateOSStreams) {
status.OSImageStream = pool.Spec.OSImageStream
}

//TODO: update api to only have one condition regarding status of update.
updatedMsg := fmt.Sprintf("All nodes are updated with %s", getPoolUpdateLine(pool, mosc, isLayeredPool))
supdated := apihelpers.NewMachineConfigPoolCondition(mcfgv1.MachineConfigPoolUpdated, corev1.ConditionTrue, "", updatedMsg)
Expand Down
Loading