From 75567631c09c24ebdf0ca976a43c535e3f3c6a8d Mon Sep 17 00:00:00 2001 From: Courtney Date: Mon, 11 May 2026 16:55:54 -0600 Subject: [PATCH] Phase 1 changes for MCS Firstboot Pivot Failure Reporting --- pkg/daemon/constants/constants.go | 3 +- pkg/operator/sync.go | 33 +-------------------- pkg/server/bootstrap_server.go | 2 +- pkg/server/cluster_server.go | 19 +++++++++++- pkg/server/server.go | 48 +++++++++++++++++++++++++++---- pkg/server/server_test.go | 4 +-- 6 files changed, 67 insertions(+), 42 deletions(-) diff --git a/pkg/daemon/constants/constants.go b/pkg/daemon/constants/constants.go index 1f3d456505..eb2adf2198 100644 --- a/pkg/daemon/constants/constants.go +++ b/pkg/daemon/constants/constants.go @@ -12,7 +12,8 @@ const ( CurrentImageAnnotationKey = "machineconfiguration.openshift.io/currentImage" // DesiredImageAnnotationKey is used to specify the desired OS image pullspec for a machine DesiredImageAnnotationKey = "machineconfiguration.openshift.io/desiredImage" - + // MachineConfigServerURLAnnotationKey stores the MCS base URL for status reporting + MachineConfigServerURLAnnotationKey = "machineconfiguration.openshift.io/machineConfigServerURL" // CurrentMachineConfigAnnotationKey is used to fetch current MachineConfig for a machine CurrentMachineConfigAnnotationKey = "machineconfiguration.openshift.io/currentConfig" // DesiredMachineConfigAnnotationKey is used to specify the desired MachineConfig for a machine diff --git a/pkg/operator/sync.go b/pkg/operator/sync.go index 354bb1f22c..a8286c8491 100644 --- a/pkg/operator/sync.go +++ b/pkg/operator/sync.go @@ -9,11 +9,8 @@ import ( "encoding/pem" "errors" "fmt" - "net" - "net/url" "os" "reflect" - "strconv" "strings" "time" @@ -620,7 +617,7 @@ func (optr *Operator) syncRenderConfig(_ *renderConfig, _ *configv1.ClusterOpera templatectrl.DockerRegistryKey: imgs.DockerRegistry, } - ignitionHost, err := getIgnitionHost(&infra.Status) + ignitionHost, err := server.GetIgnitionHost(&infra.Status) if err != nil { return err } @@ -698,34 +695,6 @@ func (optr *Operator) getTrustedBundle(proxy *configv1.Proxy) ([]byte, error) { return trustBundle, nil } -func getIgnitionHost(infraStatus *configv1.InfrastructureStatus) (string, error) { - internalURL := infraStatus.APIServerInternalURL - internalURLParsed, err := url.Parse(internalURL) - if err != nil { - return "", err - } - securePortStr := strconv.Itoa(server.SecurePort) - ignitionHost := fmt.Sprintf("%s:%s", internalURLParsed.Hostname(), securePortStr) - if infraStatus.PlatformStatus != nil { - switch infraStatus.PlatformStatus.Type { - case configv1.BareMetalPlatformType: - ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.BareMetal.APIServerInternalIPs[0], securePortStr) - case configv1.OpenStackPlatformType: - ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.OpenStack.APIServerInternalIPs[0], securePortStr) - case configv1.OvirtPlatformType: - ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.Ovirt.APIServerInternalIPs[0], securePortStr) - case configv1.VSpherePlatformType: - if infraStatus.PlatformStatus.VSphere != nil { - if len(infraStatus.PlatformStatus.VSphere.APIServerInternalIPs) > 0 { - ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.VSphere.APIServerInternalIPs[0], securePortStr) - } - } - } - } - - return ignitionHost, nil -} - func (optr *Operator) syncMachineConfigPools(config *renderConfig, _ *configv1.ClusterOperator) error { generatedPools, err := manifests.GetMachineConfigPools() if err != nil { diff --git a/pkg/server/bootstrap_server.go b/pkg/server/bootstrap_server.go index d8b8cf3d30..e09acf1733 100644 --- a/pkg/server/bootstrap_server.go +++ b/pkg/server/bootstrap_server.go @@ -136,7 +136,7 @@ func (bsc *bootstrapServer) GetConfig(cr poolRequest) (*runtime.RawExtension, er addDataAndMaybeAppendToIgnition(cloudProviderCAPath, cc.Spec.CloudProviderCAData, &ignConf) appenders := newAppendersBuilder(nil, bsc.kubeconfigFunc, bsc.certs, bsc.serverBaseDir). - WithNodeAnnotations(currConf, ""). + WithNodeAnnotations(currConf, "", ""). build() for _, a := range appenders { diff --git a/pkg/server/cluster_server.go b/pkg/server/cluster_server.go index 59c6d13889..ac422bb5e3 100644 --- a/pkg/server/cluster_server.go +++ b/pkg/server/cluster_server.go @@ -21,6 +21,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" corelisterv1 "k8s.io/client-go/listers/core/v1" @@ -56,6 +57,7 @@ type clusterServer struct { kubeconfigFunc kubeconfigFunc apiserverURL string + mcsURL string } const minResyncPeriod = 20 * time.Minute @@ -85,6 +87,7 @@ func NewClusterServer(kubeConfig, apiserverURL string) (Server, error) { machineConfigClient := clientsBuilder.MachineConfigClientOrDie("machine-config-shared-informer") kubeClient := clientsBuilder.KubeClientOrDie("kube-client-shared-informer") routeClient := clientsBuilder.RouteClientOrDie("route-client") + configClient := clientsBuilder.ConfigClientOrDie("config-client") sharedInformerFactory := mcfginformers.NewSharedInformerFactory(machineConfigClient, resyncPeriod()()) kubeNamespacedSharedInformer := informers.NewSharedInformerFactoryWithOptions(kubeClient, resyncPeriod()(), informers.WithNamespace("openshift-machine-config-operator")) @@ -112,6 +115,19 @@ func NewClusterServer(kubeConfig, apiserverURL string) (Server, error) { return nil, errors.New("failed to wait for cache sync") } + // Fetch Infrastructure to compute MCS URL + ctx := context.Background() + infra, err := configClient.ConfigV1().Infrastructures().Get(ctx, "cluster", metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to get cluster infrastructure: %w", err) + } + + ignitionHost, err := GetIgnitionHost(&infra.Status) + if err != nil { + return nil, fmt.Errorf("failed to compute ignition host: %w", err) + } + mcsURL := fmt.Sprintf("https://%s", ignitionHost) + return &clusterServer{ machineConfigPoolLister: mcpLister, machineConfigLister: mcLister, @@ -123,6 +139,7 @@ func NewClusterServer(kubeConfig, apiserverURL string) (Server, error) { routeclient: routeClient, kubeconfigFunc: func() ([]byte, []byte, error) { return kubeconfigFromSecret(bootstrapTokenDir, apiserverURL, nil) }, apiserverURL: apiserverURL, + mcsURL: mcsURL, }, nil } @@ -187,7 +204,7 @@ func (cs *clusterServer) GetConfig(cr poolRequest) (*runtime.RawExtension, error desiredImage := cs.resolveDesiredImageForPool(mp) appenders := newAppendersBuilder(cr.version, cs.kubeconfigFunc, []string{}, ""). - WithNodeAnnotations(currConf, desiredImage). + WithNodeAnnotations(currConf, desiredImage, cs.mcsURL). WithCustomAppender(appendDesiredOSImage(desiredImage)). build() diff --git a/pkg/server/server.go b/pkg/server/server.go index ad1792bf92..a65c704b4d 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -2,9 +2,11 @@ package server import ( "fmt" + "net" "net/url" "os" "path/filepath" + "strconv" "strings" "github.com/clarketm/json" @@ -14,6 +16,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" + configv1 "github.com/openshift/api/config/v1" mcfgv1 "github.com/openshift/api/machineconfiguration/v1" build "github.com/openshift/machine-config-operator/pkg/controller/build/constants" ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common" @@ -70,9 +73,9 @@ func newAppendersBuilder(version *semver.Version, kubeconfigFn kubeconfigFunc, c } // WithNodeAnnotations adds the node annotations appender with the specified config and image. -func (ab *appendersBuilder) WithNodeAnnotations(currMachineConfig, image string) *appendersBuilder { +func (ab *appendersBuilder) WithNodeAnnotations(currMachineConfig, image, mcsURL string) *appendersBuilder { ab.customAppenders = append(ab.customAppenders, func(cfg *ign3types.Config, mc *mcfgv1.MachineConfig) error { - return appendNodeAnnotations(cfg, currMachineConfig, image, mc) + return appendNodeAnnotations(cfg, currMachineConfig, image, mcsURL, mc) }) return ab } @@ -196,8 +199,8 @@ func appendKubeConfig(conf *ign3types.Config, f kubeconfigFunc) error { return nil } -func appendNodeAnnotations(conf *ign3types.Config, currConf, image string, mc *mcfgv1.MachineConfig) error { - anno, err := getNodeAnnotation(currConf, image, mc) +func appendNodeAnnotations(conf *ign3types.Config, currConf, image, mcsURL string, mc *mcfgv1.MachineConfig) error { + anno, err := getNodeAnnotation(currConf, image, mcsURL, mc) if err != nil { return err } @@ -208,7 +211,7 @@ func appendNodeAnnotations(conf *ign3types.Config, currConf, image string, mc *m return nil } -func getNodeAnnotation(conf, image string, mc *mcfgv1.MachineConfig) (string, error) { +func getNodeAnnotation(conf, image, mcsURL string, mc *mcfgv1.MachineConfig) (string, error) { nodeAnnotations := map[string]string{ daemonconsts.CurrentMachineConfigAnnotationKey: conf, daemonconsts.DesiredMachineConfigAnnotationKey: conf, @@ -216,6 +219,10 @@ func getNodeAnnotation(conf, image string, mc *mcfgv1.MachineConfig) (string, er daemonconsts.MachineConfigDaemonStateAnnotationKey: daemonconsts.MachineConfigDaemonStateDone, } + if mcsURL != "" { + nodeAnnotations[daemonconsts.MachineConfigServerURLAnnotationKey] = mcsURL + } + // Determine which image to use: // 1. Pre-built image from MC annotations (install-time hybrid OCL) takes priority // 2. Dynamically resolved image parameter (runtime scaling) @@ -308,3 +315,34 @@ func addDataAndMaybeAppendToIgnition(path string, data []byte, ignConf *ign3type appendFileToIgnition(ignConf, path, (string(data))) } } + +// GetIgnitionHost computes the MCS hostname:port for serving Ignition configs +// from the cluster's Infrastructure status. Platform-specific IP addresses +// take precedence over the parsed APIServerInternalURL hostname. +func GetIgnitionHost(infraStatus *configv1.InfrastructureStatus) (string, error) { + internalURL := infraStatus.APIServerInternalURL + internalURLParsed, err := url.Parse(internalURL) + if err != nil { + return "", err + } + securePortStr := strconv.Itoa(SecurePort) + ignitionHost := fmt.Sprintf("%s:%s", internalURLParsed.Hostname(), securePortStr) + if infraStatus.PlatformStatus != nil { + switch infraStatus.PlatformStatus.Type { + case configv1.BareMetalPlatformType: + ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.BareMetal.APIServerInternalIPs[0], securePortStr) + case configv1.OpenStackPlatformType: + ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.OpenStack.APIServerInternalIPs[0], securePortStr) + case configv1.OvirtPlatformType: + ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.Ovirt.APIServerInternalIPs[0], securePortStr) + case configv1.VSpherePlatformType: + if infraStatus.PlatformStatus.VSphere != nil { + if len(infraStatus.PlatformStatus.VSphere.APIServerInternalIPs) > 0 { + ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.VSphere.APIServerInternalIPs[0], securePortStr) + } + } + } + } + + return ignitionHost, nil +} diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index 6a46689989..8c2affda77 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -185,7 +185,7 @@ func TestBootstrapServer(t *testing.T) { if err != nil { t.Fatalf("unexpected error while appending file to ignition: %v", err) } - anno, err := getNodeAnnotation(mp.Status.Configuration.Name, "", mc) + anno, err := getNodeAnnotation(mp.Status.Configuration.Name, "", "https://api-int.test.example.com:22623", mc) if err != nil { t.Fatalf("unexpected error while creating annotations err: %v", err) } @@ -380,7 +380,7 @@ func TestClusterServer(t *testing.T) { if err != nil { t.Fatalf("unexpected error while appending file to ignition: %v", err) } - anno, err := getNodeAnnotation(mp.Status.Configuration.Name, "", mc) + anno, err := getNodeAnnotation(mp.Status.Configuration.Name, "", "https://api-int.test.example.com:22623", mc) if err != nil { t.Fatalf("unexpected error while creating annotations err: %v", err) }