@@ -9,12 +9,15 @@ import (
99 "path/filepath"
1010 "time"
1111
12+ ign3types "github.com/coreos/ignition/v2/config/v3_5/types"
1213 yaml "github.com/ghodss/yaml"
14+ mcfgv1 "github.com/openshift/api/machineconfiguration/v1"
1315 mcfginformers "github.com/openshift/client-go/machineconfiguration/informers/externalversions"
1416
1517 "github.com/openshift/machine-config-operator/internal/clients"
1618 ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common"
1719 corev1 "k8s.io/api/core/v1"
20+ "k8s.io/apimachinery/pkg/labels"
1821 "k8s.io/apimachinery/pkg/runtime"
1922 "k8s.io/client-go/informers"
2023 corelisterv1 "k8s.io/client-go/listers/core/v1"
@@ -42,6 +45,8 @@ type clusterServer struct {
4245 machineConfigLister v1.MachineConfigLister
4346 controllerConfigLister v1.ControllerConfigLister
4447 configMapLister corelisterv1.ConfigMapLister
48+ machineOSConfigLister v1.MachineOSConfigLister
49+ machineOSBuildLister v1.MachineOSBuildLister
4550
4651 kubeconfigFunc kubeconfigFunc
4752 apiserverURL string
@@ -76,23 +81,27 @@ func NewClusterServer(kubeConfig, apiserverURL string) (Server, error) {
7681 sharedInformerFactory := mcfginformers .NewSharedInformerFactory (machineConfigClient , resyncPeriod ()())
7782 kubeNamespacedSharedInformer := informers .NewFilteredSharedInformerFactory (kubeClient , resyncPeriod ()(), "openshift-machine-config-operator" , nil )
7883
79- mcpInformer , mcInformer , ccInformer , cmInformer :=
84+ mcpInformer , mcInformer , ccInformer , cmInformer , moscInformer , mosbInformer :=
8085 sharedInformerFactory .Machineconfiguration ().V1 ().MachineConfigPools (),
8186 sharedInformerFactory .Machineconfiguration ().V1 ().MachineConfigs (),
8287 sharedInformerFactory .Machineconfiguration ().V1 ().ControllerConfigs (),
83- kubeNamespacedSharedInformer .Core ().V1 ().ConfigMaps ()
84- mcpLister , mcLister , ccLister , cmLister := mcpInformer .Lister (), mcInformer .Lister (), ccInformer .Lister (), cmInformer .Lister ()
85- mcpListerHasSynced , mcListerHasSynced , ccListerHasSynced , cmListerHasSynced :=
88+ kubeNamespacedSharedInformer .Core ().V1 ().ConfigMaps (),
89+ sharedInformerFactory .Machineconfiguration ().V1 ().MachineOSConfigs (),
90+ sharedInformerFactory .Machineconfiguration ().V1 ().MachineOSBuilds ()
91+ mcpLister , mcLister , ccLister , cmLister , moscLister , mosbLister := mcpInformer .Lister (), mcInformer .Lister (), ccInformer .Lister (), cmInformer .Lister (), moscInformer .Lister (), mosbInformer .Lister ()
92+ mcpListerHasSynced , mcListerHasSynced , ccListerHasSynced , cmListerHasSynced , moscListerHasSynced , mosbListerHasSynced :=
8693 mcpInformer .Informer ().HasSynced ,
8794 mcInformer .Informer ().HasSynced ,
8895 ccInformer .Informer ().HasSynced ,
89- cmInformer .Informer ().HasSynced
96+ cmInformer .Informer ().HasSynced ,
97+ moscInformer .Informer ().HasSynced ,
98+ mosbInformer .Informer ().HasSynced
9099
91100 var informerStopCh chan struct {}
92101 go sharedInformerFactory .Start (informerStopCh )
93102 go kubeNamespacedSharedInformer .Start (informerStopCh )
94103
95- if ! cache .WaitForCacheSync (informerStopCh , mcpListerHasSynced , mcListerHasSynced , ccListerHasSynced , cmListerHasSynced ) {
104+ if ! cache .WaitForCacheSync (informerStopCh , mcpListerHasSynced , mcListerHasSynced , ccListerHasSynced , cmListerHasSynced , moscListerHasSynced , mosbListerHasSynced ) {
96105 return nil , errors .New ("failed to wait for cache sync" )
97106 }
98107
@@ -101,6 +110,8 @@ func NewClusterServer(kubeConfig, apiserverURL string) (Server, error) {
101110 machineConfigLister : mcLister ,
102111 controllerConfigLister : ccLister ,
103112 configMapLister : cmLister ,
113+ machineOSConfigLister : moscLister ,
114+ machineOSBuildLister : mosbLister ,
104115 kubeconfigFunc : func () ([]byte , []byte , error ) { return kubeconfigFromSecret (bootstrapTokenDir , apiserverURL , nil ) },
105116 apiserverURL : apiserverURL ,
106117 }, nil
@@ -163,7 +174,27 @@ func (cs *clusterServer) GetConfig(cr poolRequest) (*runtime.RawExtension, error
163174
164175 addDataAndMaybeAppendToIgnition (caBundleFilePath , cc .Spec .KubeAPIServerServingCAData , & ignConf )
165176 addDataAndMaybeAppendToIgnition (cloudProviderCAPath , cc .Spec .CloudProviderCAData , & ignConf )
166- appenders := getAppenders (currConf , cr .version , cs .kubeconfigFunc , []string {}, "" )
177+
178+ desiredImage := cs .resolveDesiredImageForPool (mp )
179+ klog .Infof ("desiredImage is found to be %s" , desiredImage )
180+
181+ appenders := []appenderFunc {
182+ func (cfg * ign3types.Config , _ * mcfgv1.MachineConfig ) error {
183+ return appendNodeAnnotations (cfg , currConf , desiredImage )
184+ },
185+ func (cfg * ign3types.Config , _ * mcfgv1.MachineConfig ) error {
186+ return appendKubeConfig (cfg , cs .kubeconfigFunc )
187+ },
188+ appendInitialMachineConfig ,
189+ func (cfg * ign3types.Config , _ * mcfgv1.MachineConfig ) error { return appendCerts (cfg , []string {}, "" ) },
190+ // Inject desired image into the MC
191+ appendDesiredOSImage (desiredImage ),
192+ // Must be last
193+ func (cfg * ign3types.Config , mc * mcfgv1.MachineConfig ) error {
194+ return appendEncapsulated (cfg , mc , cr .version )
195+ },
196+ }
197+
167198 for _ , a := range appenders {
168199 if err := a (& ignConf , mc ); err != nil {
169200 return nil , err
@@ -224,3 +255,90 @@ func kubeconfigFromSecret(secretDir, apiserverURL string, caData []byte) ([]byte
224255 }
225256 return kcData , caData , nil
226257}
258+
259+ // Finds the MOSC that targets this MCO and verfies that the MOSC has an image in status
260+ // locates the matching MOSB for the MCP's current or next rendered MC and confirms build succeeded
261+ // and returns the image pullspec when its ready
262+ func (cs * clusterServer ) resolveDesiredImageForPool (pool * mcfgv1.MachineConfigPool ) string {
263+ // Find MachineOSConfig for this pool
264+ moscList , err := cs .machineOSConfigLister .List (labels .Everything ())
265+ if err != nil {
266+ // MOSC resources not available
267+ klog .Infof ("Could not list MachineOSConfigs for pool %s: %v" , pool .Name , err )
268+ return ""
269+ }
270+
271+ var mosc * mcfgv1.MachineOSConfig
272+ for _ , config := range moscList {
273+ if config .Spec .MachineConfigPool .Name == pool .Name {
274+ mosc = config
275+ break
276+ }
277+ }
278+
279+ // No MOSC for this pool - not layered
280+ if mosc == nil {
281+ return ""
282+ }
283+
284+ // Check if image is ready in MOSC status
285+ moscState := ctrlcommon .NewMachineOSConfigState (mosc )
286+ if ! moscState .HasOSImage () {
287+ klog .Infof ("Pool %s has MachineOSConfig but image not ready yet" , pool .Name )
288+ return ""
289+ }
290+
291+ // Also verify the corresponding MOSB is successful to ensure we don't serve an image that hasn't been built yet
292+ mosbList , err := cs .machineOSBuildLister .List (labels .Everything ())
293+ if err != nil {
294+ klog .Infof ("Could not list MachineOSBuilds for pool %s: %v" , pool .Name , err )
295+ return ""
296+ }
297+
298+ var currentConf string
299+ if pool .Status .UpdatedMachineCount > 0 {
300+ currentConf = pool .Spec .Configuration .Name
301+ } else {
302+ currentConf = pool .Status .Configuration .Name
303+ }
304+
305+ var mosb * mcfgv1.MachineOSBuild
306+ for _ , build := range mosbList {
307+ if build .Spec .MachineOSConfig .Name == mosc .Name &&
308+ build .Spec .MachineConfig .Name == currentConf {
309+ mosb = build
310+ break
311+ }
312+ }
313+
314+ if mosb == nil {
315+ klog .Infof ("Pool %s has MachineOSConfig but no matching MachineOSBuild for MC %s" , pool .Name , currentConf )
316+ return ""
317+ }
318+
319+ // Check build is successful
320+ mosbState := ctrlcommon .NewMachineOSBuildState (mosb )
321+ if ! mosbState .IsBuildSuccess () {
322+ klog .Infof ("Pool %s has MachineOSBuild but build not successful yet" , pool .Name )
323+ return ""
324+ }
325+
326+ imageSpec := moscState .GetOSImage ()
327+ klog .Infof ("Resolved layered image for pool %s: %s" , pool .Name , imageSpec )
328+ return imageSpec
329+ }
330+
331+ // appendDesiredOSImage mutates the MC to include the desired OS image.
332+ // This runs appendEncapsulated so mcd-firstboot sees the image on first boot.
333+ func appendDesiredOSImage (desired string ) appenderFunc {
334+ return func (_ * ign3types.Config , mc * mcfgv1.MachineConfig ) error {
335+ if desired == "" {
336+ return nil
337+ }
338+ if mc .Spec .OSImageURL != desired {
339+ klog .Infof ("overriding MC OSImageURL: %q -> %q" , mc .Spec .OSImageURL , desired )
340+ mc .Spec .OSImageURL = desired
341+ }
342+ return nil
343+ }
344+ }
0 commit comments