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: 9 additions & 5 deletions cmd/machine-config-server/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var (
serverBaseDir string
serverKubeConfig string
certificates []string
mcsURL string
}
)

Expand All @@ -30,6 +31,7 @@ func init() {
bootstrapCmd.PersistentFlags().StringVar(&bootstrapOpts.serverBaseDir, "server-basedir", "/etc/mcs/bootstrap", "base directory on the host, relative to which machine-configs and pools can be found.")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapOpts.serverKubeConfig, "bootstrap-kubeconfig", "/etc/kubernetes/kubeconfig", "path to bootstrap kubeconfig served by the bootstrap server.")
bootstrapCmd.PersistentFlags().StringArrayVar(&bootstrapOpts.certificates, "bootstrap-certs", []string{}, "a certificate bundle formatted in a string array with the format key=value,key=value")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapOpts.mcsURL, "mcs-url", "", "Base URL for Machine Config Server; Used for status reporting")
}

func runBootstrapCmd(_ *cobra.Command, _ []string) {
Expand All @@ -39,10 +41,9 @@ func runBootstrapCmd(_ *cobra.Command, _ []string) {
// To help debugging, immediately log version
klog.Infof("Version: %+v (%s)", version.Raw, version.Hash)

bs, err := server.NewBootstrapServer(bootstrapOpts.serverBaseDir, bootstrapOpts.serverKubeConfig, bootstrapOpts.certificates)

bs, err := server.NewBootstrapServer(bootstrapOpts.serverBaseDir, bootstrapOpts.serverKubeConfig, bootstrapOpts.mcsURL, bootstrapOpts.certificates)
if err != nil {
klog.Exitf("Machine Config Server exited with error: %v", err)
klog.Fatal(err)
}

// Read-in bootstrap apiserver file /etc/mcs/bootstrap/api-server/apiserver.yaml.
Expand All @@ -59,9 +60,12 @@ func runBootstrapCmd(_ *cobra.Command, _ []string) {
klog.Infof("Launching bootstrap server with tls min version: %v & cipher suites %v", tlsminversion, tlsciphersuites)
tlsConfig := ctrlcommon.GetGoTLSConfig(tlsminversion, tlsciphersuites)

failureReporter := server.NewBootstrapFailureReporter()
failureHandler := server.NewNodeFailureHandler(failureReporter)

apiHandler := server.NewServerAPIHandler(bs)
secureServer := server.NewAPIServer(apiHandler, rootOpts.sport, false, rootOpts.cert, rootOpts.key, tlsConfig)
insecureServer := server.NewAPIServer(apiHandler, rootOpts.isport, true, "", "", tlsConfig)
secureServer := server.NewAPIServer(apiHandler, failureHandler, rootOpts.sport, false, rootOpts.cert, rootOpts.key, tlsConfig)
insecureServer := server.NewAPIServer(apiHandler, failureHandler, rootOpts.isport, true, "", "", tlsConfig)

stopCh := make(chan struct{})
go secureServer.Serve()
Expand Down
11 changes: 8 additions & 3 deletions cmd/machine-config-server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ var (
startOpts struct {
kubeconfig string
apiserverURL string
mcsURL string
}
)

func init() {
rootCmd.AddCommand(startCmd)
startCmd.PersistentFlags().StringVar(&startOpts.kubeconfig, "kubeconfig", "", "Kubeconfig file to access a remote cluster (testing only)")
startCmd.PersistentFlags().StringVar(&startOpts.apiserverURL, "apiserver-url", "", "URL for apiserver; Used to generate kubeconfig")
startCmd.PersistentFlags().StringVar(&startOpts.mcsURL, "mcs-url", "", "Base URL for Machine Config Server; Used for status reporting")

}

Expand All @@ -47,17 +49,20 @@ func runStartCmd(_ *cobra.Command, _ []string) {
klog.Exitf("--apiserver-url cannot be empty")
}

cs, err := server.NewClusterServer(startOpts.kubeconfig, startOpts.apiserverURL)
cs, err := server.NewClusterServer(startOpts.kubeconfig, startOpts.apiserverURL, startOpts.mcsURL)
if err != nil {
ctrlcommon.WriteTerminationError(err)
}
Comment on lines +52 to 55
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Exit after NewClusterServer error to avoid nil dereference.

When NewClusterServer returns an error, execution continues and cs.GetKubeClient() can panic. Fail fast after writing the termination error.

Suggested fix
 	cs, err := server.NewClusterServer(startOpts.kubeconfig, startOpts.apiserverURL, startOpts.mcsURL)
 	if err != nil {
 		ctrlcommon.WriteTerminationError(err)
+		klog.Exitf("failed to initialize cluster server: %v", err)
 	}

Also applies to: 60-61

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/machine-config-server/start.go` around lines 52 - 55, The code calls
server.NewClusterServer and logs errors with ctrlcommon.WriteTerminationError
but continues execution, risking a nil dereference when calling
cs.GetKubeClient(); update the error handling in the NewClusterServer call (and
the similar block around cs.GetKubeClient() at the second occurrence) to fail
fast after ctrlcommon.WriteTerminationError by returning or exiting (e.g.,
return an error from start or call os.Exit(1)) so that cs is never dereferenced
when NewClusterServer returns an error.


klog.Infof("Launching server with tls min version: %v & cipher suites %v", rootOpts.tlsminversion, rootOpts.tlsciphersuites)
tlsConfig := ctrlcommon.GetGoTLSConfig(rootOpts.tlsminversion, rootOpts.tlsciphersuites)

failureReporter := server.NewClusterFailureReporter(cs.GetKubeClient())
failureHandler := server.NewNodeFailureHandler(failureReporter)

apiHandler := server.NewServerAPIHandler(cs)
secureServer := server.NewAPIServer(apiHandler, rootOpts.sport, false, rootOpts.cert, rootOpts.key, tlsConfig)
insecureServer := server.NewAPIServer(apiHandler, rootOpts.isport, true, "", "", tlsConfig)
secureServer := server.NewAPIServer(apiHandler, failureHandler, rootOpts.sport, false, rootOpts.cert, rootOpts.key, tlsConfig)
insecureServer := server.NewAPIServer(apiHandler, failureHandler, rootOpts.isport, true, "", "", tlsConfig)

stopCh := make(chan struct{})
go secureServer.Serve()
Expand Down
1 change: 1 addition & 0 deletions manifests/bootstrap-pod-v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ spec:
command: ["/usr/bin/machine-config-server"]
args:
- "bootstrap"
- "--mcs-url={{.MCSURL}}"
- "--payload-version={{.ReleaseVersion}}"
terminationMessagePolicy: FallbackToLogsOnError
volumeMounts:
Expand Down
1 change: 1 addition & 0 deletions manifests/machineconfigserver/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ spec:
args:
- "start"
- "--apiserver-url={{.APIServerURL}}"
- "--mcs-url={{.MCSURL}}"
- "--payload-version={{.ReleaseVersion}}"
- "--tls-cipher-suites={{join .TLSCipherSuites ","}}"
- "--tls-min-version={{.TLSMinVersion}}"
Expand Down
3 changes: 2 additions & 1 deletion pkg/daemon/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion pkg/operator/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,14 @@ func buildSpec(dependencies *BootstrapDependencies, imgs *ctrlcommon.Images, rel
templatectrl.DockerRegistryKey: imgs.DockerRegistry,
}

ignitionHost, err := getIgnitionHost(&dependencies.Infrastructure.Status)
if err != nil {
return nil, err
}
mcsURL := fmt.Sprintf("https://%s", ignitionHost)

config := getRenderConfig("", dependencies.KubeAPIServerServingCA, spec,
&imgs.RenderConfigImages, dependencies.Infrastructure, nil, nil, "2")
&imgs.RenderConfigImages, dependencies.Infrastructure, nil, nil, mcsURL, "2")
Comment on lines +150 to +157
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Guard getIgnitionHost against empty platform IP lists before bootstrap render.

Line 150 introduces a new bootstrap-time call path to getIgnitionHost, but that helper indexes APIServerInternalIPs[0] for some platforms without a length check. Empty lists will panic the render path.

Proposed hardening
--- a/pkg/operator/sync.go
+++ b/pkg/operator/sync.go
@@
 	if infraStatus.PlatformStatus != nil {
 		switch infraStatus.PlatformStatus.Type {
 		case configv1.BareMetalPlatformType:
-			ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.BareMetal.APIServerInternalIPs[0], securePortStr)
+			if infraStatus.PlatformStatus.BareMetal != nil && len(infraStatus.PlatformStatus.BareMetal.APIServerInternalIPs) > 0 {
+				ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.BareMetal.APIServerInternalIPs[0], securePortStr)
+			}
 		case configv1.OpenStackPlatformType:
-			ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.OpenStack.APIServerInternalIPs[0], securePortStr)
+			if infraStatus.PlatformStatus.OpenStack != nil && len(infraStatus.PlatformStatus.OpenStack.APIServerInternalIPs) > 0 {
+				ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.OpenStack.APIServerInternalIPs[0], securePortStr)
+			}
 		case configv1.OvirtPlatformType:
-			ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.Ovirt.APIServerInternalIPs[0], securePortStr)
+			if infraStatus.PlatformStatus.Ovirt != nil && len(infraStatus.PlatformStatus.Ovirt.APIServerInternalIPs) > 0 {
+				ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.Ovirt.APIServerInternalIPs[0], securePortStr)
+			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/operator/bootstrap.go` around lines 150 - 157, The call to
getIgnitionHost can panic because it indexes APIServerInternalIPs[0] without
verifying the slice length; update the getIgnitionHost function to check that
platform-specific APIServerInternalIPs (or equivalent IP lists) are non-empty
before indexing and return a clear error if empty, and keep the existing error
return path in the bootstrap caller (the call site in bootstrap.go already
checks err). Ensure the check covers all platforms handled inside
getIgnitionHost and that the returned error message identifies the missing
internal IP so the bootstrap render path can fail gracefully instead of
panicking.

return config, nil
}

Expand Down
1 change: 1 addition & 0 deletions pkg/operator/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type renderConfig struct {
ControllerConfig mcfgv1.ControllerConfigSpec
KubeAPIServerServingCA string
APIServerURL string
MCSURL string
Images *ctrlcommon.RenderConfigImages
Infra configv1.Infrastructure
Constants map[string]string
Expand Down
7 changes: 5 additions & 2 deletions pkg/operator/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,8 @@ func (optr *Operator) syncRenderConfig(_ *renderConfig, _ *configv1.ClusterOpera
return err
}

mcsURL := fmt.Sprintf("https://%s", ignitionHost)

pointerConfig, err := ctrlcommon.PointerConfig(ignitionHost, machineConfigServerCABundle)
if err != nil {
return err
Expand All @@ -650,7 +652,7 @@ func (optr *Operator) syncRenderConfig(_ *renderConfig, _ *configv1.ClusterOpera
optr.setOperatorLogLevel(mcop.Spec.OperatorLogLevel)
}

optr.renderConfig = getRenderConfig(optr.namespace, string(kubeAPIServerServingCABytes), spec, &imgs.RenderConfigImages, infra, pointerConfigData, apiServer, fmt.Sprintf("%d", optr.logLevel))
optr.renderConfig = getRenderConfig(optr.namespace, string(kubeAPIServerServingCABytes), spec, &imgs.RenderConfigImages, infra, pointerConfigData, apiServer, mcsURL, fmt.Sprintf("%d", optr.logLevel))

return nil
}
Expand Down Expand Up @@ -2150,7 +2152,7 @@ func setGVK(obj runtime.Object, scheme *runtime.Scheme) error {
return nil
}

func getRenderConfig(tnamespace, kubeAPIServerServingCA string, ccSpec *mcfgv1.ControllerConfigSpec, imgs *ctrlcommon.RenderConfigImages, infra *configv1.Infrastructure, pointerConfigData []byte, apiServer *configv1.APIServer, logLevel string) *renderConfig {
func getRenderConfig(tnamespace, kubeAPIServerServingCA string, ccSpec *mcfgv1.ControllerConfigSpec, imgs *ctrlcommon.RenderConfigImages, infra *configv1.Infrastructure, pointerConfigData []byte, apiServer *configv1.APIServer, mcsURL, logLevel string) *renderConfig {
tlsMinVersion, tlsCipherSuites := ctrlcommon.GetSecurityProfileCiphersFromAPIServer(apiServer)
return &renderConfig{
TargetNamespace: tnamespace,
Expand All @@ -2160,6 +2162,7 @@ func getRenderConfig(tnamespace, kubeAPIServerServingCA string, ccSpec *mcfgv1.C
Images: imgs,
KubeAPIServerServingCA: kubeAPIServerServingCA,
APIServerURL: infra.Status.APIServerInternalURL,
MCSURL: mcsURL,
PointerConfig: string(pointerConfigData),
Infra: *infra,
TLSMinVersion: tlsMinVersion,
Expand Down
68 changes: 67 additions & 1 deletion pkg/server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ type APIServer struct {
// NewAPIServer initializes a new API server
// that runs the Machine Config Server as a
// handler.
func NewAPIServer(a *APIHandler, p int, is bool, c, k string, t *tls.Config) *APIServer {
func NewAPIServer(a *APIHandler, failureHandler *NodeFailureHandler, p int, is bool, c, k string, t *tls.Config) *APIServer {
mux := http.NewServeMux()
mux.Handle("/config/", a)
mux.Handle("/healthz", &healthHandler{})
mux.Handle("/v1/node-failure", failureHandler) // NEW
mux.Handle("/", &defaultHandler{})
Comment on lines +48 to 53
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Guard against nil failureHandler in NewAPIServer.

http.ServeMux.Handle panics with a nil handler. NewAPIServer should tolerate nil (or explicitly fail) instead of panicking during initialization.

Suggested fix
 func NewAPIServer(a *APIHandler, failureHandler *NodeFailureHandler, p int, is bool, c, k string, t *tls.Config) *APIServer {
 	mux := http.NewServeMux()
 	mux.Handle("/config/", a)
 	mux.Handle("/healthz", &healthHandler{})
-	mux.Handle("/v1/node-failure", failureHandler) // NEW
+	if failureHandler != nil {
+		mux.Handle("/v1/node-failure", failureHandler)
+	}
 	mux.Handle("/", &defaultHandler{})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func NewAPIServer(a *APIHandler, failureHandler *NodeFailureHandler, p int, is bool, c, k string, t *tls.Config) *APIServer {
mux := http.NewServeMux()
mux.Handle("/config/", a)
mux.Handle("/healthz", &healthHandler{})
mux.Handle("/v1/node-failure", failureHandler) // NEW
mux.Handle("/", &defaultHandler{})
func NewAPIServer(a *APIHandler, failureHandler *NodeFailureHandler, p int, is bool, c, k string, t *tls.Config) *APIServer {
mux := http.NewServeMux()
mux.Handle("/config/", a)
mux.Handle("/healthz", &healthHandler{})
if failureHandler != nil {
mux.Handle("/v1/node-failure", failureHandler)
}
mux.Handle("/", &defaultHandler{})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/server/api.go` around lines 48 - 53, NewAPIServer currently calls
mux.Handle("/v1/node-failure", failureHandler) which will panic if
failureHandler is nil; update NewAPIServer to guard this by checking if
failureHandler != nil before registering, and if it is nil register a safe
fallback (e.g., http.NotFoundHandler() or a small handler that returns 404/503)
or skip registration entirely so ServeMux.Handle is never called with a nil
value; adjust the code around NewAPIServer, NodeFailureHandler, and the
mux.Handle call accordingly.


return &APIServer{
Expand Down Expand Up @@ -182,6 +183,71 @@ func (sh *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

type healthHandler struct{}

// NodeFailureHandler handles POST requests to /v1/node-failure
type NodeFailureHandler struct {
reporter FailureReporter
}

// NewNodeFailureHandler creates a handler for node failure reports
func NewNodeFailureHandler(reporter FailureReporter) *NodeFailureHandler {
return &NodeFailureHandler{
reporter: reporter,
}
}

// ServeHTTP processes firstboot failure reports from nodes
func (h *NodeFailureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Only accept POST
if r.Method != http.MethodPost {
w.Header().Set("Content-Length", "0")
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

// Read and decode JSON body
var report FirstbootFailureReport
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&report); err != nil {
klog.Errorf("Failed to decode failure report: %v", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"error": "Invalid JSON payload",
})
return
}

// Validate required fields
if report.Pool == "" || report.NodeID == "" || report.Stage == "" {
klog.Errorf("Missing required fields in failure report: pool=%s node=%s stage=%s",
report.Pool, report.NodeID, report.Stage)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"error": "Missing required fields: pool, nodeID, and stage are required",
})
return
}

klog.Infof("Received failure report: pool=%s node=%s stage=%s",
report.Pool, report.NodeID, report.Stage)

// Report the failure (best-effort, errors are logged but don't fail the request)
ctx := r.Context()
if err := h.reporter.ReportFailure(ctx, &report); err != nil {
// Log error but still return 202 - we don't want the node to retry-loop
klog.Errorf("Failed to process failure report (still returning 202): %v", err)
}

// Always return 202 Accepted (best-effort delivery)
// Never return 5xx - the node must not retry-loop on reporter errors
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
json.NewEncoder(w).Encode(map[string]string{
"status": "accepted",
})
}

type acceptHeaderValue struct {
MIMEType string
MIMESubtype string
Expand Down
7 changes: 6 additions & 1 deletion pkg/server/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/net/http2"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"

ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common"
"github.com/openshift/machine-config-operator/test/helpers"
Expand All @@ -28,6 +29,10 @@ func (ms *mockServer) GetConfig(pr poolRequest) (*runtime.RawExtension, error) {
return ms.GetConfigFn(pr)
}

func (ms *mockServer) GetKubeClient() kubernetes.Interface {
return nil
}

type checkResponse func(t *testing.T, response *http.Response)

type scenario struct {
Expand Down Expand Up @@ -717,7 +722,7 @@ func TestAPIServer(t *testing.T) {
ms := &mockServer{
GetConfigFn: scenario.serverFunc,
}
server := NewAPIServer(NewServerAPIHandler(ms), 0, false, "", "", nil)
server := NewAPIServer(NewServerAPIHandler(ms), nil, 0, false, "", "", nil)
server.handler.ServeHTTP(w, scenario.request)

resp := w.Result()
Expand Down
13 changes: 11 additions & 2 deletions pkg/server/bootstrap_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

yaml "github.com/ghodss/yaml"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
clientcmd "k8s.io/client-go/tools/clientcmd/api/v1"
"k8s.io/klog/v2"

Expand All @@ -28,18 +29,26 @@ type bootstrapServer struct {
kubeconfigFunc kubeconfigFunc

certs []string

mcsURL string
}

// GetKubeClient returns nil for bootstrap server (no k8s access during bootstrap)
func (bsc *bootstrapServer) GetKubeClient() kubernetes.Interface {
return nil
}

// NewBootstrapServer initializes a new Bootstrap server that implements
// the Server interface.
func NewBootstrapServer(dir, kubeconfig string, ircerts []string) (Server, error) {
func NewBootstrapServer(dir, kubeconfig, mcsURL string, ircerts []string) (Server, error) {
if _, err := os.Stat(kubeconfig); err != nil {
return nil, fmt.Errorf("kubeconfig not found at location: %s", kubeconfig)
}
return &bootstrapServer{
serverBaseDir: dir,
kubeconfigFunc: func() ([]byte, []byte, error) { return kubeconfigFromFile(kubeconfig) },
certs: ircerts,
mcsURL: mcsURL,
}, nil
}

Expand Down Expand Up @@ -136,7 +145,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, "", bsc.mcsURL).
build()

for _, a := range appenders {
Expand Down
12 changes: 10 additions & 2 deletions pkg/server/cluster_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
clientset "k8s.io/client-go/kubernetes"
corelisterv1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
Expand Down Expand Up @@ -56,6 +57,7 @@ type clusterServer struct {

kubeconfigFunc kubeconfigFunc
apiserverURL string
mcsURL string
}

const minResyncPeriod = 20 * time.Minute
Expand All @@ -76,7 +78,7 @@ func resyncPeriod() func() time.Duration {
// It accepts a kubeConfig, which is not required when it's
// run from within a cluster(useful in testing).
// It accepts the apiserverURL which is the location of the KubeAPIServer.
func NewClusterServer(kubeConfig, apiserverURL string) (Server, error) {
func NewClusterServer(kubeConfig, apiserverURL, mcsURL string) (Server, error) {
clientsBuilder, err := clients.NewBuilder(kubeConfig)
if err != nil {
return nil, fmt.Errorf("failed to create Kubernetes rest client: %w", err)
Expand Down Expand Up @@ -123,6 +125,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
}

Expand Down Expand Up @@ -187,7 +190,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()

Expand Down Expand Up @@ -369,3 +372,8 @@ func appendDesiredOSImage(desired string) appenderFunc {
return nil
}
}

// GetKubeClient returns the Kubernetes client for this cluster server
func (cs *clusterServer) GetKubeClient() kubernetes.Interface {
return cs.kubeclient
}
Loading