Skip to content
Open
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
5 changes: 1 addition & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ require (
github.com/openshift/hive/apis v0.0.0
github.com/openshift/installer v1.4.22-ec5
github.com/openshift/library-go v0.0.0-20251107090138-0de9712313a5
github.com/openshift/machine-api-operator v0.2.1-0.20251128002018-85c00c0d525f
github.com/openshift/machine-api-operator v0.2.1-0.20251128002018-85c00c0d525f // indirect
github.com/openshift/machine-api-provider-gcp v0.0.1-0.20260113091719-80740861bb2a
github.com/openshift/machine-api-provider-ibmcloud v0.0.0-20231207164151-6b0b8ea7b16d
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -429,15 +429,12 @@ require (
google.golang.org/genproto v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/gcfg.v1 v1.2.3 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
honnef.co/go/tools v0.6.1 // indirect
k8s.io/apiserver v0.35.4 // indirect
k8s.io/cloud-provider-vsphere v1.33.3 // indirect
k8s.io/component-base v0.35.4 // indirect
k8s.io/component-helpers v0.35.4 // indirect
k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect
Expand Down
6 changes: 0 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -849,8 +849,6 @@ github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1 h1:Hr/R38eg5ZJ
github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1/go.mod h1:Za51LlH76ALiQ/aKGBYJXmyJNkA//IDJ+I///30CA2M=
github.com/openshift/cloud-credential-operator v0.0.0-20250925191450-a3e50aeacc55 h1:l4vr7i3v6Z+Ho2X6Msr7dUK1HqyGjwqNvsZ8qITyzxc=
github.com/openshift/cloud-credential-operator v0.0.0-20250925191450-a3e50aeacc55/go.mod h1:vRzx2kLy4/GPEwJb/PDUyS3C1/eLkhQD5Hezp+wQQCw=
github.com/openshift/cloud-provider-vsphere v1.19.1-0.20240626105621-6464d0bb4928 h1:gX0HAKR0f40xmMWlUSn8DBMCjip8Iuzg5XToWAv6Uzw=
github.com/openshift/cloud-provider-vsphere v1.19.1-0.20240626105621-6464d0bb4928/go.mod h1:eVEtCena8tmeeYDKwZlA2w+xBKzBHbQZ4CNHV8KL4ho=
github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d h1:+sqUThLi/lmgT5/scmmjnS6+RZFtbdxRAscNfCPyLPI=
github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20250910145856-21d03d30056d/go.mod h1:9+FWWWLkVrnBo1eYhA/0Ehlq5JMgIAHtcB0IF+qV1AA=
github.com/openshift/cluster-autoscaler-operator v0.0.1-0.20250219201631-227f7537c3b4 h1:Xepsi7/s1kHkemmhyWDFV21dijgXZUcT0+8YWh6CbxU=
Expand Down Expand Up @@ -1371,8 +1369,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
Expand All @@ -1383,8 +1379,6 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
11 changes: 10 additions & 1 deletion pkg/controller/machinepool/machinepool_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
autoscalingv1beta1 "github.com/openshift/cluster-autoscaler-operator/pkg/apis/autoscaling/v1beta1"
cpms "github.com/openshift/cluster-control-plane-machine-set-operator/pkg/machineproviders/providers/openshift/machine/v1beta1/providerconfig"
installertypes "github.com/openshift/installer/pkg/types"
installervsphere "github.com/openshift/installer/pkg/types/vsphere"
"github.com/openshift/library-go/pkg/operator/resource/resourcemerge"
ibmcloudprovider "github.com/openshift/machine-api-provider-ibmcloud/pkg/apis/ibmcloudprovider/v1"

Expand Down Expand Up @@ -1393,7 +1394,15 @@ func (r *ReconcileMachinePool) createActuator(
case cd.Spec.Platform.OpenStack != nil:
return NewOpenStackActuator(masterMachine, r.Client, logger)
case cd.Spec.Platform.VSphere != nil:
return NewVSphereActuator(masterMachine, r.scheme, logger)
var fds []installervsphere.FailureDomain
var infraID string
if cd.Spec.Platform.VSphere.Infrastructure != nil {
fds = cd.Spec.Platform.VSphere.Infrastructure.FailureDomains
}
if cd.Spec.ClusterMetadata != nil {
infraID = cd.Spec.ClusterMetadata.InfraID
}
return NewVSphereActuator(remoteMachineSets, infraID, fds, logger)
default:
return nil, errors.New("unsupported platform")
}
Expand Down
105 changes: 98 additions & 7 deletions pkg/controller/machinepool/vsphereactuator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package machinepool

import (
"encoding/json"
"fmt"
"path"

"github.com/pkg/errors"
log "github.com/sirupsen/logrus"

Expand All @@ -9,22 +13,34 @@ import (
machineapi "github.com/openshift/api/machine/v1beta1"
installvspheremachines "github.com/openshift/installer/pkg/asset/machines/vsphere"
installertypes "github.com/openshift/installer/pkg/types"
installervsphere "github.com/openshift/installer/pkg/types/vsphere"

hivev1 "github.com/openshift/hive/apis/hive/v1"
)

// VSphereActuator encapsulates the pieces necessary to be able to generate
// a list of MachineSets to sync to the remote cluster
// a list of MachineSets to sync to the remote cluster.
type VSphereActuator struct {
logger log.FieldLogger
logger log.FieldLogger
templates map[string]string // fd-name → template extracted from remote MachineSets
}

var _ Actuator = &VSphereActuator{}

// NewVSphereActuator is the constructor for building a VSphereActuator
func NewVSphereActuator(masterMachine *machineapi.Machine, scheme *runtime.Scheme, logger log.FieldLogger) (*VSphereActuator, error) {
// NewVSphereActuator is the constructor for building a VSphereActuator.
// Following the GCP actuator pattern, it preprocesses remoteMachineSets into scalar
// data (a per-failure-domain template map) at construction time so that GenerateMachineSets
// can apply it after DeepCopy without mutating the ClusterDeployment.
func NewVSphereActuator(
remoteMachineSets []machineapi.MachineSet,
infraID string,
failureDomains []installervsphere.FailureDomain,
logger log.FieldLogger,
) (*VSphereActuator, error) {
templates := buildTemplateMap(failureDomains, remoteMachineSets, infraID, logger)
return &VSphereActuator{
logger: logger,
logger: logger,
templates: templates,
}, nil
}

Expand All @@ -47,20 +63,28 @@ func (a *VSphereActuator) GenerateMachineSets(cd *hivev1.ClusterDeployment, pool
computePool := baseMachinePool(pool)
computePool.Platform.VSphere = &pool.Spec.Platform.VSphere.MachinePool

// Fake an install config as we do with other actuators.
ic := &installertypes.InstallConfig{
Platform: installertypes.Platform{
VSphere: cd.Spec.Platform.VSphere.Infrastructure.DeepCopy(),
},
}
for i := range ic.VSphere.FailureDomains {
failureDomain := &ic.VSphere.FailureDomains[i] // because go ranges by copy, not by reference
failureDomain := &ic.VSphere.FailureDomains[i]
if pool.Spec.Platform.VSphere.ResourcePool != "" {
failureDomain.Topology.ResourcePool = pool.Spec.Platform.VSphere.ResourcePool
}
if len(pool.Spec.Platform.VSphere.TagIDs) > 0 {
failureDomain.Topology.TagIDs = pool.Spec.Platform.VSphere.TagIDs
}
if failureDomain.Topology.Template == "" {
if tmpl, ok := a.templates[failureDomain.Name]; ok {
failureDomain.Topology.Template = tmpl
logger.WithFields(log.Fields{
"failureDomain": failureDomain.Name,
"template": tmpl,
}).Info("applied backfilled Topology.Template from remote MachineSet")
}
}
}

installerMachineSets, err := installvspheremachines.MachineSets(
Expand All @@ -76,3 +100,70 @@ func (a *VSphereActuator) GenerateMachineSets(cd *hivev1.ClusterDeployment, pool

return installerMachineSets, true, nil
}

// buildTemplateMap creates a fd-name → template mapping by matching each failure domain
// (where Template is empty) against remote MachineSets using workspace fields.
func buildTemplateMap(failureDomains []installervsphere.FailureDomain, remoteMachineSets []machineapi.MachineSet, infraID string, logger log.FieldLogger) map[string]string {
templates := make(map[string]string)
for i := range failureDomains {
fd := &failureDomains[i]
if fd.Topology.Template != "" {
continue
}
template := findTemplateForFD(fd, remoteMachineSets, infraID, logger)
if template != "" {
templates[fd.Name] = template
logger.WithFields(log.Fields{
"failureDomain": fd.Name,
"template": template,
}).Info("found template backfill from remote MachineSet")
}
}
return templates
}

// findTemplateForFD searches remote MachineSets for one whose workspace fields match the
// given failure domain using a 5-field conjunction (MCO PR #5745 pattern):
// Datacenter + Datastore + Server + VMGroup + ResourcePool.
// Always matches against the FD's own ResourcePool since existing remote MachineSets
// were created with the FD default, not a MachinePool-level override.
func findTemplateForFD(fd *installervsphere.FailureDomain, remoteMachineSets []machineapi.MachineSet, infraID string, logger log.FieldLogger) string {
vmGroup := ""
if fd.ZoneType == installervsphere.HostGroupFailureDomain {
vmGroup = fmt.Sprintf("%s-%s", infraID, fd.Name)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this pattern reliable? The whole reason we're here is because such a pattern changed upstream.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Still this. @jcpowermac ?

}

for _, ms := range remoteMachineSets {
spec, err := vsphereProviderSpecFromRawExtension(ms.Spec.Template.Spec.ProviderSpec.Value)
if err != nil {
logger.WithError(err).WithField("machineSet", ms.Name).Warn("cannot decode VSphereMachineProviderSpec")
continue
}
if spec.Template == "" || spec.Workspace == nil {
continue
}

if spec.Workspace.Datacenter != fd.Topology.Datacenter ||
spec.Workspace.Datastore != fd.Topology.Datastore ||
spec.Workspace.Server != fd.Server ||
spec.Workspace.VMGroup != vmGroup ||
path.Clean(spec.Workspace.ResourcePool) != path.Clean(fd.Topology.ResourcePool) {
continue
}

return spec.Template
}
return ""
}

// vsphereProviderSpecFromRawExtension unmarshals a JSON-encoded VSphereMachineProviderSpec.
func vsphereProviderSpecFromRawExtension(rawExtension *runtime.RawExtension) (*machineapi.VSphereMachineProviderSpec, error) {
if rawExtension == nil {
return &machineapi.VSphereMachineProviderSpec{}, nil
}
spec := new(machineapi.VSphereMachineProviderSpec)
if err := json.Unmarshal(rawExtension.Raw, spec); err != nil {
return nil, errors.Wrap(err, "error unmarshalling VSphereMachineProviderSpec")
}
return spec, nil
}
Loading