From 29791fd83f3bad296a7d9212cb1c46ff1eb8edb1 Mon Sep 17 00:00:00 2001 From: Chin2691 Date: Wed, 1 Jul 2026 14:00:32 +0530 Subject: [PATCH] OCPBUGS-91668: backfill vSphere template from remote MachineSets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clusters installed before the HIVE-2391 template naming change have empty Topology.Template fields in their ClusterDeployment. When the machinepool controller generates new MachineSets, Infrastructure.DeepCopy() preserves the empty Template, causing the installer to generate a new-format name that does not exist on the vCenter. Fix: follow the GCP actuator pattern — NewVSphereActuator preprocesses remoteMachineSets into a fd-name→template map at construction time, then GenerateMachineSets applies it to the DeepCopy'd install-config. The ClusterDeployment is never mutated. Matching uses a 5-field workspace conjunction (Datacenter, Datastore, Server, VMGroup, ResourcePool) mirroring MCO PR #5745. VMGroup is derived as {infraID}-{fdName} only for host-group zonal failure domains. Additionally relaxes the ClusterDeployment webhook to allow setting Topology.Template when previously blank, using the established spoof pattern (6 existing instances). This permits customers to set the template explicitly and avoids rejecting updates when the backfill-sourced template from a remote MachineSet is itself invalid. Removes the machine-api-operator vendor dependency (~7KLOC) by inlining the VSphereMachineProviderSpec unmarshalling with encoding/json. Assisted-by: Claude Code Co-authored-by: Cursor --- go.mod | 5 +- go.sum | 6 - .../machinepool/machinepool_controller.go | 11 +- pkg/controller/machinepool/vsphereactuator.go | 105 +- .../machinepool/vsphereactuator_test.go | 321 ++- ...terdeployment_validating_admission_hook.go | 17 + ...ployment_validating_admission_hook_test.go | 159 ++ .../pkg/controller/vsphere/actuator.go | 202 -- .../pkg/controller/vsphere/machine_scope.go | 304 --- .../pkg/controller/vsphere/reconciler.go | 1864 ----------------- .../pkg/controller/vsphere/session/session.go | 243 --- .../vsphere/session/tag_ids_caching_client.go | 241 --- .../pkg/controller/vsphere/util.go | 280 --- .../pkg/util/ipam/util.go | 231 -- vendor/gopkg.in/gcfg.v1/LICENSE | 28 - vendor/gopkg.in/gcfg.v1/README | 4 - vendor/gopkg.in/gcfg.v1/doc.go | 145 -- vendor/gopkg.in/gcfg.v1/errors.go | 57 - vendor/gopkg.in/gcfg.v1/read.go | 257 --- vendor/gopkg.in/gcfg.v1/scanner/errors.go | 121 -- vendor/gopkg.in/gcfg.v1/scanner/scanner.go | 342 --- vendor/gopkg.in/gcfg.v1/set.go | 329 --- vendor/gopkg.in/gcfg.v1/token/position.go | 435 ---- vendor/gopkg.in/gcfg.v1/token/serialize.go | 56 - vendor/gopkg.in/gcfg.v1/token/token.go | 83 - vendor/gopkg.in/gcfg.v1/types/bool.go | 23 - vendor/gopkg.in/gcfg.v1/types/doc.go | 4 - vendor/gopkg.in/gcfg.v1/types/enum.go | 44 - vendor/gopkg.in/gcfg.v1/types/int.go | 86 - vendor/gopkg.in/gcfg.v1/types/scan.go | 23 - vendor/gopkg.in/warnings.v0/LICENSE | 24 - vendor/gopkg.in/warnings.v0/README | 77 - vendor/gopkg.in/warnings.v0/warnings.go | 194 -- vendor/k8s.io/cloud-provider-vsphere/LICENSE | 202 -- .../pkg/common/config/config.go | 285 --- .../pkg/common/config/config_ini_legacy.go | 265 --- .../pkg/common/config/config_yaml.go | 229 -- .../pkg/common/config/consts_and_errors.go | 69 - .../pkg/common/config/types_common.go | 117 -- .../pkg/common/config/types_ini_legacy.go | 128 -- .../pkg/common/config/types_yaml.go | 132 -- vendor/modules.txt | 15 - 42 files changed, 546 insertions(+), 7217 deletions(-) delete mode 100644 vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/actuator.go delete mode 100644 vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/machine_scope.go delete mode 100644 vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/reconciler.go delete mode 100644 vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/session/session.go delete mode 100644 vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/session/tag_ids_caching_client.go delete mode 100644 vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/util.go delete mode 100644 vendor/github.com/openshift/machine-api-operator/pkg/util/ipam/util.go delete mode 100644 vendor/gopkg.in/gcfg.v1/LICENSE delete mode 100644 vendor/gopkg.in/gcfg.v1/README delete mode 100644 vendor/gopkg.in/gcfg.v1/doc.go delete mode 100644 vendor/gopkg.in/gcfg.v1/errors.go delete mode 100644 vendor/gopkg.in/gcfg.v1/read.go delete mode 100644 vendor/gopkg.in/gcfg.v1/scanner/errors.go delete mode 100644 vendor/gopkg.in/gcfg.v1/scanner/scanner.go delete mode 100644 vendor/gopkg.in/gcfg.v1/set.go delete mode 100644 vendor/gopkg.in/gcfg.v1/token/position.go delete mode 100644 vendor/gopkg.in/gcfg.v1/token/serialize.go delete mode 100644 vendor/gopkg.in/gcfg.v1/token/token.go delete mode 100644 vendor/gopkg.in/gcfg.v1/types/bool.go delete mode 100644 vendor/gopkg.in/gcfg.v1/types/doc.go delete mode 100644 vendor/gopkg.in/gcfg.v1/types/enum.go delete mode 100644 vendor/gopkg.in/gcfg.v1/types/int.go delete mode 100644 vendor/gopkg.in/gcfg.v1/types/scan.go delete mode 100644 vendor/gopkg.in/warnings.v0/LICENSE delete mode 100644 vendor/gopkg.in/warnings.v0/README delete mode 100644 vendor/gopkg.in/warnings.v0/warnings.go delete mode 100644 vendor/k8s.io/cloud-provider-vsphere/LICENSE delete mode 100644 vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config.go delete mode 100644 vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config_ini_legacy.go delete mode 100644 vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config_yaml.go delete mode 100644 vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/consts_and_errors.go delete mode 100644 vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_common.go delete mode 100644 vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_ini_legacy.go delete mode 100644 vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_yaml.go diff --git a/go.mod b/go.mod index 816196fb691..1612e244361 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -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 diff --git a/go.sum b/go.sum index 6da213633e0..51da9c18cc0 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= @@ -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= diff --git a/pkg/controller/machinepool/machinepool_controller.go b/pkg/controller/machinepool/machinepool_controller.go index b29b2b8caf3..ec23e5951b9 100644 --- a/pkg/controller/machinepool/machinepool_controller.go +++ b/pkg/controller/machinepool/machinepool_controller.go @@ -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" @@ -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") } diff --git a/pkg/controller/machinepool/vsphereactuator.go b/pkg/controller/machinepool/vsphereactuator.go index 7230a06f12c..22ac2bdd8cc 100644 --- a/pkg/controller/machinepool/vsphereactuator.go +++ b/pkg/controller/machinepool/vsphereactuator.go @@ -1,6 +1,10 @@ package machinepool import ( + "encoding/json" + "fmt" + "path" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -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 } @@ -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( @@ -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) + } + + 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 +} diff --git a/pkg/controller/machinepool/vsphereactuator_test.go b/pkg/controller/machinepool/vsphereactuator_test.go index 5363c7587f9..8e1c2e78720 100644 --- a/pkg/controller/machinepool/vsphereactuator_test.go +++ b/pkg/controller/machinepool/vsphereactuator_test.go @@ -1,6 +1,7 @@ package machinepool import ( + "encoding/json" "fmt" "testing" @@ -11,13 +12,12 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" machineapi "github.com/openshift/api/machine/v1beta1" - vsphereutil "github.com/openshift/machine-api-operator/pkg/controller/vsphere" hivev1 "github.com/openshift/hive/apis/hive/v1" hivev1vsphere "github.com/openshift/hive/apis/hive/v1/vsphere" - "github.com/openshift/hive/pkg/util/scheme" ) func TestVSphereActuator(t *testing.T) { @@ -25,7 +25,6 @@ func TestVSphereActuator(t *testing.T) { name string clusterDeployment *hivev1.ClusterDeployment pool *hivev1.MachinePool - masterMachine *machineapi.Machine expectedMachineSetReplicas map[string]int64 expectedErr bool }{ @@ -33,14 +32,12 @@ func TestVSphereActuator(t *testing.T) { name: "deprecated vsphere fields", clusterDeployment: testDeprecatedVSphereClusterDeployment(), pool: testVSpherePool(), - masterMachine: testVSphereMachine("master0", "master"), expectedErr: true, }, { name: "generate machineset", clusterDeployment: testVSphereClusterDeployment(), pool: testVSpherePool(), - masterMachine: testVSphereMachine("master0", "master"), expectedMachineSetReplicas: map[string]int64{ fmt.Sprintf("%s-worker-0", testInfraID): 3, }, @@ -49,9 +46,17 @@ func TestVSphereActuator(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - scheme := scheme.GetScheme() - - actuator, err := NewVSphereActuator(test.masterMachine, scheme, log.WithField("actuator", "vsphereactuator_test")) + var fds []vsphere.FailureDomain + var infraID string + if test.clusterDeployment.Spec.Platform.VSphere != nil && + test.clusterDeployment.Spec.Platform.VSphere.Infrastructure != nil { + fds = test.clusterDeployment.Spec.Platform.VSphere.Infrastructure.FailureDomains + } + if test.clusterDeployment.Spec.ClusterMetadata != nil { + infraID = test.clusterDeployment.Spec.ClusterMetadata.InfraID + } + logger := log.WithField("actuator", "vsphereactuator_test") + actuator, err := NewVSphereActuator(nil, infraID, fds, logger) assert.NoError(t, err, "unexpected error creating VSphereActuator") generatedMachineSets, _, err := actuator.GenerateMachineSets(test.clusterDeployment, test.pool, actuator.logger) @@ -66,6 +71,254 @@ func TestVSphereActuator(t *testing.T) { } } +func TestBackfillVSphereTemplates(t *testing.T) { + logger := log.WithField("test", "backfill") + + tests := []struct { + name string + failureDomains []vsphere.FailureDomain + remoteMachineSets []machineapi.MachineSet + infraID string + expectedTemplates map[string]string + }{ + { + name: "non-zonal: backfill from matching MachineSet", + failureDomains: []vsphere.FailureDomain{ + { + Name: "generated-failure-domain", + Server: "vcenter.example.com", + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "datastore1", + ResourcePool: "/dc1/host/cluster1/Resources", + }, + }, + }, + remoteMachineSets: []machineapi.MachineSet{ + testRemoteMachineSet("test-infra-worker-0", "vcenter.example.com", "dc1", "datastore1", "/dc1/host/cluster1/Resources", "", "/dc1/vm/test-infra-rhcos-generated-region-generated-zone"), + }, + infraID: "test-infra", + expectedTemplates: map[string]string{ + "generated-failure-domain": "/dc1/vm/test-infra-rhcos-generated-region-generated-zone", + }, + }, + { + name: "non-zonal: template already set, not overwritten", + failureDomains: []vsphere.FailureDomain{ + { + Name: "fd1", + Server: "vcenter.example.com", + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "datastore1", + ResourcePool: "/dc1/host/cluster1/Resources", + Template: "/dc1/vm/explicit-template", + }, + }, + }, + remoteMachineSets: []machineapi.MachineSet{ + testRemoteMachineSet("test-infra-worker-0", "vcenter.example.com", "dc1", "datastore1", "/dc1/host/cluster1/Resources", "", "/dc1/vm/test-infra-rhcos-fd1"), + }, + infraID: "test-infra", + expectedTemplates: map[string]string{ + "fd1": "", + }, + }, + { + name: "non-zonal: no matching remote MachineSet, template stays empty", + failureDomains: []vsphere.FailureDomain{ + { + Name: "fd1", + Server: "vcenter.example.com", + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "datastore1", + ResourcePool: "/dc1/host/cluster1/Resources", + }, + }, + }, + remoteMachineSets: []machineapi.MachineSet{ + testRemoteMachineSet("test-infra-worker-0", "other-vcenter.example.com", "dc2", "datastore2", "/dc2/host/cluster2/Resources", "", "/dc2/vm/test-infra-rhcos-fd2"), + }, + infraID: "test-infra", + expectedTemplates: map[string]string{"fd1": ""}, + }, + { + name: "compute-cluster zonal: ResourcePool disambiguates FDs", + failureDomains: []vsphere.FailureDomain{ + { + Name: "us-east-1", + Server: "vcenter.example.com", + ZoneType: vsphere.ComputeClusterFailureDomain, + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "shared-ds", + ResourcePool: "/dc1/host/cluster1/Resources", + }, + }, + { + Name: "us-east-2", + Server: "vcenter.example.com", + ZoneType: vsphere.ComputeClusterFailureDomain, + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "shared-ds", + ResourcePool: "/dc1/host/cluster2/Resources", + }, + }, + }, + remoteMachineSets: []machineapi.MachineSet{ + testRemoteMachineSet("test-infra-worker-0", "vcenter.example.com", "dc1", "shared-ds", "/dc1/host/cluster1/Resources", "", "/dc1/vm/test-infra-rhcos-us-east-1"), + testRemoteMachineSet("test-infra-worker-1", "vcenter.example.com", "dc1", "shared-ds", "/dc1/host/cluster2/Resources", "", "/dc1/vm/test-infra-rhcos-us-east-2"), + }, + infraID: "test-infra", + expectedTemplates: map[string]string{ + "us-east-1": "/dc1/vm/test-infra-rhcos-us-east-1", + "us-east-2": "/dc1/vm/test-infra-rhcos-us-east-2", + }, + }, + { + name: "host-group zonal: VMGroup disambiguates FDs", + failureDomains: []vsphere.FailureDomain{ + { + Name: "zone-a", + Server: "vcenter.example.com", + ZoneType: vsphere.HostGroupFailureDomain, + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "shared-ds", + ResourcePool: "/dc1/host/cluster1/Resources", + }, + }, + { + Name: "zone-b", + Server: "vcenter.example.com", + ZoneType: vsphere.HostGroupFailureDomain, + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "shared-ds", + ResourcePool: "/dc1/host/cluster1/Resources", + }, + }, + }, + remoteMachineSets: []machineapi.MachineSet{ + testRemoteMachineSet("test-infra-worker-0", "vcenter.example.com", "dc1", "shared-ds", "/dc1/host/cluster1/Resources", "test-infra-zone-a", "/dc1/vm/test-infra-rhcos-zone-a"), + testRemoteMachineSet("test-infra-worker-1", "vcenter.example.com", "dc1", "shared-ds", "/dc1/host/cluster1/Resources", "test-infra-zone-b", "/dc1/vm/test-infra-rhcos-zone-b"), + }, + infraID: "test-infra", + expectedTemplates: map[string]string{ + "zone-a": "/dc1/vm/test-infra-rhcos-zone-a", + "zone-b": "/dc1/vm/test-infra-rhcos-zone-b", + }, + }, + { + name: "no remote MachineSets: template stays empty", + failureDomains: []vsphere.FailureDomain{ + { + Name: "fd1", + Server: "vcenter.example.com", + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "datastore1", + ResourcePool: "/dc1/host/cluster1/Resources", + }, + }, + }, + remoteMachineSets: nil, + infraID: "test-infra", + expectedTemplates: map[string]string{"fd1": ""}, + }, + { + name: "ResourcePool normalization: trailing slash match", + failureDomains: []vsphere.FailureDomain{ + { + Name: "fd1", + Server: "vcenter.example.com", + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "datastore1", + ResourcePool: "/dc1/host/cluster1/Resources/", + }, + }, + }, + remoteMachineSets: []machineapi.MachineSet{ + testRemoteMachineSet("test-infra-worker-0", "vcenter.example.com", "dc1", "datastore1", "/dc1/host/cluster1/Resources", "", "/dc1/vm/test-infra-rhcos-fd1"), + }, + infraID: "test-infra", + expectedTemplates: map[string]string{ + "fd1": "/dc1/vm/test-infra-rhcos-fd1", + }, + }, + { + name: "MachinePool ResourcePool override does not affect template discovery", + failureDomains: []vsphere.FailureDomain{ + { + Name: "fd1", + Server: "vcenter.example.com", + Topology: vsphere.Topology{ + Datacenter: "dc1", + Datastore: "datastore1", + ResourcePool: "/dc1/host/cluster1/Resources", + }, + }, + }, + remoteMachineSets: []machineapi.MachineSet{ + testRemoteMachineSet("test-infra-worker-0", "vcenter.example.com", "dc1", "datastore1", "/dc1/host/cluster1/Resources", "", "/dc1/vm/test-infra-rhcos-fd1"), + }, + infraID: "test-infra", + expectedTemplates: map[string]string{ + "fd1": "/dc1/vm/test-infra-rhcos-fd1", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + templates := buildTemplateMap(test.failureDomains, test.remoteMachineSets, test.infraID, logger) + + for fdName, expected := range test.expectedTemplates { + if expected == "" { + assert.NotContains(t, templates, fdName, "expected no template for FD %s", fdName) + } else { + assert.Equal(t, expected, templates[fdName], "unexpected template for FD %s", fdName) + } + } + }) + } +} + +func testRemoteMachineSet(name, server, datacenter, datastore, resourcePool, vmGroup, template string) machineapi.MachineSet { + spec := &machineapi.VSphereMachineProviderSpec{ + TypeMeta: metav1.TypeMeta{ + Kind: "VSphereMachineProviderSpec", + APIVersion: machineapi.SchemeGroupVersion.String(), + }, + Template: template, + Workspace: &machineapi.Workspace{ + Server: server, + Datacenter: datacenter, + Datastore: datastore, + ResourcePool: resourcePool, + VMGroup: vmGroup, + }, + } + raw, _ := json.Marshal(spec) + return machineapi.MachineSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: machineapi.MachineSetSpec{ + Template: machineapi.MachineTemplateSpec{ + Spec: machineapi.MachineSpec{ + ProviderSpec: machineapi.ProviderSpec{ + Value: &runtime.RawExtension{Raw: raw}, + }, + }, + }, + }, + } +} + func validateVSphereMachineSets(t *testing.T, mSets []*machineapi.MachineSet, expectedMSReplicas map[string]int64) { assert.Equal(t, len(expectedMSReplicas), len(mSets), "different number of machine sets generated than expected") @@ -93,8 +346,6 @@ func validateVSphereMachineSets(t *testing.T, mSets []*machineapi.MachineSet, ex func testVSpherePool() *hivev1.MachinePool { p := testMachinePool() p.Spec.Platform = hivev1.MachinePoolPlatform{ - // Observation: when constructing this way, we have to use hive.MachinePool{installer.MachinePool{}} - // whereas when accessing it, we can (optionally) skip the intermediate (e.g. ...Platform.VSphere.MemoryMiB) VSphere: &hivev1vsphere.MachinePool{ MachinePool: vsphere.MachinePool{ MemoryMiB: 32 * 1024, @@ -151,53 +402,3 @@ func testVSphereClusterDeployment() *hivev1.ClusterDeployment { } return cd } - -func testVSphereMachineSpec(machineType string) machineapi.MachineSpec { - rawVSphereProviderSpec, err := vsphereutil.RawExtensionFromProviderSpec(testVSphereProviderSpec()) - if err != nil { - log.WithError(err).Fatal("error encoding VSphere machine provider spec") - } - return machineapi.MachineSpec{ - ObjectMeta: machineapi.ObjectMeta{ - Labels: map[string]string{ - "machine.openshift.io/cluster-api-cluster": testInfraID, - "machine.openshift.io/cluster-api-machine-role": machineType, - "machine.openshift.io/cluster-api-machine-type": machineType, - }, - }, - ProviderSpec: machineapi.ProviderSpec{ - Value: rawVSphereProviderSpec, - }, - Taints: []corev1.Taint{ - { - Key: "foo", - Value: "bar", - Effect: corev1.TaintEffectNoSchedule, - }, - }, - } -} - -func testVSphereMachine(name string, machineType string) *machineapi.Machine { - return &machineapi.Machine{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: machineAPINamespace, - Labels: map[string]string{ - "machine.openshift.io/cluster-api-cluster": testInfraID, - }, - }, - Spec: testVSphereMachineSpec(machineType), - } -} - -func testVSphereProviderSpec() *machineapi.VSphereMachineProviderSpec { - return &machineapi.VSphereMachineProviderSpec{ - TypeMeta: metav1.TypeMeta{ - Kind: "VSphereMachineProviderSpec", - APIVersion: machineapi.SchemeGroupVersion.String(), - }, - NumCPUs: 8, - DiskGiB: 120, - } -} diff --git a/pkg/validating-webhooks/hive/v1/clusterdeployment_validating_admission_hook.go b/pkg/validating-webhooks/hive/v1/clusterdeployment_validating_admission_hook.go index 7ca114ac039..0898b095d76 100644 --- a/pkg/validating-webhooks/hive/v1/clusterdeployment_validating_admission_hook.go +++ b/pkg/validating-webhooks/hive/v1/clusterdeployment_validating_admission_hook.go @@ -706,6 +706,23 @@ func (a *ClusterDeploymentValidatingAdmissionHook) validateUpdate(admissionSpec // We've stealthily allowed replacement of secret refs. This will at least make sure they're not empty. allErrs = append(allErrs, validateClusterPlatform(specPath, cd)...) } + + // Allow setting Topology.Template on failure domains where it was previously blank. + // Clusters installed before the template naming convention change have empty Template + // fields; customers or automation may need to set them explicitly. + if oldObject.Spec.Platform.VSphere.Infrastructure != nil && cd.Spec.Platform.VSphere.Infrastructure != nil { + oldFDs := oldObject.Spec.Platform.VSphere.Infrastructure.FailureDomains + newFDs := cd.Spec.Platform.VSphere.Infrastructure.FailureDomains + for i := range oldFDs { + if i >= len(newFDs) { + break + } + if oldFDs[i].Topology.Template == "" && newFDs[i].Topology.Template != "" { + // copy over the value to spoof the immutability checker + oldFDs[i].Topology.Template = newFDs[i].Topology.Template + } + } + } } hasChangedImmutableField, unsupportedDiff := hasChangedImmutableField(&oldObject.Spec, &cd.Spec) diff --git a/pkg/validating-webhooks/hive/v1/clusterdeployment_validating_admission_hook_test.go b/pkg/validating-webhooks/hive/v1/clusterdeployment_validating_admission_hook_test.go index 17c31e85b27..03c98cfecce 100644 --- a/pkg/validating-webhooks/hive/v1/clusterdeployment_validating_admission_hook_test.go +++ b/pkg/validating-webhooks/hive/v1/clusterdeployment_validating_admission_hook_test.go @@ -1953,6 +1953,165 @@ func TestClusterDeploymentValidate(t *testing.T) { newObject: validVSphereClusterDeployment(), operation: admissionv1.Update, }, + { + name: "vsphere Topology.Template empty to non-empty is allowed", + oldObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "" + return cd + }(), + newObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/my-template" + return cd + }(), + operation: admissionv1.Update, + expectedAllowed: true, + }, + { + name: "vsphere Topology.Template change when already set is blocked", + oldObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/original-template" + return cd + }(), + newObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/different-template" + return cd + }(), + operation: admissionv1.Update, + }, + { + name: "vsphere Topology.Template unchanged non-empty is allowed", + oldObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/same-template" + return cd + }(), + newObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/same-template" + return cd + }(), + operation: admissionv1.Update, + expectedAllowed: true, + }, + { + name: "vsphere Topology.Template both empty is allowed", + oldObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "" + return cd + }(), + newObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "" + return cd + }(), + operation: admissionv1.Update, + expectedAllowed: true, + }, + { + name: "vsphere Topology.Template clearing non-empty is blocked", + oldObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/existing-template" + return cd + }(), + newObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "" + return cd + }(), + operation: admissionv1.Update, + }, + { + name: "vsphere Topology.Template multi-FD mixed: blank to set allowed while set unchanged", + oldObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains = append( + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains, + installervsphere.FailureDomain{ + Server: "somevcenter.com", + Topology: installervsphere.Topology{ + Datacenter: "dc1", + Datastore: "vmse-test-2", + Folder: "/dc1/vm/test-2", + ComputeCluster: "test-2", + Networks: []string{"Network"}, + }, + }, + ) + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/already-set" + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[1].Topology.Template = "" + return cd + }(), + newObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains = append( + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains, + installervsphere.FailureDomain{ + Server: "somevcenter.com", + Topology: installervsphere.Topology{ + Datacenter: "dc1", + Datastore: "vmse-test-2", + Folder: "/dc1/vm/test-2", + ComputeCluster: "test-2", + Networks: []string{"Network"}, + }, + }, + ) + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/already-set" + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[1].Topology.Template = "/dc1/vm/backfilled" + return cd + }(), + operation: admissionv1.Update, + expectedAllowed: true, + }, + { + name: "vsphere Topology.Template multi-FD all blank to all set is allowed", + oldObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains = append( + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains, + installervsphere.FailureDomain{ + Server: "somevcenter.com", + Topology: installervsphere.Topology{ + Datacenter: "dc1", + Datastore: "vmse-test-2", + Folder: "/dc1/vm/test-2", + ComputeCluster: "test-2", + Networks: []string{"Network"}, + }, + }, + ) + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "" + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[1].Topology.Template = "" + return cd + }(), + newObject: func() *hivev1.ClusterDeployment { + cd := validVSphereClusterDeployment() + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains = append( + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains, + installervsphere.FailureDomain{ + Server: "somevcenter.com", + Topology: installervsphere.Topology{ + Datacenter: "dc1", + Datastore: "vmse-test-2", + Folder: "/dc1/vm/test-2", + ComputeCluster: "test-2", + Networks: []string{"Network"}, + }, + }, + ) + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[0].Topology.Template = "/dc1/vm/template-1" + cd.Spec.Platform.VSphere.Infrastructure.FailureDomains[1].Topology.Template = "/dc1/vm/template-2" + return cd + }(), + operation: admissionv1.Update, + expectedAllowed: true, + }, } for _, tc := range cases { diff --git a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/actuator.go b/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/actuator.go deleted file mode 100644 index 56def7a6feb..00000000000 --- a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/actuator.go +++ /dev/null @@ -1,202 +0,0 @@ -package vsphere - -// This is a thin layer to implement the machine actuator interface with cloud provider details. -// The lifetime of scope and reconciler is a machine actuator operation. -import ( - "context" - "fmt" - "time" - - "k8s.io/component-base/featuregate" - - machinev1 "github.com/openshift/api/machine/v1beta1" - machinecontroller "github.com/openshift/machine-api-operator/pkg/controller/machine" - corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/record" - "k8s.io/klog/v2" - runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - scopeFailFmt = "%s: failed to create scope for machine: %v" - reconcilerFailFmt = "%s: reconciler failed to %s machine: %w" - createEventAction = "Create" - updateEventAction = "Update" - deleteEventAction = "Delete" - noEventAction = "" - requeueAfterSeconds = 20 -) - -// Actuator is responsible for performing machine reconciliation. -type Actuator struct { - client runtimeclient.Client - apiReader runtimeclient.Reader - eventRecorder record.EventRecorder - TaskIDCache map[string]string - FeatureGates featuregate.MutableFeatureGate - openshiftConfigNamespace string -} - -// ActuatorParams holds parameter information for Actuator. -type ActuatorParams struct { - Client runtimeclient.Client - APIReader runtimeclient.Reader - EventRecorder record.EventRecorder - TaskIDCache map[string]string - FeatureGates featuregate.MutableFeatureGate - OpenshiftConfigNamespace string -} - -// NewActuator returns an actuator. -func NewActuator(params ActuatorParams) *Actuator { - return &Actuator{ - client: params.Client, - apiReader: params.APIReader, - eventRecorder: params.EventRecorder, - TaskIDCache: params.TaskIDCache, - FeatureGates: params.FeatureGates, - openshiftConfigNamespace: params.OpenshiftConfigNamespace, - } -} - -// Set corresponding event based on error. It also returns the original error -// for convenience, so callers can do "return handleMachineError(...)". -func (a *Actuator) handleMachineError(machine *machinev1.Machine, err error, eventAction string) error { - klog.Errorf("%q error: %v", machine.GetName(), err) - if eventAction != noEventAction { - a.eventRecorder.Eventf(machine, corev1.EventTypeWarning, "Failed"+eventAction, "%v", err) - } - return err -} - -// Create creates a machine and is invoked by the machine controller. -func (a *Actuator) Create(ctx context.Context, machine *machinev1.Machine) error { - klog.Infof("%s: actuator creating machine", machine.GetName()) - - scope, err := newMachineScope(machineScopeParams{ - Context: ctx, - client: a.client, - machine: machine, - apiReader: a.apiReader, - featureGates: a.FeatureGates, - openshiftConfigNameSpace: a.openshiftConfigNamespace, - }) - if err != nil { - fmtErr := fmt.Errorf(scopeFailFmt, machine.GetName(), err) - return a.handleMachineError(machine, fmtErr, createEventAction) - } - - // Ensure we're not reconciling a stale machine by checking our task-id. - // This is a workaround for a cache race condition. - if val, ok := a.TaskIDCache[machine.Name]; ok { - if val != scope.providerStatus.TaskRef { - klog.Errorf("%s: machine object missing expected provider task ID, requeue", machine.GetName()) - return &machinecontroller.RequeueAfterError{RequeueAfter: requeueAfterSeconds * time.Second} - } - } - - var retErr error - err = newReconciler(scope).create() - // save the taskRef in our cache in case of any error with patch. - if scope.providerStatus.TaskRef != "" { - a.TaskIDCache[machine.Name] = scope.providerStatus.TaskRef - } - if err != nil { - fmtErr := fmt.Errorf(reconcilerFailFmt, machine.GetName(), createEventAction, err) - retErr = a.handleMachineError(machine, fmtErr, createEventAction) - } else { - a.eventRecorder.Eventf(machine, corev1.EventTypeNormal, createEventAction, "Created Machine %v", machine.GetName()) - } - - if err := scope.PatchMachine(); err != nil { - return err - } - - return retErr -} - -func (a *Actuator) Exists(ctx context.Context, machine *machinev1.Machine) (bool, error) { - klog.Infof("%s: actuator checking if machine exists", machine.GetName()) - scope, err := newMachineScope(machineScopeParams{ - Context: ctx, - client: a.client, - machine: machine, - apiReader: a.apiReader, - featureGates: a.FeatureGates, - openshiftConfigNameSpace: a.openshiftConfigNamespace, - }) - if err != nil { - return false, fmt.Errorf(scopeFailFmt, machine.GetName(), err) - } - return newReconciler(scope).exists() -} - -func (a *Actuator) Update(ctx context.Context, machine *machinev1.Machine) error { - klog.Infof("%s: actuator updating machine", machine.GetName()) - // Cleanup TaskIDCache so we don't continually grow - delete(a.TaskIDCache, machine.Name) - - scope, err := newMachineScope(machineScopeParams{ - Context: ctx, - client: a.client, - machine: machine, - apiReader: a.apiReader, - featureGates: a.FeatureGates, - openshiftConfigNameSpace: a.openshiftConfigNamespace, - }) - if err != nil { - fmtErr := fmt.Errorf(scopeFailFmt, machine.GetName(), err) - return a.handleMachineError(machine, fmtErr, updateEventAction) - } - if err := newReconciler(scope).update(); err != nil { - // Update machine and machine status in case it was modified - if err := scope.PatchMachine(); err != nil { - return err - } - fmtErr := fmt.Errorf(reconcilerFailFmt, machine.GetName(), updateEventAction, err) - return a.handleMachineError(machine, fmtErr, updateEventAction) - } - previousResourceVersion := scope.machine.ResourceVersion - - if err := scope.PatchMachine(); err != nil { - return err - } - - currentResourceVersion := scope.machine.ResourceVersion - - // Create event only if machine object was modified - if previousResourceVersion != currentResourceVersion { - a.eventRecorder.Eventf(machine, corev1.EventTypeNormal, updateEventAction, "Updated Machine %v", machine.GetName()) - } - - return nil -} - -func (a *Actuator) Delete(ctx context.Context, machine *machinev1.Machine) error { - klog.Infof("%s: actuator deleting machine", machine.GetName()) - // Cleanup TaskIDCache so we don't continually grow - // Cleanup here as well in case Update() was never successfully called. - delete(a.TaskIDCache, machine.Name) - - scope, err := newMachineScope(machineScopeParams{ - Context: ctx, - client: a.client, - machine: machine, - apiReader: a.apiReader, - featureGates: a.FeatureGates, - openshiftConfigNameSpace: a.openshiftConfigNamespace, - }) - if err != nil { - fmtErr := fmt.Errorf(scopeFailFmt, machine.GetName(), err) - return a.handleMachineError(machine, fmtErr, deleteEventAction) - } - if err := newReconciler(scope).delete(); err != nil { - if err := scope.PatchMachine(); err != nil { - return err - } - fmtErr := fmt.Errorf(reconcilerFailFmt, machine.GetName(), deleteEventAction, err) - return a.handleMachineError(machine, fmtErr, deleteEventAction) - } - a.eventRecorder.Eventf(machine, corev1.EventTypeNormal, deleteEventAction, "Deleted machine %v", machine.GetName()) - return scope.PatchMachine() -} diff --git a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/machine_scope.go b/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/machine_scope.go deleted file mode 100644 index 141a19fa20a..00000000000 --- a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/machine_scope.go +++ /dev/null @@ -1,304 +0,0 @@ -package vsphere - -import ( - "context" - "errors" - "fmt" - - "k8s.io/component-base/featuregate" - - machinev1 "github.com/openshift/api/machine/v1beta1" - machinecontroller "github.com/openshift/machine-api-operator/pkg/controller/machine" - "github.com/openshift/machine-api-operator/pkg/controller/vsphere/session" - apicorev1 "k8s.io/api/core/v1" - apimachineryerrors "k8s.io/apimachinery/pkg/api/errors" - apimachineryutilerrors "k8s.io/apimachinery/pkg/util/errors" - vsphere "k8s.io/cloud-provider-vsphere/pkg/common/config" - "k8s.io/klog/v2" - runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - userDataSecretKey = "userData" -) - -// machineScopeParams defines the input parameters used to create a new MachineScope. -type machineScopeParams struct { - context.Context - client runtimeclient.Client - apiReader runtimeclient.Reader - machine *machinev1.Machine - featureGates featuregate.MutableFeatureGate - openshiftConfigNameSpace string -} - -// machineScope defines a scope defined around a machine and its cluster. -type machineScope struct { - context.Context - // vsphere session - session *session.Session - // api server controller runtime client - client runtimeclient.Client - // client reader that bypasses the manager's cache - apiReader runtimeclient.Reader - // vSphere cloud-provider config - vSphereConfig *vsphere.Config - // machine resource - machine *machinev1.Machine - providerSpec *machinev1.VSphereMachineProviderSpec - providerStatus *machinev1.VSphereMachineProviderStatus - machineToBePatched runtimeclient.Patch - featureGates featuregate.MutableFeatureGate -} - -// newMachineScope creates a new machineScope from the supplied parameters. -// This is meant to be called for each machine actuator operation. -func newMachineScope(params machineScopeParams) (*machineScope, error) { - if params.Context == nil { - return nil, fmt.Errorf("%v: machine scope require a context", params.machine.GetName()) - } - - vSphereConfig, err := getVSphereConfig(params.apiReader, params.openshiftConfigNameSpace) - if err != nil { - klog.Errorf("Failed to fetch vSphere config: %v", err) - } - - providerSpec, err := ProviderSpecFromRawExtension(params.machine.Spec.ProviderSpec.Value) - if err != nil { - return nil, machinecontroller.InvalidMachineConfiguration("failed to get machine config: %v", err) - } - - providerStatus, err := ProviderStatusFromRawExtension(params.machine.Status.ProviderStatus) - if err != nil { - return nil, machinecontroller.InvalidMachineConfiguration("failed to get machine provider status: %v", err.Error()) - } - - user, password, err := getCredentialsSecret(params.client, params.machine.GetNamespace(), *providerSpec) - if err != nil { - return nil, fmt.Errorf("%v: error getting credentials: %w", params.machine.GetName(), err) - } - if providerSpec.Workspace == nil { - return nil, fmt.Errorf("%v: no workspace provided", params.machine.GetName()) - } - - server := fmt.Sprintf("%s:%s", providerSpec.Workspace.Server, getVCenterPortFromConfig(vSphereConfig, providerSpec.Workspace.Server)) - authSession, err := session.GetOrCreate(params.Context, - server, providerSpec.Workspace.Datacenter, - user, password, getVCenterInsecureFlagFromConfig(vSphereConfig, providerSpec.Workspace.Server)) - if err != nil { - return nil, fmt.Errorf("failed to create vSphere session: %w", err) - } - - return &machineScope{ - Context: params.Context, - client: params.client, - apiReader: params.apiReader, - session: authSession, - machine: params.machine, - providerSpec: providerSpec, - providerStatus: providerStatus, - vSphereConfig: vSphereConfig, - featureGates: params.featureGates, - machineToBePatched: runtimeclient.MergeFrom(params.machine.DeepCopy()), - }, nil -} - -// Patch patches the machine spec and machine status after reconciling. -func (s *machineScope) PatchMachine() error { - klog.V(3).Infof("%v: patching machine", s.machine.GetName()) - - providerStatus, err := RawExtensionFromProviderStatus(s.providerStatus) - if err != nil { - return machinecontroller.InvalidMachineConfiguration("failed to get machine provider status: %v", err.Error()) - } - s.machine.Status.ProviderStatus = providerStatus - - statusCopy := *s.machine.Status.DeepCopy() - - // patch machine - if err := s.client.Patch(context.Background(), s.machine, s.machineToBePatched); err != nil { - klog.Errorf("Failed to patch machine %q: %v", s.machine.GetName(), err) - return err - } - - s.machine.Status = statusCopy - - // patch status - if err := s.client.Status().Patch(context.Background(), s.machine, s.machineToBePatched); err != nil { - klog.Errorf("Failed to patch machine status %q: %v", s.machine.GetName(), err) - return err - } - - return nil -} - -func (s *machineScope) GetSession() *session.Session { - return s.session -} - -func (s *machineScope) isNodeLinked() bool { - return s.machine.Status.NodeRef != nil && s.machine.Status.NodeRef.Name != "" -} - -func (s *machineScope) getNode() (*apicorev1.Node, error) { - var node apicorev1.Node - if !s.isNodeLinked() { - return nil, fmt.Errorf("NodeRef empty, unable to get related Node") - } - nodeName := s.machine.Status.NodeRef.Name - objectKey := runtimeclient.ObjectKey{ - Name: nodeName, - } - if err := s.apiReader.Get(s.Context, objectKey, &node); err != nil { - if apimachineryerrors.IsNotFound(err) { - klog.V(2).Infof("Node %q not found", nodeName) - return nil, err - } - klog.Errorf("Failed to get node %q: %v", nodeName, err) - return nil, err - } - - return &node, nil -} - -func (s *machineScope) checkNodeReachable() (bool, error) { - node, err := s.getNode() - if err != nil { - // do not return error if node object not found, treat it as unreachable - if apimachineryerrors.IsNotFound(err) { - return false, nil - } - return false, err - } - return nodeReachable(node), nil -} - -// deleteUnevictedPods checks respective node for reachability, -// if the node is not reachable it tries to remove pods in the 'Terminating' state. -// Returns the number of deleted pods and errors if there were any. -// Returns error, if the node is operational. -func (s *machineScope) deleteUnevictedPods() (int, error) { - node, err := s.getNode() - if err != nil { - // do not return error if node object not found, treat it as unreachable - if apimachineryerrors.IsNotFound(err) { - return 0, nil - } - return 0, err - } - if nodeReachable(node) { - return 0, fmt.Errorf("node is in operational state, won't proceed with pods deletion") - } - - terminatingPods, err := getPodList(s.Context, s.apiReader, node, []podPredicate{isTerminating}) - if err != nil { - return 0, err - } - - gracePeriodSeconds := int64(0) - deleteOptions := &runtimeclient.DeleteOptions{ - GracePeriodSeconds: &gracePeriodSeconds, - } - deletedPods := 0 - var deleteErrList []error - for _, pod := range terminatingPods.Items { - err := s.client.Delete(s.Context, &pod, deleteOptions) - if err != nil { - deleteErrList = append(deleteErrList, err) - } else { - deletedPods += 1 - } - } - if len(deleteErrList) > 0 { - return deletedPods, apimachineryutilerrors.NewAggregate(deleteErrList) - } - return deletedPods, nil -} - -func nodeReachable(node *apicorev1.Node) bool { - for _, condition := range node.Status.Conditions { - if condition.Type == apicorev1.NodeReady && condition.Status == apicorev1.ConditionUnknown { - return false - } - } - return true -} - -// GetUserData fetches the user-data from the secret referenced in the Machine's -// provider spec, if one is set. -func (s *machineScope) GetUserData() ([]byte, error) { - if s.providerSpec == nil || s.providerSpec.UserDataSecret == nil { - return nil, machinecontroller.InvalidMachineConfiguration("user data secret is missing in provider spec") - } - - userDataSecret := &apicorev1.Secret{} - - objKey := runtimeclient.ObjectKey{ - Namespace: s.machine.Namespace, - Name: s.providerSpec.UserDataSecret.Name, - } - - if err := s.client.Get(context.Background(), objKey, userDataSecret); err != nil { - return nil, err - } - - userData, exists := userDataSecret.Data[userDataSecretKey] - if !exists { - return nil, fmt.Errorf("secret %s missing %s key", objKey, userDataSecretKey) - } - - return userData, nil -} - -// getCredentialsSecret returns the username and password from the VSphere credentials secret. -// The secret is expected to be in the format documented here: -// https://vmware.github.io/vsphere-storage-for-kubernetes/documentation/k8s-secret.html -// -// Assuming the vcenter is our dev server vcsa.vmware.devcluster.openshift.com, -// the secret would be in this format: -// -// apiVersion: v1 -// kind: Secret -// metadata: -// name: vsphere -// namespace: openshift-machine-api -// type: Opaque -// data: -// vcsa.vmware.devcluster.openshift.com.username: base64 string -// vcsa.vmware.devcluster.openshift.com.password: base64 string -func getCredentialsSecret(client runtimeclient.Client, namespace string, spec machinev1.VSphereMachineProviderSpec) (string, string, error) { - if spec.CredentialsSecret == nil { - return "", "", nil - } - - var credentialsSecret apicorev1.Secret - if err := client.Get(context.Background(), - runtimeclient.ObjectKey{Namespace: namespace, Name: spec.CredentialsSecret.Name}, - &credentialsSecret); err != nil { - - if apimachineryerrors.IsNotFound(err) { - return "", "", machinecontroller.InvalidMachineConfiguration("credentials secret %v/%v not found: %v", namespace, spec.CredentialsSecret.Name, err.Error()) - } - return "", "", fmt.Errorf("error getting credentials secret %v/%v: %v", namespace, spec.CredentialsSecret.Name, err) - } - - // TODO: add provider spec validation logic and move this check there - if spec.Workspace == nil { - return "", "", errors.New("no workspace") - } - - credentialsSecretUser := fmt.Sprintf("%s.username", spec.Workspace.Server) - credentialsSecretPassword := fmt.Sprintf("%s.password", spec.Workspace.Server) - - user, exists := credentialsSecret.Data[credentialsSecretUser] - if !exists { - return "", "", machinecontroller.InvalidMachineConfiguration("secret %v/%v does not have %q field set", namespace, spec.CredentialsSecret.Name, credentialsSecretUser) - } - - password, exists := credentialsSecret.Data[credentialsSecretPassword] - if !exists { - return "", "", machinecontroller.InvalidMachineConfiguration("secret %v/%v does not have %q field set", namespace, spec.CredentialsSecret.Name, credentialsSecretPassword) - } - - return string(user), string(password), nil -} diff --git a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/reconciler.go b/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/reconciler.go deleted file mode 100644 index 74240b3b73d..00000000000 --- a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/reconciler.go +++ /dev/null @@ -1,1864 +0,0 @@ -package vsphere - -import ( - "context" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "net" - "net/netip" - "regexp" - "slices" - "strconv" - "strings" - - "github.com/vmware/govmomi/task" - - "github.com/openshift/machine-api-operator/pkg/util/ipam" - - "github.com/google/uuid" - - "github.com/vmware/govmomi/find" - "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/property" - "github.com/vmware/govmomi/vim25" - "github.com/vmware/govmomi/vim25/mo" - "github.com/vmware/govmomi/vim25/types" - - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - apimachinerytypes "k8s.io/apimachinery/pkg/types" - apimachineryutilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/component-base/featuregate" - "k8s.io/klog/v2" - "k8s.io/utils/ptr" - - apifeatures "github.com/openshift/api/features" - machinev1 "github.com/openshift/api/machine/v1beta1" - - machinecontroller "github.com/openshift/machine-api-operator/pkg/controller/machine" - "github.com/openshift/machine-api-operator/pkg/controller/vsphere/session" - "github.com/openshift/machine-api-operator/pkg/metrics" -) - -const ( - fullCloneDiskMoveType = string(types.VirtualMachineRelocateDiskMoveOptionsMoveAllDiskBackingsAndConsolidate) - linkCloneDiskMoveType = string(types.VirtualMachineRelocateDiskMoveOptionsCreateNewChildDiskBacking) - ethCardType = "vmxnet3" - providerIDPrefix = "vsphere://" - regionKey = "region" - zoneKey = "zone" - minimumHWVersion = 15 - // maxUnitNumber constant is used to define the maximum number of devices that can be assigned to a virtual machine's controller. - // Not all controllers support up to 30, but the maximum is 30. - // xref: https://docs.vmware.com/en/VMware-vSphere/8.0/vsphere-vm-administration/GUID-5872D173-A076-42FE-8D0B-9DB0EB0E7362.html#:~:text=If%20you%20add%20a%20hard,values%20from%200%20to%2014. - maxUnitNumber = 30 -) - -// These are the guestinfo variables used by Ignition. -// https://access.redhat.com/documentation/en-us/openshift_container_platform/4.1/html/installing/installing-on-vsphere -const ( - GuestInfoIgnitionData = "guestinfo.ignition.config.data" - GuestInfoIgnitionEncoding = "guestinfo.ignition.config.data.encoding" - GuestInfoHostname = "guestinfo.hostname" - GuestInfoNetworkKargs = "guestinfo.afterburn.initrd.network-kargs" - StealClock = "stealclock.enable" -) - -// vSphere tasks description IDs, for determinate task types (clone, delete, etc) -const ( - cloneVmTaskDescriptionId = "VirtualMachine.clone" - destroyVmTaskDescriptionId = "VirtualMachine.destroy" - powerOffVmTaskDescriptionId = "VirtualMachine.powerOff" -) - -// Reconciler runs the logic to reconciles a machine resource towards its desired state -type Reconciler struct { - *machineScope -} - -func newReconciler(scope *machineScope) *Reconciler { - return &Reconciler{ - machineScope: scope, - } -} - -// create creates machine if it does not exists. -func (r *Reconciler) create() error { - if err := validateMachine(*r.machine); err != nil { - return fmt.Errorf("%v: failed validating machine provider spec: %w", r.machine.GetName(), err) - } - - if r.providerSpec.Workspace.VMGroup != "" && !r.featureGates.Enabled(featuregate.Feature(apifeatures.FeatureGateVSphereHostVMGroupZonal)) { - return fmt.Errorf("%v: vmGroup is only available with the VSphereHostVMGroupZonal feature gate", r.machine.GetName()) - } - - if ipam.HasStaticIPConfiguration(r.providerSpec) { - outstandingClaims, err := ipam.HasOutstandingIPAddressClaims( - r.Context, - r.client, - r.machine, - r.providerSpec.Network.Devices, - ) - if err != nil { - return err - } - condition := metav1.Condition{ - Type: string(machinev1.IPAddressClaimedCondition), - Reason: machinev1.IPAddressClaimedReason, - Message: "All IP address claims are bound", - Status: metav1.ConditionTrue, - } - - if outstandingClaims > 0 { - condition.Message = fmt.Sprintf("Waiting on %d IP address claims to be bound", outstandingClaims) - condition.Reason = machinev1.WaitingForIPAddressReason - condition.Status = metav1.ConditionFalse - klog.Infof("Waiting for IPAddressClaims associated with machine %s to be bound", r.machine.Name) - } - if err := setProviderStatus("", condition, r.machineScope, nil); err != nil { - return fmt.Errorf("could not set provider status: %w", err) - } - } - - // We only clone the VM template if we have no taskRef. - if r.providerStatus.TaskRef == "" { - klog.V(4).Infof("%v: ProviderStatus does not have TaskRef", r.machine.GetName()) - if !r.machineScope.session.IsVC() { - return fmt.Errorf("%v: not connected to a vCenter", r.machine.GetName()) - } - - // Attempt to power on instance in situation where we alredy cloned the instance and lost taskRef. - klog.V(4).Infof("%v: InstanceState is: %q", r.machine.GetName(), ptr.Deref(r.machineScope.providerStatus.InstanceState, "")) - if types.VirtualMachinePowerState(ptr.Deref(r.machineScope.providerStatus.InstanceState, "")) == types.VirtualMachinePowerStatePoweredOff { - klog.Infof("Powering on cloned machine without taskID: %v", r.machine.Name) - - task, err := powerOn(r.machineScope) - if err != nil { - metrics.RegisterFailedInstanceCreate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "PowerOn task finished with error", - }) - - conditionFailed := conditionFailed() - conditionFailed.Message = err.Error() - statusError := setProviderStatus(task, conditionFailed, r.machineScope, nil) - if statusError != nil { - return fmt.Errorf("failed to set provider status: %w", err) - } - - return fmt.Errorf("%v: failed to power on machine: %w", r.machine.GetName(), err) - } - - return setProviderStatus(task, conditionSuccess(), r.machineScope, nil) - } - - klog.Infof("%v: cloning", r.machine.GetName()) - task, err := clone(r.machineScope) - if err != nil { - metrics.RegisterFailedInstanceCreate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "Clone task finished with error", - }) - conditionFailed := conditionFailed() - conditionFailed.Message = err.Error() - statusError := setProviderStatus(task, conditionFailed, r.machineScope, nil) - if statusError != nil { - return fmt.Errorf("failed to set provider status: %w", err) - } - return err - } - return setProviderStatus(task, conditionSuccess(), r.machineScope, nil) - } - - moTask, err := r.session.GetTask(r.Context, r.providerStatus.TaskRef) - if err != nil { - metrics.RegisterFailedInstanceCreate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "GetTask finished with error", - }) - return err - } - - if moTask == nil { - // Possible eventual consistency problem from vsphere - // TODO: change error message here to indicate this might be expected. - return fmt.Errorf("unexpected moTask nil") - } - - if taskIsFinished, err := taskIsFinished(moTask); err != nil { - if taskIsFinished { - metrics.RegisterFailedInstanceCreate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "Task finished with error", - }) - conditionFailed := conditionFailed() - conditionFailed.Message = err.Error() - statusError := setProviderStatus(moTask.Reference().Value, conditionFailed, r.machineScope, nil) - if statusError != nil { - return fmt.Errorf("failed to set provider status: %w", statusError) - } - return machinecontroller.CreateMachine("%s", err.Error()) - } else { - return fmt.Errorf("failed to check task status: %w", err) - } - } else { - if taskIsFinished { - klog.V(4).Infof("%v task %v has completed", moTask.Info.DescriptionId, moTask.Reference().Value) - } else { - return fmt.Errorf("%v task %v has not finished", moTask.Info.DescriptionId, moTask.Reference().Value) - } - } - - // if clone task finished successfully, power on the vm - // The simulator task.Info.DescriptionId is different (VirtualMachine.cloneVM) - if strings.Contains(moTask.Info.DescriptionId, cloneVmTaskDescriptionId) { - if r.machineScope.providerSpec.Workspace.VMGroup != "" { - klog.Infof("Adding on cloned machine: %s to vm group: %s", r.machine.Name, r.machineScope.providerSpec.Workspace.VMGroup) - - if err := modifyVMGroup(r.machineScope, false); err != nil { - var taskError task.Error - if errors.As(err, &taskError) { - return fmt.Errorf("could not update VM Group membership: %w", taskError) - } - - return fmt.Errorf("could not update VM Group membership: %w", err) - } - } - - klog.Infof("Powering on cloned machine: %v", r.machine.Name) - task, err := powerOn(r.machineScope) - if err != nil { - metrics.RegisterFailedInstanceCreate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "PowerOn task finished with error", - }) - conditionFailed := conditionFailed() - conditionFailed.Message = err.Error() - statusError := setProviderStatus(task, conditionFailed, r.machineScope, nil) - if statusError != nil { - return fmt.Errorf("failed to set provider status: %w", err) - } - return err - } - return setProviderStatus(task, conditionSuccess(), r.machineScope, nil) - } - - // If taskIsFinished then next reconcile should result in update. - return nil -} - -// update finds a vm and reconciles the machine resource status against it. -func (r *Reconciler) update() error { - if err := validateMachine(*r.machine); err != nil { - return fmt.Errorf("%v: failed validating machine provider spec: %w", r.machine.GetName(), err) - } - - if r.providerStatus.TaskRef != "" { - moTask, err := r.session.GetTask(r.Context, r.providerStatus.TaskRef) - if err != nil { - if !isRetrieveMONotFound(r.providerStatus.TaskRef, err) { - metrics.RegisterFailedInstanceUpdate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "GetTask finished with error", - }) - return err - } - } - if moTask != nil { - if taskIsFinished, err := taskIsFinished(moTask); err != nil { - metrics.RegisterFailedInstanceUpdate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "Task finished with error", - }) - return fmt.Errorf("%v task %v finished with error: %w", moTask.Info.DescriptionId, moTask.Reference().Value, err) - } else if !taskIsFinished { - return fmt.Errorf("%v task %v has not finished", moTask.Info.DescriptionId, moTask.Reference().Value) - } - } - } - - vmRef, err := findVM(r.machineScope) - if err != nil { - metrics.RegisterFailedInstanceUpdate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "FindVM finished with error", - }) - if !isNotFound(err) { - return err - } - return fmt.Errorf("vm not found on update: %w", err) - } - - vm := &virtualMachine{ - Context: r.machineScope.Context, - Obj: object.NewVirtualMachine(r.machineScope.session.Client.Client, vmRef), - Ref: vmRef, - } - - if err := vm.reconcileTags(r.Context, r.session, r.machine, r.providerSpec); err != nil { - metrics.RegisterFailedInstanceUpdate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "ReconcileTags finished with error", - }) - return fmt.Errorf("failed to reconcile tags: %w", err) - } - - if err := r.reconcileMachineWithCloudState(vm, r.providerStatus.TaskRef); err != nil { - metrics.RegisterFailedInstanceUpdate(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "ReconcileWithCloudState finished with error", - }) - return err - } - - return nil -} - -// exists returns true if machine exists. -func (r *Reconciler) exists() (bool, error) { - if err := validateMachine(*r.machine); err != nil { - return false, fmt.Errorf("%v: failed validating machine provider spec: %w", r.machine.GetName(), err) - } - - vmRef, err := findVM(r.machineScope) - if err != nil { - if !isNotFound(err) { - return false, err - } - klog.Infof("%v: does not exist", r.machine.GetName()) - return false, nil - } - - // Check if machine was powered on after clone. - // If it is powered off and in "Provisioning" phase, treat machine as non-existed yet and proceed with creation procedure. - powerState := types.VirtualMachinePowerState(ptr.Deref(r.machineScope.providerStatus.InstanceState, "")) - if powerState == "" || ptr.Deref(r.machine.Status.Phase, "") == machinev1.PhaseProvisioning { - vm := &virtualMachine{ - Context: r.machineScope.Context, - Obj: object.NewVirtualMachine(r.machineScope.session.Client.Client, vmRef), - Ref: vmRef, - } - powerState, err = vm.getPowerState() - if err != nil { - return false, fmt.Errorf("%v: failed checking machine's power state: %w", r.machine.GetName(), err) - } - } - - if ptr.Deref(r.machine.Status.Phase, "") == machinev1.PhaseProvisioning && powerState == types.VirtualMachinePowerStatePoweredOff { - klog.Infof("%v: already exists, but was not powered on after clone", r.machine.GetName()) - r.machineScope.providerStatus.InstanceState = ptr.To(string(powerState)) - if err := r.machineScope.PatchMachine(); err != nil { - return false, fmt.Errorf("%v: failed to patch machine: %w", r.machine.GetName(), err) - } - return false, nil - } - - klog.Infof("%v: already exists", r.machine.GetName()) - return true, nil -} - -func (r *Reconciler) delete() error { - if r.providerStatus.TaskRef != "" { - // TODO: We need to use a separate status field for the create and the - // delete taskref. - moTask, err := r.session.GetTask(r.Context, r.providerStatus.TaskRef) - if err != nil { - if !isRetrieveMONotFound(r.providerStatus.TaskRef, err) { - return err - } - } - if moTask != nil { - if taskIsFinished, err := taskIsFinished(moTask); err != nil { - // Check if latest task is not a task for vm cloning - if taskIsFinished && moTask.Info.DescriptionId != cloneVmTaskDescriptionId { - metrics.RegisterFailedInstanceDelete(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "Task finished with error", - }) - klog.Errorf("Delete task finished with error: %v", err) - return fmt.Errorf("%v task %v finished with error: %w", moTask.Info.DescriptionId, moTask.Reference().Value, err) - } else { - klog.Warningf( - "TaskRef points to clone task which finished with error: %v. Proceeding with machine deletion", err, - ) - } - } else if !taskIsFinished { - return fmt.Errorf("%v task %v has not finished", moTask.Info.DescriptionId, moTask.Reference().Value) - } - } - } - - vmRef, err := findVM(r.machineScope) - if err != nil { - if !isNotFound(err) { - metrics.RegisterFailedInstanceDelete(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "FindVM finished with error", - }) - return err - } - klog.Infof("%v: vm does not exist", r.machine.GetName()) - - // remove any finalizers for IPAddressClaims which may be associated with the machine - err = ipam.RemoveFinalizersForIPAddressClaims(r.Context, r.client, *r.machine) - if err != nil { - return fmt.Errorf("unable to remove finalizer for IP address claims: %w", err) - } - - return nil - } - - vm := &virtualMachine{ - Context: r.Context, - Obj: object.NewVirtualMachine(r.machineScope.session.Client.Client, vmRef), - Ref: vmRef, - } - - powerState, err := vm.getPowerState() - if err != nil { - return fmt.Errorf("can not determine %v vm power state: %w", r.machine.GetName(), err) - } - if powerState != types.VirtualMachinePowerStatePoweredOff { - powerOffTaskRef, err := vm.powerOffVM() - if err != nil { - return fmt.Errorf("%v: failed to power off vm: %w", r.machine.GetName(), err) - } - if err := setProviderStatus(powerOffTaskRef, conditionSuccess(), r.machineScope, vm); err != nil { - return fmt.Errorf("failed to set provider status: %w", err) - } - return fmt.Errorf("powering off vm is in progress, requeuing") - } - - // At this point node should be drained and vm powered off already. - // We need to check attached disks and ensure that all disks potentially related to PVs were detached - // to prevent possible data loss. - // Destroying a VM with attached disks might lead to data loss in case pvs are handled by the intree storage driver. - - _, drainSkipped := r.machine.ObjectMeta.Annotations[machinecontroller.ExcludeNodeDrainingAnnotation] - - // If node linked to the machine, and node was drained checking node status first - if r.machineScope.isNodeLinked() && !drainSkipped { - // After node draining, make sure volumes are detached before deleting the Node. - attached, err := r.nodeHasVolumesAttached(r.Context, r.machine.Status.NodeRef.Name, r.machine.Name) - if err != nil { - return fmt.Errorf("failed to determine if node %v has attached volumes: %w", r.machine.Status.NodeRef.Name, err) - } - if attached { - // If there are volumes still attached, it's possible that node draining did not fully finish, - // this might happen if the kubelet was non-functional during the draining procedure. - // Try forcefully deleting pods in the "Terminating" state to trigger persistent volumes detachment. - klog.Warningf( - "Attached volumes detected on a powered off node, node draining may not succeed. " + - "Attempting to delete unevicted pods", - ) - numPodsDeleted, err := r.machineScope.deleteUnevictedPods() - klog.Warningf("Deleted %d pods", numPodsDeleted) - if err != nil { - return fmt.Errorf("unable to fully drain node, can not delete unevicted pods: %w", err) - } - return fmt.Errorf("node %v has attached volumes, requeuing", r.machine.Status.NodeRef.Name) - } - } - - klog.V(3).Infof("Checking attached disks before vm destroy") - disks, err := vm.getAttachedDisks() - if err != nil { - return fmt.Errorf("%v: can not obtain virtual disks attached to the vm: %w", r.machine.GetName(), err) - } - - additionalDisks := len(r.providerSpec.DataDisks) - // Currently, MAPI only allows VMs to be configured w/ 1 primary disk in the template and a limited number of additional - // disks via the data disks configuration. So, we are expecting the VM to have only one disk, which is OS disk, plus - // the additional disks defined in the DataDisks configuration. - if len(disks) > 1+additionalDisks { - // If node drain was skipped we need to detach disks forcefully to prevent possible data corruption. - if drainSkipped { - klog.V(1).Infof( - "%s: drain was skipped for the machine, detaching disks before vm destruction to prevent data loss", - r.machine.GetName(), - ) - if err := vm.detachDisks(filterOutVmOsDisk(disks, r.machine)); err != nil { - return fmt.Errorf("failed to detach disks: %w", err) - } - klog.V(1).Infof( - "%s: disks were detached", r.machine.GetName(), - ) - return errors.New( - "disks were detached, vm will be attempted to destroy in next reconciliation, requeuing", - ) - } - - // Block vm destruction till attach-detach controller has properly detached disks - return errors.New( - "additional attached disks detected, block vm destruction and wait for disks to be detached", - ) - } - - task, err := vm.Obj.Destroy(r.Context) - if err != nil { - metrics.RegisterFailedInstanceDelete(&metrics.MachineLabels{ - Name: r.machine.Name, - Namespace: r.machine.Namespace, - Reason: "Destroy finished with error", - }) - return fmt.Errorf("%v: failed to destroy vm: %w", r.machine.GetName(), err) - } - - if r.machineScope.providerSpec.Workspace.VMGroup != "" { - klog.Infof("Removing machine: %v from vm group: %v", r.machine.Name, r.machineScope.providerSpec.Workspace.VMGroup) - if err := modifyVMGroup(r.machineScope, true); err != nil { - return fmt.Errorf("failed to remove machine from vm group: %w", err) - } - } - - if err := setProviderStatus(task.Reference().Value, conditionSuccess(), r.machineScope, vm); err != nil { - return fmt.Errorf("failed to set provider status: %w", err) - } - - // TODO: consider returning an error to specify retry time here - return fmt.Errorf("destroying vm in progress, requeuing") -} - -// nodeHasVolumesAttached returns true if node status still have volumes attached -// pod deletion and volume detach happen asynchronously, so pod could be deleted before volume detached from the node -// this could cause issue for some storage provisioner, for example, vsphere-volume this is problematic -// because if the node is deleted before detach success, then the underline VMDK will be deleted together with the Machine -// so after node draining we need to check if all volumes are detached before deleting the node. -func (r *Reconciler) nodeHasVolumesAttached(ctx context.Context, nodeName string, machineName string) (bool, error) { - node := &corev1.Node{} - if err := r.apiReader.Get(ctx, apimachinerytypes.NamespacedName{Name: nodeName}, node); err != nil { - if apierrors.IsNotFound(err) { - klog.Errorf("Could not find node from noderef, it may have already been deleted: %v", err) - return false, nil - } - return true, err - } - - return len(node.Status.VolumesAttached) != 0, nil -} - -// reconcileMachineWithCloudState reconcile machineSpec and status with the latest cloud state -func (r *Reconciler) reconcileMachineWithCloudState(vm *virtualMachine, taskRef string) error { - klog.V(3).Infof("%v: reconciling machine with cloud state", r.machine.GetName()) - // TODO: reconcile task - - if err := r.reconcileRegionAndZoneLabels(vm); err != nil { - // Not treating this is as a fatal error for now. - klog.Errorf("Failed to reconcile region and zone labels: %v", err) - } - - klog.V(3).Infof("%v: reconciling providerID", r.machine.GetName()) - if err := r.reconcileProviderID(vm); err != nil { - return err - } - - klog.V(3).Infof("%v: reconciling network", r.machine.GetName()) - if err := r.reconcileNetwork(vm); err != nil { - return err - } - - klog.V(3).Infof("%v: reconciling powerstate annotation", r.machine.GetName()) - if err := r.reconcilePowerStateAnnontation(vm); err != nil { - return err - } - - return setProviderStatus(taskRef, conditionSuccess(), r.machineScope, vm) -} - -// reconcileRegionAndZoneLabels reconciles the labels on the Machine containing -// region and zone information -- provided the vSphere cloud provider has been -// configured with the labels that identify region and zone, and the configured -// tags are found somewhere in the ancestry of the given virtual machine. -func (r *Reconciler) reconcileRegionAndZoneLabels(vm *virtualMachine) error { - if r.vSphereConfig == nil { - klog.Warning("No vSphere cloud provider config. " + - "Will not set region and zone labels.") - return nil - } - - regionLabel := r.vSphereConfig.Labels.Region - zoneLabel := r.vSphereConfig.Labels.Zone - - var res map[string]string - - err := r.session.WithCachingTagsManager(vm.Context, func(c *session.CachingTagsManager) error { - var err error - res, err = vm.getRegionAndZone(c, regionLabel, zoneLabel) - - return err - }) - - if err != nil { - return err - } - - if r.machine.Labels == nil { - r.machine.Labels = make(map[string]string) - } - - r.machine.Labels[machinecontroller.MachineRegionLabelName] = res[regionKey] - r.machine.Labels[machinecontroller.MachineAZLabelName] = res[zoneKey] - - return nil -} - -func (r *Reconciler) reconcileProviderID(vm *virtualMachine) error { - providerID, err := convertUUIDToProviderID(vm.Obj.UUID(vm.Context)) - if err != nil { - return err - } - r.machine.Spec.ProviderID = &providerID - return nil -} - -// convertUUIDToProviderID transforms a UUID string into a provider ID. -func convertUUIDToProviderID(UUID string) (string, error) { - parsedUUID, err := uuid.Parse(UUID) - if err != nil { - return "", err - } - return providerIDPrefix + parsedUUID.String(), nil -} - -func (r *Reconciler) reconcileNetwork(vm *virtualMachine) error { - currentNetworkStatusList, err := vm.getNetworkStatusList(r.session.Client.Client) - if err != nil { - return fmt.Errorf("error getting network status: %v", err) - } - - //If the VM is powered on then issue requeues until all of the VM's - //networks have IP addresses. - expectNetworkLen, currentNetworkLen := len(r.providerSpec.Network.Devices), len(currentNetworkStatusList) - if expectNetworkLen != currentNetworkLen { - return fmt.Errorf("invalid network count: expected=%d current=%d", expectNetworkLen, currentNetworkLen) - } - - var ipAddrs []corev1.NodeAddress - for _, netStatus := range currentNetworkStatusList { - for _, ip := range netStatus.IPAddrs { - ipAddrs = append(ipAddrs, corev1.NodeAddress{ - Type: corev1.NodeInternalIP, - Address: ip, - }) - } - } - - // Using Name() if InventoryPath is empty will return empty name - // see: https://github.com/vmware/govmomi/blob/master/object/common.go#L66-L75 - // Using ObjectName() as it will query from VirtualMachine properties - - vmName, err := vm.Obj.ObjectName(vm.Context) - if err != nil { - return fmt.Errorf("error getting virtual machine name: %v", err) - } - - ipAddrs = append(ipAddrs, corev1.NodeAddress{ - Type: corev1.NodeInternalDNS, - Address: vmName, - }) - - klog.V(3).Infof("%v: reconciling network: IP addresses: %v", r.machine.GetName(), ipAddrs) - r.machine.Status.Addresses = ipAddrs - - // If static IP, verify machine still has IPAddressClaim w/ owner field configure - if ipam.HasStaticIPConfiguration(r.providerSpec) { - err = ipam.VerifyIPAddressOwners(r.Context, r.client, r.machine, r.providerSpec.Network.Devices) - if err != nil { - return fmt.Errorf("error verifying ip address claims: %v", err) - } - } - - return nil -} - -func (r *Reconciler) reconcilePowerStateAnnontation(vm *virtualMachine) error { - if vm == nil { - return errors.New("provided VM is nil") - } - - // This can return an error if machine is being deleted - powerState, err := vm.getPowerState() - if err != nil { - return err - } - - if r.machine.Annotations == nil { - r.machine.Annotations = map[string]string{} - } - r.machine.Annotations[machinecontroller.MachineInstanceStateAnnotationName] = string(powerState) - - return nil -} - -func validateMachine(machine machinev1.Machine) error { - if machine.Labels[machinev1.MachineClusterIDLabel] == "" { - return machinecontroller.InvalidMachineConfiguration("%v: missing %q label", machine.GetName(), machinev1.MachineClusterIDLabel) - } - - return nil -} - -func findVM(s *machineScope) (types.ManagedObjectReference, error) { - uuid := string(s.machine.UID) - - vm, err := s.GetSession().FindVM(s.Context, uuid, s.machine.Name) - if err != nil { - if isNotFound(err) { - return types.ManagedObjectReference{}, errNotFound{instanceUUID: true, uuid: uuid} - } - return types.ManagedObjectReference{}, err - } - - if vm == nil { - return types.ManagedObjectReference{}, errNotFound{instanceUUID: true, uuid: uuid} - } - - return vm.Reference(), nil -} - -// errNotFound is returned by the findVM function when a VM is not found. -type errNotFound struct { - instanceUUID bool - uuid string -} - -func (e errNotFound) Error() string { - if e.instanceUUID { - return fmt.Sprintf("vm with instance uuid %s not found", e.uuid) - } - return fmt.Sprintf("vm with bios uuid %s not found", e.uuid) -} - -func isNotFound(err error) bool { - switch err.(type) { - case errNotFound, *errNotFound, *find.NotFoundError: - return true - default: - return false - } -} - -func getSubnetMask(prefix netip.Prefix) (string, error) { - prefixLength := net.IPv4len * 8 - if prefix.Addr().Is6() { - prefixLength = net.IPv6len * 8 - } - ipMask := net.CIDRMask(prefix.Masked().Bits(), prefixLength) - maskBytes, err := hex.DecodeString(ipMask.String()) - if err != nil { - return "", fmt.Errorf("could not translate ip mask: %w", err) - } - ip := net.IP(maskBytes) - maskStr := ip.To16().String() - return maskStr, nil -} - -// getAddressesFromPool retrieves IP addresses and associated gateway from IP address pools -func getAddressesFromPool(configIdx int, networkConfig machinev1.NetworkDeviceSpec, s *machineScope) ([]string, string, error) { - addresses := []string{} - var gateway string - for poolIdx := range networkConfig.AddressesFromPools { - claimName := ipam.GetIPAddressClaimName(s.machine, configIdx, poolIdx) - ipAddress, err := ipam.RetrieveBoundIPAddress(s.Context, s.client, s.machine, claimName) - if err != nil { - return nil, "", fmt.Errorf("error retrieving bound IP address: %w", err) - } - ipAddressSpec := ipAddress.Spec - addresses = append(addresses, fmt.Sprintf("%s/%d", ipAddressSpec.Address, ipAddressSpec.Prefix)) - if len(ipAddressSpec.Gateway) > 0 { - gateway = ipAddressSpec.Gateway - } - } - return addresses, gateway, nil -} - -// constructKargsFromNetworkConfig builds a string which comprises ip and nameserver stanzas -// which are consumed by guestinfo.afterburn.initrd.network-kargs. -func constructKargsFromNetworkConfig(s *machineScope) (string, error) { - outKargs := "" - networkConfigs := s.providerSpec.Network.Devices - for configIdx, networkConfig := range networkConfigs { - // retrieve any IP addresses assigned by an IP address pool - addressesFromPool, gatewayFromPool, err := getAddressesFromPool(configIdx, networkConfig, s) - if err != nil { - return "", fmt.Errorf("error getting addresses from IP pool: %w", err) - } - var gateway string - if len(gatewayFromPool) > 0 { - gateway = gatewayFromPool - } else { - gateway = networkConfig.Gateway - } - - var gatewayIp netip.Addr - if len(gateway) > 0 { - gatewayIp, err = netip.ParseAddr(gateway) - if err != nil { - return "", fmt.Errorf("error parsing gateway address: %w", err) - } - } - - ipAddresses := []string{} - ipAddresses = append(ipAddresses, networkConfig.IPAddrs...) - ipAddresses = append(ipAddresses, addressesFromPool...) - - // construct IP address network kargs for each IP address - for _, address := range ipAddresses { - prefix, err := netip.ParsePrefix(address) - if err != nil { - return "", fmt.Errorf("error parsing prefix: %w", err) - } - var ipStr, gatewayStr, maskStr string - addr := prefix.Addr() - // IPv6 addresses must be wrapped in [] for dracut network kargs - if addr.Is6() { - maskStr = fmt.Sprintf("%d", prefix.Bits()) - ipStr = fmt.Sprintf("[%s]", addr.String()) - if len(gateway) > 0 && gatewayIp.Is6() { - gatewayStr = fmt.Sprintf("[%s]", gateway) - } - } else if addr.Is4() { - maskStr, err = getSubnetMask(prefix) - if err != nil { - return "", fmt.Errorf("error getting subnet mask: %w", err) - } - if len(gateway) > 0 && gatewayIp.Is4() { - gatewayStr = gateway - } - ipStr = addr.String() - } else { - return "", errors.New("IP address must adhere to IPv4 or IPv6 format") - } - - outKargs = outKargs + fmt.Sprintf("ip=%s::%s:%s:::none ", ipStr, gatewayStr, maskStr) - } - - // construct nameserver network karg for each defined nameserver - for _, nameserver := range networkConfig.Nameservers { - ip := net.ParseIP(nameserver) - if ip.To4() == nil { - nameserver = fmt.Sprintf("[%s]", nameserver) - } - outKargs = outKargs + fmt.Sprintf("nameserver=%s ", nameserver) - } - } - return outKargs, nil -} - -func isRetrieveMONotFound(taskRef string, err error) bool { - return err.Error() == fmt.Sprintf("ServerFaultCode: The object 'vim.Task:%v' has already been deleted or has not been completely created", taskRef) -} - -func getHwVersion(ctx context.Context, vm *object.VirtualMachine) (int, error) { - var _vm mo.VirtualMachine - if err := vm.Properties(ctx, vm.Reference(), []string{"config.version"}, &_vm); err != nil { - return 0, fmt.Errorf("error getting hw version information for vm %s: %w", vm.Name(), err) - } - - versionString := _vm.Config.Version - version := strings.TrimPrefix(versionString, "vmx-") - parsedVersion, err := strconv.Atoi(version) - if err != nil { - return 0, fmt.Errorf("can not extract hardware version from version string: %s, format unknown", versionString) - } - return parsedVersion, nil -} - -func clone(s *machineScope) (string, error) { - userData, err := s.GetUserData() - if err != nil { - return "", err - } - - vmTemplate, err := s.GetSession().FindVM(*s, "", s.providerSpec.Template) - if err != nil { - const multipleFoundMsg = "multiple templates found, specify one in config" - const notFoundMsg = "template not found, specify valid value" - defaultError := fmt.Errorf("unable to get template %q: %w", s.providerSpec.Template, err) - return "", handleVSphereError(multipleFoundMsg, notFoundMsg, defaultError, err) - } - - hwVersion, err := getHwVersion(s.Context, vmTemplate) - if err != nil { - return "", machinecontroller.InvalidMachineConfiguration( - "Unable to detect machine template HW version for machine '%s': %v", s.machine.GetName(), err, - ) - } - if hwVersion < minimumHWVersion { - return "", machinecontroller.InvalidMachineConfiguration( - "Hardware lower than %d is not supported, clone stopped. "+ - "Detected machine template version is %d. "+ - "Please update machine template: https://docs.openshift.com/container-platform/latest/updating/updating_a_cluster/updating-hardware-on-nodes-running-on-vsphere.html", - minimumHWVersion, hwVersion, - ) - } - - // Default clone type is FullClone, having snapshot on clonee template will cause incorrect disk sizing. - diskMoveType := fullCloneDiskMoveType - var snapshotRef *types.ManagedObjectReference - - // If a linked clone is requested then a MoRef for a snapshot must be - // found with which to perform the linked clone. - // Empty clone mode is a full clone, - // because otherwise disk size from provider spec will not be respected. - if s.providerSpec.CloneMode == machinev1.LinkedClone { - if s.providerSpec.DiskGiB > 0 { - klog.Warningf("LinkedClone mode is set. Disk size parameter from ProviderSpec will be ignored") - } - if s.providerSpec.Snapshot == "" { - klog.V(3).Infof("%v: no snapshot name provided, getting snapshot using template", s.machine.GetName()) - var vm mo.VirtualMachine - if err := vmTemplate.Properties(s.Context, vmTemplate.Reference(), []string{"snapshot"}, &vm); err != nil { - return "", fmt.Errorf("error getting snapshot information for template %s: %w", vmTemplate.Name(), err) - } - - if vm.Snapshot != nil { - snapshotRef = vm.Snapshot.CurrentSnapshot - } - } else { - klog.V(3).Infof("%v: searching for snapshot by name %s", s.machine.GetName(), s.providerSpec.Snapshot) - var err error - snapshotRef, err = vmTemplate.FindSnapshot(s.Context, s.providerSpec.Snapshot) - if err != nil { - // Maybe return an error there? - klog.V(3).Infof("%v: failed to find snapshot %s, fallback to FullClone", s.machine.GetName(), s.providerSpec.Snapshot) - } - } - - if snapshotRef != nil { - diskMoveType = linkCloneDiskMoveType - } - } - - var folderPath, datastorePath, resourcepoolPath string - if s.providerSpec.Workspace != nil { - folderPath = s.providerSpec.Workspace.Folder - datastorePath = s.providerSpec.Workspace.Datastore - resourcepoolPath = s.providerSpec.Workspace.ResourcePool - } - - folder, err := s.GetSession().Finder.FolderOrDefault(s, folderPath) - if err != nil { - const multipleFoundMsg = "multiple folders found, specify one in config" - const notFoundMsg = "folder not found, specify valid value" - defaultError := fmt.Errorf("unable to get folder for %q: %w", folderPath, err) - return "", handleVSphereError(multipleFoundMsg, notFoundMsg, defaultError, err) - } - - datastore, err := s.GetSession().Finder.DatastoreOrDefault(s, datastorePath) - if err != nil { - const multipleFoundMsg = "multiple datastores found, specify one in config" - const notFoundMsg = "datastore not found, specify valid value" - defaultError := fmt.Errorf("unable to get datastore for %q: %w", datastorePath, err) - return "", handleVSphereError(multipleFoundMsg, notFoundMsg, defaultError, err) - } - - resourcepool, err := s.GetSession().Finder.ResourcePoolOrDefault(s, resourcepoolPath) - if err != nil { - const multipleFoundMsg = "multiple resource pools found, specify one in config" - const notFoundMsg = "resource pool not found, specify valid value" - defaultError := fmt.Errorf("unable to get resource pool for %q: %w", resourcepool, err) - return "", handleVSphereError(multipleFoundMsg, notFoundMsg, defaultError, err) - } - - numCPUs := s.providerSpec.NumCPUs - - numCoresPerSocket := s.providerSpec.NumCoresPerSocket - if numCoresPerSocket == 0 { - numCoresPerSocket = numCPUs - } - - devices, err := vmTemplate.Device(s.Context) - if err != nil { - return "", fmt.Errorf("error getting devices %v", err) - } - - // Create a new list of device specs for cloning the VM. - deviceSpecs := []types.BaseVirtualDeviceConfigSpec{} - - // Only non-linked clones may expand the size of the template's disk. - if snapshotRef == nil { - diskSpec, err := getDiskSpec(s, devices) - if err != nil { - return "", fmt.Errorf("error getting disk spec for %q: %w", s.providerSpec.Snapshot, err) - } - deviceSpecs = append(deviceSpecs, diskSpec) - } - - // Process all DataDisks definitions to dynamically create and add disks to the VM - additionalDisks, err := createDataDisks(s, devices) - if err != nil { - return "", fmt.Errorf("error getting additional disk specs: %w", err) - } - deviceSpecs = append(deviceSpecs, additionalDisks...) - - klog.V(3).Infof("Getting network devices") - networkDevices, err := getNetworkDevices(s, resourcepool, devices) - if err != nil { - return "", fmt.Errorf("error getting network specs: %w", err) - } - - deviceSpecs = append(deviceSpecs, networkDevices...) - - extraConfig := []types.BaseOptionValue{} - - extraConfig = append(extraConfig, IgnitionConfig(userData)...) - extraConfig = append(extraConfig, &types.OptionValue{ - Key: GuestInfoHostname, - Value: s.machine.GetName(), - }) - extraConfig = append(extraConfig, &types.OptionValue{ - Key: StealClock, - Value: "TRUE", - }) - - if ipam.HasStaticIPConfiguration(s.providerSpec) { - networkKargs, err := constructKargsFromNetworkConfig(s) - if err != nil { - return "", err - } - if len(networkKargs) > 0 { - extraConfig = append(extraConfig, &types.OptionValue{ - Key: GuestInfoNetworkKargs, - Value: networkKargs, - }) - } - } - - spec := types.VirtualMachineCloneSpec{ - Config: &types.VirtualMachineConfigSpec{ - Annotation: s.machine.GetName(), - // Assign the clone's InstanceUUID the value of the Kubernetes Machine - // object's UID. This allows lookup of the cloned VM prior to knowing - // the VM's UUID. - InstanceUuid: string(s.machine.UID), - Flags: newVMFlagInfo(), - ExtraConfig: extraConfig, - DeviceChange: deviceSpecs, - NumCPUs: numCPUs, - NumCoresPerSocket: numCoresPerSocket, - MemoryMB: s.providerSpec.MemoryMiB, - }, - Location: types.VirtualMachineRelocateSpec{ - Datastore: types.NewReference(datastore.Reference()), - Folder: types.NewReference(folder.Reference()), - Pool: types.NewReference(resourcepool.Reference()), - DiskMoveType: diskMoveType, - }, - PowerOn: false, // Create powered off machine, for power it on later in "create" procedure - Snapshot: snapshotRef, - } - - task, err := vmTemplate.Clone(s, folder, s.machine.GetName(), spec) - if err != nil { - return "", fmt.Errorf("error triggering clone op for machine %v: %w", s, err) - } - taskVal := task.Reference().Value - klog.V(3).Infof("%v: running task: %+v", s.machine.GetName(), taskVal) - return taskVal, nil -} - -func modifyVMGroup(s *machineScope, delete bool) error { - vmRef, err := findVM(s) - if err != nil { - if isNotFound(err) { - return fmt.Errorf("virtual machine %s was not found: %w", s.machine.Name, err) - } - return fmt.Errorf("error finding virtual machine: %w", err) - } - - rp, err := s.session.Finder.ResourcePool(s.Context, s.providerSpec.Workspace.ResourcePool) - if err != nil { - return fmt.Errorf("error getting resource pool %s: %w", s.providerSpec.Workspace.ResourcePool, err) - } - - ownerRef, err := rp.Owner(s.Context) - if err != nil { - return fmt.Errorf("error getting cluster owner reference from resource pool %s: %w", s.providerSpec.Workspace.ResourcePool, err) - } - - var ccr *object.ClusterComputeResource - var ok bool - if ccr, ok = ownerRef.(*object.ClusterComputeResource); !ok { - return fmt.Errorf("error getting cluster from resource pool %s: %w", s.providerSpec.Workspace.ResourcePool, err) - } - - clusterConfig, err := ccr.Configuration(s.Context) - if err != nil { - return fmt.Errorf("error getting cluster %s configuration: %w", s.providerSpec.Workspace.ResourcePool, err) - } - - var clusterVmGroup *types.ClusterVmGroup - - for _, g := range clusterConfig.Group { - if vmg, ok := g.(*types.ClusterVmGroup); ok { - if vmg.Name == s.providerSpec.Workspace.VMGroup { - clusterVmGroup = vmg - break - } - } - } - - switch { - case clusterVmGroup == nil: - clusterVmGroup = &types.ClusterVmGroup{ - Vm: []types.ManagedObjectReference{vmRef}, - } - case slices.Contains(clusterVmGroup.Vm, vmRef) && delete: - clusterVmGroup.Vm = slices.DeleteFunc(clusterVmGroup.Vm, func(ref types.ManagedObjectReference) bool { - return vmRef.Value == ref.Value - }) - case !slices.Contains(clusterVmGroup.Vm, vmRef): - clusterVmGroup.Vm = append(clusterVmGroup.Vm, vmRef) - default: - return nil - } - - clusterConfigSpec := &types.ClusterConfigSpecEx{ - GroupSpec: []types.ClusterGroupSpec{ - { - ArrayUpdateSpec: types.ArrayUpdateSpec{ - Operation: types.ArrayUpdateOperation("edit"), - }, - Info: &types.ClusterVmGroup{ - ClusterGroupInfo: types.ClusterGroupInfo{ - Name: s.providerSpec.Workspace.VMGroup, - }, - Vm: clusterVmGroup.Vm, - }, - }, - }, - } - - clusterTask, err := ccr.Reconfigure(s.Context, clusterConfigSpec, true) - if err != nil { - return fmt.Errorf("error reconfiguring cluster %s for vm-host group %s: %w", ccr.Name(), clusterVmGroup.Name, err) - } - - return clusterTask.Wait(s.Context) -} - -func powerOn(s *machineScope) (string, error) { - vmRef, err := findVM(s) - if err != nil { - if !isNotFound(err) { - return "", err - } - return "", fmt.Errorf("vm not found during creation for powering on: %w", err) - } - - datacenter := s.session.Datacenter - if datacenter == nil { // if there is no dataceneter, fallback to old powerOn method via vm object - vm := &virtualMachine{ - Context: s.Context, - Obj: object.NewVirtualMachine(s.session.Client.Client, vmRef), - Ref: vmRef, - } - - return vm.powerOnVM() - } - - overrideDRS := &types.OptionValue{ - Key: string(types.ClusterPowerOnVmOptionOverrideAutomationLevel), - Value: string(types.DrsBehaviorFullyAutomated), - } - task, err := datacenter.PowerOnVM(s.Context, []types.ManagedObjectReference{vmRef}, overrideDRS) - if err != nil { - return "", fmt.Errorf("error powering on %s vm: %w", s.machine.Name, err) - } - return task.Reference().Value, nil -} - -func getDiskSpec(s *machineScope, devices object.VirtualDeviceList) (types.BaseVirtualDeviceConfigSpec, error) { - disks := devices.SelectByType((*types.VirtualDisk)(nil)) - if len(disks) != 1 { - return nil, fmt.Errorf("invalid disk count: %d", len(disks)) - } - - disk := disks[0].(*types.VirtualDisk) - cloneCapacityKB := int64(s.providerSpec.DiskGiB) * 1024 * 1024 - if disk.CapacityInKB > cloneCapacityKB { - return nil, machinecontroller.InvalidMachineConfiguration( - "can't resize template disk down, initial capacity is larger: %dKiB > %dKiB", - disk.CapacityInKB, cloneCapacityKB) - } - disk.CapacityInKB = cloneCapacityKB - - return &types.VirtualDeviceConfigSpec{ - Operation: types.VirtualDeviceConfigSpecOperationEdit, - Device: disk, - }, nil -} - -func createDataDisks(s *machineScope, devices object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) { - var diskSpecs []types.BaseVirtualDeviceConfigSpec - - // Only add additional disks if the feature gate is enabled. - if len(s.providerSpec.DataDisks) > 0 && !s.featureGates.Enabled(featuregate.Feature(apifeatures.FeatureGateVSphereMultiDisk)) { - return nil, machinecontroller.InvalidMachineConfiguration( - "machines cannot contain additional disks due to VSphereMultiDisk feature gate being disabled") - } - - // Get primary disk - disks := devices.SelectByType((*types.VirtualDisk)(nil)) - if len(disks) == 0 { - return nil, fmt.Errorf("invalid disk count: %d", len(disks)) - } - - // There is at least one disk - primaryDisk := disks[0].(*types.VirtualDisk) - - // Get the controller of the primary disk. - controller, ok := devices.FindByKey(primaryDisk.ControllerKey).(types.BaseVirtualController) - if !ok { - return nil, fmt.Errorf("unable to find controller with key=%v", primaryDisk.ControllerKey) - } - - controllerKey := controller.GetVirtualController().Key - unitNumberAssigner, err := newUnitNumberAssigner(controller, devices) - if err != nil { - return nil, fmt.Errorf("unable to create unit number assigner: %v", err) - } - - // Let's create the data disks now - for i, dataDisk := range s.providerSpec.DataDisks { - klog.V(2).InfoS("Adding disk", "name", dataDisk.Name, "spec", dataDisk) - - backing := &types.VirtualDiskFlatVer2BackingInfo{ - DiskMode: string(types.VirtualDiskModePersistent), - VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{ - FileName: "", - }, - } - - // Set provisioning type for the new data disk. - // Currently, if ThinProvisioned is not set, GOVC will set default to false. We may want to change this behavior - // to match what template image OS disk has configured to make them match if not set. - switch dataDisk.ProvisioningMode { - case machinev1.ProvisioningModeThin: - backing.ThinProvisioned = types.NewBool(true) - case machinev1.ProvisioningModeThick: - backing.ThinProvisioned = types.NewBool(false) - case machinev1.ProvisioningModeEagerlyZeroed: - backing.ThinProvisioned = types.NewBool(false) - backing.EagerlyScrub = types.NewBool(true) - default: - klog.V(2).Infof("No provisioning type detected. Leaving configuration empty.") - } - - dev := &types.VirtualDisk{ - VirtualDevice: types.VirtualDevice{ - // Key needs to be unique and cannot match another new disk being added. So we'll use the index as an - // input to NewKey. NewKey() will always return same value since our new devices are not part of devices yet. - Key: devices.NewKey() - int32(i), - Backing: backing, - ControllerKey: controller.GetVirtualController().Key, - }, - CapacityInKB: int64(dataDisk.SizeGiB) * 1024 * 1024, - } - - vd := dev.GetVirtualDevice() - vd.ControllerKey = controllerKey - - // Assign unit number to the new disk. Should be next available slot on the controller. - unitNumber, err := unitNumberAssigner.assign() - if err != nil { - return nil, err - } - vd.UnitNumber = &unitNumber - - klog.V(2).InfoS("Created device for data disk device", "name", dataDisk.Name, "spec", dataDisk, "device", dev) - diskSpecs = append(diskSpecs, &types.VirtualDeviceConfigSpec{ - Device: dev, - Operation: types.VirtualDeviceConfigSpecOperationAdd, - FileOperation: types.VirtualDeviceConfigSpecFileOperationCreate, - }) - } - - return diskSpecs, nil -} - -type unitNumberAssigner struct { - used []bool - offset int32 -} - -func newUnitNumberAssigner(controller types.BaseVirtualController, existingDevices object.VirtualDeviceList) (*unitNumberAssigner, error) { - if controller == nil { - return nil, errors.New("controller parameter cannot be nil") - } - used := make([]bool, maxUnitNumber) - - // SCSIControllers also use a unit. - if scsiController, ok := controller.(types.BaseVirtualSCSIController); ok { - used[scsiController.GetVirtualSCSIController().ScsiCtlrUnitNumber] = true - } - controllerKey := controller.GetVirtualController().Key - - // Mark all unit numbers of existing devices as used - for _, device := range existingDevices { - d := device.GetVirtualDevice() - if d.ControllerKey == controllerKey && d.UnitNumber != nil { - used[*d.UnitNumber] = true - } - } - - // Set offset to 0, it will auto-increment on the first assignment. - return &unitNumberAssigner{used: used, offset: 0}, nil -} - -func (a *unitNumberAssigner) assign() (int32, error) { - if int(a.offset) > len(a.used) { - return -1, fmt.Errorf("all unit numbers are already in-use") - } - for i, isInUse := range a.used[a.offset:] { - unit := int32(i) + a.offset - if !isInUse { - a.used[unit] = true - a.offset++ - return unit, nil - } - } - return -1, fmt.Errorf("all unit numbers are already in-use") -} - -func getNetworkDevices(s *machineScope, resourcepool *object.ResourcePool, devices object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) { - var networkDevices []types.BaseVirtualDeviceConfigSpec - // Remove any existing NICs - for _, dev := range devices.SelectByType((*types.VirtualEthernetCard)(nil)) { - networkDevices = append(networkDevices, &types.VirtualDeviceConfigSpec{ - Device: dev, - Operation: types.VirtualDeviceConfigSpecOperationRemove, - }) - } - - // Add new NICs based on the machine config. - for i := range s.providerSpec.Network.Devices { - var ccrMo mo.ClusterComputeResource - var backing types.BaseVirtualDeviceBackingInfo - - netSpec := &s.providerSpec.Network.Devices[i] - klog.V(3).Infof("Adding device: %v", netSpec.NetworkName) - - clusterRef, err := resourcepool.Owner(s.Context) - if err != nil { - return nil, fmt.Errorf("unable to find cluster resource: %w", err) - } - - clusterRes := object.NewClusterComputeResource(s.GetSession().Client.Client, clusterRef.Reference()) - err = clusterRes.Properties(s.Context, clusterRef.Reference(), []string{"network"}, &ccrMo) - if err != nil { - return nil, fmt.Errorf("unable to get list of networks in cluster: %w", err) - } - - for _, netRef := range ccrMo.Network { - // Use generic network object to get name - genericNetwork := object.NewNetwork(s.GetSession().Client.Client, netRef) - networkName, err := genericNetwork.ObjectName(s.Context) - if err != nil { - return nil, fmt.Errorf("unable to get network name: %w", err) - } - if netSpec.NetworkName == networkName { - // Use more specific network reference to get Ethernet info - ref := object.NewReference(s.GetSession().Client.Client, netRef) - networkObject, ok := ref.(object.NetworkReference) - if !ok { - return nil, fmt.Errorf("unable to create new ethernet card backing info for network %q: network type failure: %s", netSpec.NetworkName, ref.Reference().Type) - } - - backing, err = networkObject.EthernetCardBackingInfo(s.Context) - if err != nil { - return nil, fmt.Errorf("unable to create new ethernet card backing info for network %q: %w", netSpec.NetworkName, err) - } - break - } - } - - if backing == nil { - return nil, machinecontroller.InvalidMachineConfiguration("unable to get network for %q", netSpec.NetworkName) - } - - dev, err := object.EthernetCardTypes().CreateEthernetCard(ethCardType, backing) - if err != nil { - return nil, fmt.Errorf("unable to create new ethernet card %q for network %q: %w", ethCardType, netSpec.NetworkName, err) - } - - // Get the actual NIC object. This is safe to assert without a check - // because "object.EthernetCardTypes().CreateEthernetCard" returns a - // "types.BaseVirtualEthernetCard" as a "types.BaseVirtualDevice". - nic := dev.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard() - // Assign a temporary device key to ensure that a unique one will be - // generated when the device is created. - nic.Key = int32(i) - - networkDevices = append(networkDevices, &types.VirtualDeviceConfigSpec{ - Device: dev, - Operation: types.VirtualDeviceConfigSpecOperationAdd, - }) - klog.V(3).Infof("Adding device: eth card type: %v, network spec: %+v, device info: %+v", - ethCardType, netSpec, dev.GetVirtualDevice().Backing) - } - - return networkDevices, nil -} - -func newVMFlagInfo() *types.VirtualMachineFlagInfo { - diskUUIDEnabled := true - return &types.VirtualMachineFlagInfo{ - DiskUuidEnabled: &diskUUIDEnabled, - } -} - -func taskIsFinished(task *mo.Task) (bool, error) { - if task == nil { - return true, nil - } - - // Otherwise the course of action is determined by the state of the task. - klog.V(3).Infof("task: %v, state: %v, description-id: %v", task.Reference().Value, task.Info.State, task.Info.DescriptionId) - switch task.Info.State { - case types.TaskInfoStateQueued: - return false, nil - case types.TaskInfoStateRunning: - return false, nil - case types.TaskInfoStateSuccess: - return true, nil - case types.TaskInfoStateError: - return true, errors.New(task.Info.Error.LocalizedMessage) - default: - return false, fmt.Errorf("task: %v, unknown state %v", task.Reference().Value, task.Info.State) - } -} - -func setProviderStatus(taskRef string, condition metav1.Condition, scope *machineScope, vm *virtualMachine) error { - klog.Infof("%s: Updating provider status", scope.machine.Name) - - if vm != nil { - id := vm.Obj.UUID(scope.Context) - scope.providerStatus.InstanceID = &id - - // This can return an error if machine is being deleted - powerState, err := vm.getPowerState() - if err != nil { - klog.V(3).Infof("%s: Failed to get power state during provider status update: %v", scope.machine.Name, err) - } else { - powerStateString := string(powerState) - scope.providerStatus.InstanceState = &powerStateString - } - } - - if taskRef != "" { - scope.providerStatus.TaskRef = taskRef - } - - scope.providerStatus.Conditions = setConditions(condition, scope.providerStatus.Conditions) - - return nil -} - -func handleVSphereError(multipleFoundMsg, notFoundMsg string, defaultError, vsphereError error) error { - var multipleFoundError *find.MultipleFoundError - if errors.As(vsphereError, &multipleFoundError) { - return machinecontroller.InvalidMachineConfiguration("%s", multipleFoundMsg) - } - - var notFoundError *find.NotFoundError - if errors.As(vsphereError, ¬FoundError) { - return machinecontroller.InvalidMachineConfiguration("%s", notFoundMsg) - } - - return defaultError -} - -type virtualMachine struct { - context.Context - Ref types.ManagedObjectReference - Obj *object.VirtualMachine -} - -// getHostSystemAncestors looks up and returns vm's host system ancestors, such as "Cluster" and "Datacenter". -// Host system is using there because in vCenter cluster is an ancestor of the hypervisor host but not the vm. -func (vm *virtualMachine) getHostSystemAncestors() ([]mo.ManagedEntity, error) { - client := vm.Obj.Client() - pc := client.ServiceContent.PropertyCollector - - host, err := vm.Obj.HostSystem(vm.Context) - if err != nil { - return nil, err - } - - return mo.Ancestors(vm.Context, client, pc, host.Reference()) -} - -// getRegionAndZone checks the virtual machine and each of its ancestors for the -// given region and zone labels and returns their values if found. -func (vm *virtualMachine) getRegionAndZone(tagsMgr *session.CachingTagsManager, regionLabel, zoneLabel string) (map[string]string, error) { - result := make(map[string]string) - - objects, err := vm.getHostSystemAncestors() - if err != nil { - klog.Errorf("Failed to get ancestors for %s: %v", vm.Ref, err) - return nil, err - } - - for i := range objects { - obj := objects[len(objects)-1-i] // Reverse order. - klog.V(4).Infof("getRegionAndZone: Name: %s, Type: %s", - obj.Self.Value, obj.Self.Type) - - tags, err := tagsMgr.ListAttachedTags(vm.Context, obj) - if err != nil { - klog.Warningf("Failed to list attached tags: %v", err) - return nil, err - } - - for _, value := range tags { - tag, err := tagsMgr.GetTag(vm.Context, value) - if err != nil { - klog.Errorf("Failed to get tag: %v", err) - return nil, err - } - - category, err := tagsMgr.GetCategory(vm.Context, tag.CategoryID) - if err != nil { - klog.Errorf("Failed to get tag category: %v", err) - return nil, err - } - - switch { - case regionLabel != "" && category.Name == regionLabel: - result[regionKey] = tag.Name - klog.V(2).Infof("%s has region tag (%s) with value %s", - vm.Ref, category.Name, tag.Name) - - case zoneLabel != "" && category.Name == zoneLabel: - result[zoneKey] = tag.Name - klog.V(2).Infof("%s has zone tag (%s) with value %s", - vm.Ref, category.Name, tag.Name) - } - - // We've found both tags, return early. - if result[regionKey] != "" && result[zoneKey] != "" { - return result, nil - } - } - } - - return result, nil -} - -func (vm *virtualMachine) powerOnVM() (string, error) { - task, err := vm.Obj.PowerOn(vm.Context) - if err != nil { - return "", err - } - return task.Reference().Value, nil -} - -func (vm *virtualMachine) powerOffVM() (string, error) { - task, err := vm.Obj.PowerOff(vm.Context) - if err != nil { - return "", err - } - return task.Reference().Value, nil -} - -func (vm *virtualMachine) getPowerState() (types.VirtualMachinePowerState, error) { - powerState, err := vm.Obj.PowerState(vm.Context) - if err != nil { - return "", err - } - - switch powerState { - case types.VirtualMachinePowerStatePoweredOn: - return types.VirtualMachinePowerStatePoweredOn, nil - case types.VirtualMachinePowerStatePoweredOff: - return types.VirtualMachinePowerStatePoweredOff, nil - case types.VirtualMachinePowerStateSuspended: - return types.VirtualMachinePowerStateSuspended, nil - default: - return "", fmt.Errorf("unexpected power state %q for vm %v", powerState, vm) - } -} - -// reconcileTags ensures that the required tags are present on the virtual machine, eg the Cluster ID -// that is used by the installer on cluster deletion to ensure ther are no leaked resources. -func (vm *virtualMachine) reconcileTags(ctx context.Context, sessionInstance *session.Session, machine *machinev1.Machine, providerSpec *machinev1.VSphereMachineProviderSpec) error { - if err := sessionInstance.WithCachingTagsManager(vm.Context, func(c *session.CachingTagsManager) error { - klog.Infof("%v: Reconciling attached tags", machine.GetName()) - - clusterID := machine.Labels[machinev1.MachineClusterIDLabel] - tagIDs := []string{clusterID} - tagIDs = append(tagIDs, providerSpec.TagIDs...) - klog.Infof("%v: Reconciling %s tags to vm", machine.GetName(), tagIDs) - for _, tagID := range tagIDs { - attached, err := vm.checkAttachedTag(ctx, tagID, c) - if err != nil { - return err - } - - if !attached { - klog.Infof("%v: Attaching %s tag to vm", machine.GetName(), tagID) - // the tag should already be created by installer or the administrator - if err := c.AttachTag(ctx, tagID, vm.Ref); err != nil { - return err - } - } - } - return nil - }); err != nil { - return err - } - - return nil -} - -// checkAttachedTag returns true if tag is already attached to a vm or tag doesn't exist -func (vm *virtualMachine) checkAttachedTag(ctx context.Context, tagName string, m *session.CachingTagsManager) (bool, error) { - // cluster ID tag doesn't exists in UPI, we should skip tag attachment if it's not found - foundTag, err := vm.foundTag(ctx, tagName, m) - if err != nil { - return false, err - } - - if !foundTag { - return true, nil - } - - tags, err := m.GetAttachedTags(ctx, vm.Ref) - if err != nil { - return false, err - } - - for _, tag := range tags { - if session.IsName(tagName) { - if tag.Name == tagName { - return true, nil - } - } else { - if tag.ID == tagName { - return true, nil - } - } - - } - - return false, nil -} - -// tagToCategoryName converts the tag name to the category name based upon the format set up by the installer. -// Note this is only valid in IPI clusters as typically a UPI cluster won't have the cluster ID tag, in which case the -// controller skips tag creation. -// Ref: https://github.com/openshift/installer/blob/f912534f12491721e3874e2bf64f7fa8d44aa7f5/data/data/vsphere/pre-bootstrap/main.tf#L57 -// Ref: https://github.com/openshift/installer/blob/f912534f12491721e3874e2bf64f7fa8d44aa7f5/pkg/destroy/vsphere/vsphere.go#L231 -func tagToCategoryName(tagName string) string { - return fmt.Sprintf("openshift-%s", tagName) -} - -func (vm *virtualMachine) foundTag(ctx context.Context, tagName string, m *session.CachingTagsManager) (bool, error) { - var tags []string - var err error - - if session.IsName(tagName) { - tags, err = m.ListTagsForCategory(ctx, tagToCategoryName(tagName)) - if err != nil { - if isNotFoundErr(err) { - return false, nil - } - return false, err - } - } else { - tags = []string{tagName} - } - klog.V(4).Infof("validating the presence of tags: %+v", tags) - for _, id := range tags { - tag, err := m.GetTag(ctx, id) - if err != nil { - return false, err - } - if session.IsName(tagName) { - if tag.Name == tagName { - return true, nil - } - } else { - if tag.ID == tagName { - return true, nil - } - } - } - - return false, nil -} - -type NetworkStatus struct { - // Connected is a flag that indicates whether this network is currently - // connected to the VM. - Connected bool - - // IPAddrs is one or more IP addresses reported by vm-tools. - IPAddrs []string - - // MACAddr is the MAC address of the network device. - MACAddr string - - // NetworkName is the name of the network. - NetworkName string -} - -func (vm *virtualMachine) getNetworkStatusList(client *vim25.Client) ([]NetworkStatus, error) { - var obj mo.VirtualMachine - var pc = property.DefaultCollector(client) - var props = []string{ - "config.hardware.device", - "guest.net", - } - - if err := pc.RetrieveOne(vm.Context, vm.Ref, props, &obj); err != nil { - return nil, fmt.Errorf("unable to fetch props %v for vm %v: %w", props, vm.Ref, err) - } - klog.V(3).Infof("Getting network status: object reference: %v", obj.Reference().Value) - if obj.Config == nil { - return nil, errors.New("config.hardware.device is nil") - } - - var networkStatusList []NetworkStatus - for _, device := range obj.Config.Hardware.Device { - if dev, ok := device.(types.BaseVirtualEthernetCard); ok { - nic := dev.GetVirtualEthernetCard() - klog.V(3).Infof("Getting network status: device: %v, macAddress: %v", nic.DeviceInfo.GetDescription().Summary, nic.MacAddress) - netStatus := NetworkStatus{ - MACAddr: nic.MacAddress, - } - if obj.Guest != nil { - klog.V(3).Infof("Getting network status: getting guest info") - for _, i := range obj.Guest.Net { - klog.V(3).Infof("Getting network status: getting guest info: network: %+v", i) - if strings.EqualFold(nic.MacAddress, i.MacAddress) { - //TODO: sanitizeIPAddrs - netStatus.IPAddrs = i.IpAddress - netStatus.NetworkName = i.Network - netStatus.Connected = i.Connected - } - } - } - networkStatusList = append(networkStatusList, netStatus) - } - } - - return networkStatusList, nil -} - -type attachedDisk struct { - device *types.VirtualDisk - fileName string - diskMode string -} - -// Filters out disks that look like vm OS disk or any of the additional disks. -// VM os disks filename contains the machine name in it -// and has the format like "[DATASTORE] path-within-datastore/machine-name.vmdk". -// This is based on vSphere behavior, an OS disk file gets a name that equals the target VM name during the clone operation. -func filterOutVmOsDisk(attachedDisks []attachedDisk, machine *machinev1.Machine) []attachedDisk { - var disks []attachedDisk - regex, _ := regexp.Compile(fmt.Sprintf(".*\\/%s(_\\d*)?.vmdk", machine.GetName())) - - for _, disk := range attachedDisks { - if regex.MatchString(disk.fileName) { - continue - } - disks = append(disks, disk) - } - return disks -} - -func (vm *virtualMachine) getAttachedDisks() ([]attachedDisk, error) { - var attachedDiskList []attachedDisk - devices, err := vm.Obj.Device(vm.Context) - if err != nil { - return nil, err - } - - for _, disk := range devices.SelectByType((*types.VirtualDisk)(nil)) { - backingInfo := disk.GetVirtualDevice().Backing.(types.BaseVirtualDeviceFileBackingInfo).(*types.VirtualDiskFlatVer2BackingInfo) - attachedDiskList = append(attachedDiskList, attachedDisk{ - device: disk.(*types.VirtualDisk), - fileName: backingInfo.FileName, - diskMode: backingInfo.DiskMode, - }) - } - - return attachedDiskList, nil -} - -func (vm *virtualMachine) detachDisks(disks []attachedDisk) error { - var errList []error - - for _, disk := range disks { - klog.V(3).Infof("Detaching disk associated with file %v", disk.fileName) - if err := vm.Obj.RemoveDevice(vm.Context, true, disk.device); err != nil { - errList = append(errList, err) - klog.Errorf("Failed to detach disk associated with file %v ", disk.fileName) - } else { - klog.V(3).Infof("Disk associated with file %v has been detached", disk.fileName) - } - } - if len(errList) > 0 { - return apimachineryutilerrors.NewAggregate(errList) - } - return nil -} - -// IgnitionConfig returns a slice of option values that set the given data as -// the guest's ignition config. -func IgnitionConfig(data []byte) []types.BaseOptionValue { - config := EncodeIgnitionConfig(data) - - if config == "" { - return nil - } - - return []types.BaseOptionValue{ - &types.OptionValue{ - Key: GuestInfoIgnitionData, - Value: config, - }, - &types.OptionValue{ - Key: GuestInfoIgnitionEncoding, - Value: "base64", - }, - } -} - -// EncodeIgnitionConfig attempts to decode the given data until it looks to be -// plain-text, then returns a base64 encoded version of that plain-text. -func EncodeIgnitionConfig(data []byte) string { - if len(data) == 0 { - return "" - } - - for { - decoded, err := base64.StdEncoding.DecodeString(string(data)) - if err != nil { - break - } - - data = decoded - } - - return base64.StdEncoding.EncodeToString(data) -} diff --git a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/session/session.go b/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/session/session.go deleted file mode 100644 index 85c9b2276e7..00000000000 --- a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/session/session.go +++ /dev/null @@ -1,243 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package session - -import ( - "context" - "errors" - "fmt" - "net/url" - "sync" - "time" - - "github.com/vmware/govmomi/vapi/rest" - "github.com/vmware/govmomi/vapi/tags" - "github.com/vmware/govmomi/vim25/mo" - "github.com/vmware/govmomi/vim25/types" - - "github.com/google/uuid" - "github.com/vmware/govmomi" - "github.com/vmware/govmomi/find" - "github.com/vmware/govmomi/object" - "github.com/vmware/govmomi/vim25/soap" - "k8s.io/klog/v2" -) - -var sessionCache = map[string]Session{} -var sessionMU sync.Mutex - -const ( - managedObjectTypeTask = "Task" - clientTimeout = 15 * time.Second -) - -// Session is a vSphere session with a configured Finder. -type Session struct { - *govmomi.Client - Finder *find.Finder - Datacenter *object.Datacenter - - username string - password string - - sessionKey string -} - -func newClientWithTimeout(ctx context.Context, u *url.URL, insecure bool, timeout time.Duration) (*govmomi.Client, error) { - clientCreateCtx, clientCreateCtxCancel := context.WithTimeout(ctx, timeout) - defer clientCreateCtxCancel() - // It makes call to vcenter during new client creation, so pass context with timeout there. - client, err := govmomi.NewClient(clientCreateCtx, u, insecure) - if err != nil { - return nil, err - } - client.Timeout = timeout - return client, nil -} - -// GetOrCreate gets a cached session or creates a new one if one does not -// already exist. -func GetOrCreate( - ctx context.Context, - server, datacenter, username, password string, insecure bool) (*Session, error) { - - sessionMU.Lock() - defer sessionMU.Unlock() - - sessionKey := server + username + datacenter - if session, ok := sessionCache[sessionKey]; ok { - sessionActive, err := session.SessionManager.SessionIsActive(ctx) - if err != nil { - klog.Errorf("Error performing session check request to vSphere: %v", err) - } - if sessionActive { - return &session, nil - } - } - klog.Infof("No existing vCenter session found, creating new session") - - soapURL, err := soap.ParseURL(server) - if err != nil { - return nil, fmt.Errorf("error parsing vSphere URL %q: %w", server, err) - } - if soapURL == nil { - return nil, fmt.Errorf("error parsing vSphere URL %q", server) - } - - // Set user to nil there for prevent login during client creation. - // See https://github.com/vmware/govmomi/blob/master/client.go#L91 - soapURL.User = nil - client, err := newClientWithTimeout(ctx, soapURL, insecure, clientTimeout) - if err != nil { - return nil, fmt.Errorf("error setting up new vSphere SOAP client: %w", err) - } - // Set up user agent before login for being able to track mapi component in vcenter sessions list - client.UserAgent = "machineAPIvSphereProvider" - if err := client.Login(ctx, url.UserPassword(username, password)); err != nil { - return nil, fmt.Errorf("unable to login to vCenter: %w", err) - } - - session := Session{ - Client: client, - username: username, - password: password, - sessionKey: sessionKey, - } - - session.Finder = find.NewFinder(session.Client.Client, false) - - dc, err := session.Finder.DatacenterOrDefault(ctx, datacenter) - if err != nil { - return nil, fmt.Errorf("unable to find datacenter %q: %w", datacenter, err) - } - session.Datacenter = dc - session.Finder.SetDatacenter(dc) - - // Cache the session. - sessionCache[sessionKey] = session - - return &session, nil -} - -func (s *Session) FindVM(ctx context.Context, UUID, name string) (*object.VirtualMachine, error) { - if !isValidUUID(UUID) { - klog.V(3).Infof("Invalid UUID for VM %q: %s, trying to find by name", name, UUID) - return s.findVMByName(ctx, name) - } - klog.V(3).Infof("Find template by instance uuid: %s", UUID) - ref, err := s.FindRefByInstanceUUID(ctx, UUID) - if ref != nil && err == nil { - return object.NewVirtualMachine(s.Client.Client, ref.Reference()), nil - } - if err != nil { - klog.V(3).Infof("Instance not found by UUID: %s, trying to find by name %q", err, name) - } - return s.findVMByName(ctx, name) -} - -// FindByInstanceUUID finds an object by its instance UUID. -func (s *Session) FindRefByInstanceUUID(ctx context.Context, UUID string) (object.Reference, error) { - return s.findRefByUUID(ctx, UUID, true) -} - -func (s *Session) findRefByUUID(ctx context.Context, UUID string, findByInstanceUUID bool) (object.Reference, error) { - if s.Client == nil { - return nil, errors.New("vSphere client is not initialized") - } - si := object.NewSearchIndex(s.Client.Client) - ref, err := si.FindByUuid(ctx, s.Datacenter, UUID, true, &findByInstanceUUID) - if err != nil { - return nil, fmt.Errorf("error finding object by uuid %q: %w", UUID, err) - } - return ref, nil -} - -func (s *Session) findVMByName(ctx context.Context, ID string) (*object.VirtualMachine, error) { - tpl, err := s.Finder.VirtualMachine(ctx, ID) - if err != nil { - if isNotFound(err) { - return nil, err - } - return nil, fmt.Errorf("unable to find template by name %q: %w", ID, err) - } - return tpl, nil -} - -func isNotFound(err error) bool { - switch err.(type) { - case *find.NotFoundError: - return true - default: - return false - } -} - -func isValidUUID(str string) bool { - _, err := uuid.Parse(str) - return err == nil -} - -func (s *Session) GetTask(ctx context.Context, taskRef string) (*mo.Task, error) { - if taskRef == "" { - return nil, errors.New("taskRef can't be empty") - } - var obj mo.Task - moRef := types.ManagedObjectReference{ - Type: managedObjectTypeTask, - Value: taskRef, - } - if err := s.RetrieveOne(ctx, moRef, []string{"info"}, &obj); err != nil { - return nil, err - } - return &obj, nil -} - -func (s *Session) WithRestClient(ctx context.Context, f func(c *rest.Client) error) error { - c := rest.NewClient(s.Client.Client) - - user := url.UserPassword(s.username, s.password) - if err := c.Login(ctx, user); err != nil { - return err - } - - defer func() { - if err := c.Logout(ctx); err != nil { - klog.Errorf("Failed to logout: %v", err) - } - }() - - return f(c) -} - -func (s *Session) WithCachingTagsManager(ctx context.Context, f func(m *CachingTagsManager) error) error { - c := rest.NewClient(s.Client.Client) - - user := url.UserPassword(s.username, s.password) - if err := c.Login(ctx, user); err != nil { - return err - } - - defer func() { - if err := c.Logout(ctx); err != nil { - klog.Errorf("Failed to logout: %v", err) - } - }() - - m := newTagsCachingClient(tags.NewManager(c), s.sessionKey) - - return f(m) -} diff --git a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/session/tag_ids_caching_client.go b/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/session/tag_ids_caching_client.go deleted file mode 100644 index d6a053d0bca..00000000000 --- a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/session/tag_ids_caching_client.go +++ /dev/null @@ -1,241 +0,0 @@ -package session - -import ( - "context" - "fmt" - "net/http" - "strings" - "sync" - "time" - - "github.com/vmware/govmomi/vapi/tags" - - "k8s.io/klog/v2" -) - -const ( - defaultCacheTTL = time.Hour * 12 - - // special value for determine whenever tag or category was found in vCenter during last lookup - notFoundValue = "NOT_FOUND" - notFoundErrMessage = "404 Not Found" -) - -type cachedItem struct { - value string - expiresAt int64 -} - -func (ci cachedItem) expired() bool { - return time.Now().UnixNano() > ci.expiresAt -} - -type cacheMap struct { - internalMap sync.Map -} - -// Set value by the given key with default 12 hours lifetime -func (c *cacheMap) Set(key string, value string) { - c.SetWithTTL(key, value, defaultCacheTTL) -} - -// SetWithTTL sets value by given key with lifetime specified in ttl parameter -func (c *cacheMap) SetWithTTL(key string, value string, ttl time.Duration) { - c.internalMap.Store(key, cachedItem{value: value, expiresAt: time.Now().Add(ttl).UnixNano()}) -} - -// Get value by the given key. Second return parameter indicates whenever value found or not -func (c *cacheMap) Get(key string) (value string, ok bool) { - item, found := c.internalMap.Load(key) - if !found { - return "", false - } - if item.(cachedItem).expired() { - klog.V(4).Infof("cache item with name %s expired, invalidating", key) - c.Delete(key) - return "", false - } - return item.(cachedItem).value, true -} - -// Delete value by the given key -func (c *cacheMap) Delete(key string) { - c.internalMap.Delete(key) -} - -type tagsAndCategoriesCache struct { - tags cacheMap - categories cacheMap -} - -var sessionAnnotatedCache = map[string]*tagsAndCategoriesCache{} -var sessionCacheMU sync.Mutex - -func getOrCreateSessionCache(sessionKey string) *tagsAndCategoriesCache { - sessionCacheMU.Lock() - defer sessionCacheMU.Unlock() - - cache, found := sessionAnnotatedCache[sessionKey] - if !found { - cache = &tagsAndCategoriesCache{} - sessionAnnotatedCache[sessionKey] = cache - return cache - } - return cache -} - -// CachingTagsManager wraps tags.Manager from vSphere SDK for -// cache mapping between tags or categories name and their ids. -// Reasoning behind this is the implementation details of tags/categories lookup by name, -// to find a tag/category by name vSphere SDK gets a list of ids and then makes an additional request -// for every object till it will not find matched names. Such peculiarity causes a huge performance degradation -// within environments with a large number of tags/categories, because in the worst case number of rest requests for -// object lookup might be equal to the number of objects. -// -// See tags.Manager methods for more details: https://github.com/vmware/govmomi/blob/a2fb82dc55a8eb00932233aa8028ce97140df784/vapi/tags/tags.go#L172 -// -// This structure is intended to be used from a Session instance (Session.WithCachingTagsManager method specifically) presented in this module. -type CachingTagsManager struct { - *tags.Manager - - sessionKey string // different vCenters might have a different tags in it -} - -func newTagsCachingClient(tagsManager *tags.Manager, sessionKey string) *CachingTagsManager { - return &CachingTagsManager{ - tagsManager, sessionKey, - } -} - -// IsName returns true if the id is not an urn. -// this method came from vSphere sdk, -// see https://github.com/vmware/govmomi/blob/a2fb82dc55a8eb00932233aa8028ce97140df784/vapi/tags/tags.go#L121 for -// more context. -func IsName(id string) bool { - return !strings.HasPrefix(id, "urn:") -} - -// isObjectNotFoundErr checks if error message contains "Not Found" message. -// vSphere api client does not expose error type, so we can rely only on error message -func isObjectNotFoundErr(err error) bool { - return err != nil && strings.HasSuffix(err.Error(), http.StatusText(http.StatusNotFound)) -} - -// GetTag fetches the tag information for the given identifier. -// The id parameter can be a Tag ID or Tag Name. -// This method shadows original tags.Manager method and caches mapping between -// tag name and its id. In case ID was passed, the original method from tags.Manager would be used. -// -// In case if a tag was not found in vCenter, this would be cached for 12 hours and lookup won't happen till cache expiration. -func (t *CachingTagsManager) GetTag(ctx context.Context, id string) (*tags.Tag, error) { - if !IsName(id) { // if id is passed no cache check needed, use original GetTag method instantly - return t.Manager.GetTag(ctx, id) - } - - cache := getOrCreateSessionCache(t.sessionKey) - cachedTagID, found := cache.tags.Get(id) - if found { - klog.V(4).Infof("tag %s: found cached tag id value", id) - if cachedTagID == notFoundValue { - klog.V(4).Infof("tag %s: cache contains special value indicates that tag was not found when cache was filled, treating as non existed tag", id) - return nil, fmt.Errorf("%s", notFoundErrMessage) - } - - tag, err := t.Manager.GetTag(ctx, cachedTagID) - if err != nil { - if isObjectNotFoundErr(err) { - klog.V(3).Infof("tag %s: tag was not found in vCenter by cached id, invalidating cache", id) - // if tag not found, invalidate the cache and fallback to the default search method - cache.tags.Delete(id) - return t.Manager.GetTag(ctx, id) - } - return tag, err - } - return tag, nil - } - - klog.V(3).Infof("tag %s: tags cache miss, trying to find tag by name, it might take time", id) - tag, err := t.Manager.GetTag(ctx, id) - if err != nil { - if isObjectNotFoundErr(err) { - klog.V(3).Infof("tag %s not found in vCenter, caching", id) - // Caching the fact that tag was not found due to performance issues with lookup tag by name. - // In case when object does not exist amount of requests equals the amount of categories should happen, - // because of vCenter rest api design. - // For more context see vcenter rest api documentation and original method implementation: - // https://developer.vmware.com/apis/vsphere-automation/v7.0U1/cis/rest/com/vmware/cis/tagging/tag/idtag_id/get/ - // https://developer.vmware.com/apis/vsphere-automation/v7.0U1/cis/rest/com/vmware/cis/tagging/tag/get/ - // https://github.com/vmware/govmomi/blob/a2fb82dc55a8eb00932233aa8028ce97140df784/vapi/tags/tags.go#L121 - cache.tags.Set(id, notFoundValue) - } - return tag, err - } - cache.tags.Set(tag.Name, tag.ID) - return tag, err -} - -// GetCategory fetches the category information for the given identifier. -// The id parameter can be a Category ID or Category Name. -// This method shadows original tags.Manager method and caches mapping between -// tag name and its id. In case ID was passed, the original method from tags.Manager would be used. -// -// In case if a tag was not found in vCenter, this would be cached for 12 hours and lookup won't happen till cache expiration. -func (t *CachingTagsManager) GetCategory(ctx context.Context, id string) (*tags.Category, error) { - if !IsName(id) { // if id is passed no cache check needed, use original GetTag method instantly - return t.Manager.GetCategory(ctx, id) - } - - cache := getOrCreateSessionCache(t.sessionKey) - cachedCategoryID, found := cache.categories.Get(id) - if found { - klog.V(4).Infof("category %s: found cached category id value", id) - if cachedCategoryID == notFoundValue { - klog.V(4).Infof("category %s: cache contains special value indicates that tag was not found when cache was filled, treating as non existing category", id) - return nil, fmt.Errorf("%s", notFoundErrMessage) - } - - category, err := t.Manager.GetCategory(ctx, cachedCategoryID) - if err != nil { - if isObjectNotFoundErr(err) { - klog.V(3).Infof("category %s: category was not found in vCenter by cached id, invalidating id cache", id) - cache.categories.Delete(id) // if category not found, invalidate the cache and fallback to the default search method - return t.Manager.GetCategory(ctx, id) - } - return category, err - } - return category, nil - } - - klog.V(3).Infof("category %s: categories cache miss, trying to find category by name, it might take time", id) - category, err := t.Manager.GetCategory(ctx, id) - if err != nil { - if isObjectNotFoundErr(err) { - klog.V(3).Infof("category %s not found in vCenter, caching", id) - // Caching the fact that category was not found due to performance issues with lookup category by name. - // In case when object does not exist amount of requests equals the amount of categories should happen, - // because of vCenter rest api design. - // For more context see vcenter rest api documentation and original method implementation: - // https://developer.vmware.com/apis/vsphere-automation/v7.0U1/cis/rest/com/vmware/cis/tagging/category/idcategory_id/get/ - // https://developer.vmware.com/apis/vsphere-automation/v7.0U1/cis/rest/com/vmware/cis/tagging/category/get/ - // https://github.com/vmware/govmomi/blob/a2fb82dc55a8eb00932233aa8028ce97140df784/vapi/tags/categories.go#L122 - cache.categories.Set(id, notFoundValue) - } - return category, err - } - cache.categories.Set(category.Name, category.ID) - return category, err -} - -// ListTagsForCategory tag ids for the given category. -// The id parameter can be a Category ID or Category Name. -// Uses caching GetCategory method. -func (t *CachingTagsManager) ListTagsForCategory(ctx context.Context, id string) ([]string, error) { - if IsName(id) { - category, err := t.GetCategory(ctx, id) - if err != nil { - return nil, err - } - id = category.ID - } - return t.Manager.ListTagsForCategory(ctx, id) -} diff --git a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/util.go b/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/util.go deleted file mode 100644 index 8b7f3da8a51..00000000000 --- a/vendor/github.com/openshift/machine-api-operator/pkg/controller/vsphere/util.go +++ /dev/null @@ -1,280 +0,0 @@ -package vsphere - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - "strings" - - configv1 "github.com/openshift/api/config/v1" - machinev1 "github.com/openshift/api/machine/v1beta1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/runtime" - vsphere "k8s.io/cloud-provider-vsphere/pkg/common/config" - "k8s.io/klog/v2" - runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - globalInfrastuctureName = "cluster" - OpenshiftConfigNamespace = "openshift-config" -) - -// GetInfrastructure retrieves the Infrastructure object from the provided API reader. -// It returns an error if the API reader is nil or if there's an issue fetching the Infrastructure. -func GetInfrastructure(c runtimeclient.Reader) (*configv1.Infrastructure, error) { - if c == nil { - return nil, errors.New("no API reader -- will not fetch infrastructure config") - } - - infra := &configv1.Infrastructure{} - infraName := runtimeclient.ObjectKey{Name: globalInfrastuctureName} - - if err := c.Get(context.Background(), infraName, infra); err != nil { - return nil, err - } - - return infra, nil -} - -func getVSphereConfig(c runtimeclient.Reader, configNamespace string) (*vsphere.Config, error) { - if c == nil { - return nil, errors.New("no API reader -- will not fetch vSphere config") - } - - infra, err := GetInfrastructure(c) - if err != nil { - return nil, err - } - - if infra.Spec.CloudConfig.Name == "" { - return nil, fmt.Errorf("cluster infrastructure CloudConfig has empty name") - } - - if infra.Spec.CloudConfig.Key == "" { - return nil, fmt.Errorf("cluster infrastructure CloudConfig has empty key") - } - - cm := &corev1.ConfigMap{} - cmName := runtimeclient.ObjectKey{ - Name: infra.Spec.CloudConfig.Name, - Namespace: configNamespace, - } - - if err := c.Get(context.Background(), cmName, cm); err != nil { - return nil, err - } - - cloudConfig, found := cm.Data[infra.Spec.CloudConfig.Key] - if !found { - return nil, fmt.Errorf("cloud-config ConfigMap has no %q key", - infra.Spec.CloudConfig.Key, - ) - } - - return vsphere.ReadConfig([]byte(cloudConfig)) -} - -func setConditions(condition metav1.Condition, conditions []metav1.Condition) []metav1.Condition { - now := metav1.Now() - - if existingCondition := findCondition(conditions, condition.Type); existingCondition == nil { - condition.LastTransitionTime = now - conditions = append(conditions, condition) - } else { - updateExistingCondition(&condition, existingCondition) - } - - return conditions -} - -func findCondition(conditions []metav1.Condition, conditionType string) *metav1.Condition { - for i := range conditions { - if conditions[i].Type == conditionType { - return &conditions[i] - } - } - return nil -} - -func updateExistingCondition(newCondition, existingCondition *metav1.Condition) { - if !shouldUpdateCondition(newCondition, existingCondition) { - return - } - - if existingCondition.Status != newCondition.Status { - existingCondition.LastTransitionTime = metav1.Now() - } - existingCondition.Status = newCondition.Status - existingCondition.Reason = newCondition.Reason - existingCondition.Message = newCondition.Message -} - -func shouldUpdateCondition(newCondition, existingCondition *metav1.Condition) bool { - return newCondition.Reason != existingCondition.Reason || newCondition.Message != existingCondition.Message -} - -func conditionSuccess() metav1.Condition { - return metav1.Condition{ - Type: string(machinev1.MachineCreation), - Status: metav1.ConditionTrue, - Reason: machinev1.MachineCreationSucceededConditionReason, - Message: "Machine successfully created", - } -} - -func conditionFailed() metav1.Condition { - return metav1.Condition{ - Type: string(machinev1.MachineCreation), - Status: metav1.ConditionFalse, - Reason: machinev1.MachineCreationSucceededConditionReason, - } -} - -func getVCenterPortFromConfig(config *vsphere.Config, vcenter string) string { - if config != nil { - for _, vc := range config.VirtualCenter { - if vc.VCenterIP == vcenter { - if len(vc.VCenterPort) > 0 { - return vc.VCenterPort - } else { - return config.Global.VCenterPort - } - } - } - } - return "" -} - -func getVCenterInsecureFlagFromConfig(config *vsphere.Config, vcenter string) bool { - if config != nil { - for _, vc := range config.VirtualCenter { - if vc.VCenterIP == vcenter { - return vc.InsecureFlag - } - } - - // Either vCenter is not found or config is missing. - return config.Global.InsecureFlag - } - - return false -} - -// RawExtensionFromProviderSpec marshals the machine provider spec. -func RawExtensionFromProviderSpec(spec *machinev1.VSphereMachineProviderSpec) (*runtime.RawExtension, error) { - if spec == nil { - return &runtime.RawExtension{}, nil - } - - var rawBytes []byte - var err error - if rawBytes, err = json.Marshal(spec); err != nil { - return nil, fmt.Errorf("error marshalling providerSpec: %v", err) - } - - return &runtime.RawExtension{ - Raw: rawBytes, - }, nil -} - -// RawExtensionFromProviderStatus marshals the provider status -func RawExtensionFromProviderStatus(status *machinev1.VSphereMachineProviderStatus) (*runtime.RawExtension, error) { - if status == nil { - return &runtime.RawExtension{}, nil - } - - var rawBytes []byte - var err error - if rawBytes, err = json.Marshal(status); err != nil { - return nil, fmt.Errorf("error marshalling providerStatus: %v", err) - } - - return &runtime.RawExtension{ - Raw: rawBytes, - }, nil -} - -// ProviderSpecFromRawExtension unmarshals the JSON-encoded spec -func ProviderSpecFromRawExtension(rawExtension *runtime.RawExtension) (*machinev1.VSphereMachineProviderSpec, error) { - if rawExtension == nil { - return &machinev1.VSphereMachineProviderSpec{}, nil - } - - spec := new(machinev1.VSphereMachineProviderSpec) - if err := json.Unmarshal(rawExtension.Raw, &spec); err != nil { - return nil, fmt.Errorf("error unmarshalling providerSpec: %v", err) - } - - klog.V(5).Infof("Got provider spec from raw extension: %+v", spec) - return spec, nil -} - -// ProviderStatusFromRawExtension unmarshals a raw extension into a VSphereMachineProviderStatus type -func ProviderStatusFromRawExtension(rawExtension *runtime.RawExtension) (*machinev1.VSphereMachineProviderStatus, error) { - if rawExtension == nil { - return &machinev1.VSphereMachineProviderStatus{}, nil - } - - providerStatus := new(machinev1.VSphereMachineProviderStatus) - if err := json.Unmarshal(rawExtension.Raw, providerStatus); err != nil { - return nil, fmt.Errorf("error unmarshalling providerStatus: %v", err) - } - - klog.V(5).Infof("Got provider Status from raw extension: %+v", providerStatus) - return providerStatus, nil -} - -// isNotFoundErr checks if error message contains "Not Found" message. -// vSphere api client does not expose error type, so we can rely only on error message -func isNotFoundErr(err error) bool { - return err != nil && strings.HasSuffix(err.Error(), http.StatusText(http.StatusNotFound)) -} - -// podPredicate is a predicate function for filtering PodList -type podPredicate func(corev1.Pod) bool - -// isTerminating is a predicate for determine pods in 'Terminating' state -func isTerminating(p corev1.Pod) bool { - return p.DeletionTimestamp != nil -} - -// filterPods filters a podList and returns a PodList matching passed predicates -func filterPods(podList *corev1.PodList, predicates ...podPredicate) *corev1.PodList { - filteredPods := &corev1.PodList{} - for _, pod := range podList.Items { - var match = true - for _, p := range predicates { - if !p(pod) { - match = false - break - } - } - if match { - filteredPods.Items = append(filteredPods.Items, pod) - } - } - - return filteredPods -} - -// getPodList returns pod list on a given node matching pod predicates -func getPodList(ctx context.Context, apiReader runtimeclient.Reader, n *corev1.Node, filters []podPredicate) (*corev1.PodList, error) { - fieldSelector, err := fields.ParseSelector("spec.nodeName=" + n.Name) - if err != nil { - return nil, err - } - - allPods := &corev1.PodList{} - if err := apiReader.List(ctx, allPods, &runtimeclient.ListOptions{ - FieldSelector: fieldSelector, - }); err != nil { - return nil, err - } - - return filterPods(allPods, filters...), nil -} diff --git a/vendor/github.com/openshift/machine-api-operator/pkg/util/ipam/util.go b/vendor/github.com/openshift/machine-api-operator/pkg/util/ipam/util.go deleted file mode 100644 index 81a616e15cd..00000000000 --- a/vendor/github.com/openshift/machine-api-operator/pkg/util/ipam/util.go +++ /dev/null @@ -1,231 +0,0 @@ -package ipam - -import ( - "context" - "fmt" - - machinev1 "github.com/openshift/api/machine/v1beta1" - ipamv1beta1 "sigs.k8s.io/cluster-api/api/ipam/v1beta1" //nolint:staticcheck - - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// EnsureIPAddressClaim ensures an IPAddressClaim exists for the given claim name and pool -func EnsureIPAddressClaim( - ctx context.Context, - runtimeClient client.Client, - claimName string, - machine *machinev1.Machine, - pool machinev1.AddressesFromPool) (*ipamv1beta1.IPAddressClaim, error) { - claimKey := client.ObjectKey{ - Namespace: machine.Namespace, - Name: claimName, - } - ipAddressClaim := &ipamv1beta1.IPAddressClaim{} - if err := runtimeClient.Get(ctx, claimKey, ipAddressClaim); err == nil { - // If we found a claim, make sure it has owner field set. - if len(ipAddressClaim.OwnerReferences) == 0 { - klog.Infof("IPAddressClaim %s is missing owner field. Updating to reference machine.", claimName) - _ = AdoptOrphanClaim(ctx, runtimeClient, claimName, machine, ipAddressClaim) - } - return ipAddressClaim, nil - } else if !apierrors.IsNotFound(err) { - return nil, fmt.Errorf("error while getting IP address claim: %w", err) - } - - klog.Infof("creating IPAddressClaim %s", claimName) - gv := machinev1.SchemeGroupVersion - machineRef := metav1.NewControllerRef(machine, gv.WithKind("Machine")) - ipAddressClaim = &ipamv1beta1.IPAddressClaim{ - ObjectMeta: metav1.ObjectMeta{ - OwnerReferences: []metav1.OwnerReference{ - *machineRef, - }, - Finalizers: []string{ - machinev1.IPClaimProtectionFinalizer, - }, - Name: claimName, - Namespace: machine.Namespace, - }, - Spec: ipamv1beta1.IPAddressClaimSpec{ - PoolRef: corev1.TypedLocalObjectReference{ - APIGroup: &pool.Group, - Kind: pool.Resource, - Name: pool.Name, - }, - }, - } - if err := runtimeClient.Create(ctx, ipAddressClaim); err != nil { - return nil, fmt.Errorf("unable to create IPAddressClaim: %w", err) - } - return ipAddressClaim, nil -} - -// AdoptOrphanClaim updates the IPAddressClaim to belong to the specified machine. -func AdoptOrphanClaim( - ctx context.Context, - runtimeClient client.Client, - claimName string, - machine *machinev1.Machine, - ipAddressClaim *ipamv1beta1.IPAddressClaim) error { - gv := machinev1.SchemeGroupVersion - machineRef := metav1.NewControllerRef(machine, gv.WithKind("Machine")) - ipAddressClaim.OwnerReferences = []metav1.OwnerReference{ - *machineRef, - } - if err := runtimeClient.Update(ctx, ipAddressClaim); err != nil { - klog.Warningf("Unable to adopt orphaned IPAddressClaim %v: %v", claimName, err) - return err - } - return nil -} - -// CountOutstandingIPAddressClaimsForMachine determines the number of outstanding IP address claims a machine is waiting -// to be fulfilled. -func CountOutstandingIPAddressClaimsForMachine( - ctx context.Context, - runtimeClient client.Client, - ipAddressClaims []ipamv1beta1.IPAddressClaim) int { - fulfilledClaimCount := 0 - - for _, claim := range ipAddressClaims { - if len(claim.Status.AddressRef.Name) > 0 { - fulfilledClaimCount++ - } - } - - return len(ipAddressClaims) - fulfilledClaimCount -} - -// RetrieveBoundIPAddress retrieves the IPAddress which is bound to the named IPAddressClaim -func RetrieveBoundIPAddress( - ctx context.Context, - runtimeClient client.Client, - machine *machinev1.Machine, - claimName string) (*ipamv1beta1.IPAddress, error) { - - claimKey := client.ObjectKey{ - Namespace: machine.Namespace, - Name: claimName, - } - ipAddressClaim := &ipamv1beta1.IPAddressClaim{} - if err := runtimeClient.Get(ctx, claimKey, ipAddressClaim); err != nil { - return nil, fmt.Errorf("unable to get IPAddressClaim: %w", err) - } - - if len(ipAddressClaim.Status.AddressRef.Name) == 0 { - return nil, fmt.Errorf("no IPAddress is bound to claim %s", claimName) - } - - ipAddress := &ipamv1beta1.IPAddress{} - addressKey := client.ObjectKey{ - Namespace: machine.Namespace, - Name: ipAddressClaim.Status.AddressRef.Name, - } - if err := runtimeClient.Get(ctx, addressKey, ipAddress); err != nil { - return nil, fmt.Errorf("unable to get IPAddress: %w", err) - } - return ipAddress, nil -} - -func RemoveFinalizersForIPAddressClaims( - ctx context.Context, - runtimeClient client.Client, - machine machinev1.Machine) error { - ipAddressClaimList := &ipamv1beta1.IPAddressClaimList{} - if err := runtimeClient.List(ctx, ipAddressClaimList, client.InNamespace(machine.Namespace)); err != nil { - return fmt.Errorf("unable to list IPAddressClaims: %w", err) - } - - for _, ipAddressClaim := range ipAddressClaimList.Items { - if metav1.IsControlledBy(&ipAddressClaim, &machine) { - finalizers := sets.NewString(ipAddressClaim.ObjectMeta.Finalizers...) - if !finalizers.Has(machinev1.IPClaimProtectionFinalizer) { - continue - } - finalizers.Delete(machinev1.IPClaimProtectionFinalizer) - ipAddressClaim.ObjectMeta.Finalizers = finalizers.List() - if err := runtimeClient.Update(ctx, &ipAddressClaim); err != nil { - return fmt.Errorf("unable to update IPAddressClaim: %w", err) - } - } - } - return nil -} - -// HasStaticIPConfiguration returns true if the specified machine has an address pool -func HasStaticIPConfiguration(providerSpec *machinev1.VSphereMachineProviderSpec) bool { - for _, device := range providerSpec.Network.Devices { - if len(device.Gateway) > 0 || - len(device.IPAddrs) > 0 || - len(device.Nameservers) > 0 || - len(device.AddressesFromPools) > 0 { - return true - } - } - return false -} - -// GetIPAddressClaimName returns a consistently named claim name -func GetIPAddressClaimName(machine *machinev1.Machine, deviceIdx int, poolIdx int) string { - return fmt.Sprintf("%s-claim-%d-%d", machine.Name, deviceIdx, poolIdx) -} - -// HasOutstandingIPAddressClaims checks to see if a given machine has outstanding IP address claims -func HasOutstandingIPAddressClaims( - ctx context.Context, - runtimeClient client.Client, - machine *machinev1.Machine, - networkDevices []machinev1.NetworkDeviceSpec, -) (int, error) { - var associatedClaims []ipamv1beta1.IPAddressClaim - for deviceIdx, networkDevice := range networkDevices { - for poolIdx, addressPool := range networkDevice.AddressesFromPools { - claimName := GetIPAddressClaimName(machine, deviceIdx, poolIdx) - claim, err := EnsureIPAddressClaim( - ctx, - runtimeClient, - claimName, - machine, - addressPool) - if err != nil { - return -1, fmt.Errorf("error while ensuring IPAddressClaim exists: %w", err) - } - associatedClaims = append(associatedClaims, *claim) - } - } - - if len(associatedClaims) > 0 { - outstandingClaims := CountOutstandingIPAddressClaimsForMachine(ctx, runtimeClient, associatedClaims) - return outstandingClaims, nil - } - return 0, nil -} - -// VerifyIPAddressOwners verifies that each IPAddress associated with the machine has the owner set. -func VerifyIPAddressOwners( - ctx context.Context, - runtimeClient client.Client, - machine *machinev1.Machine, - networkDevices []machinev1.NetworkDeviceSpec) error { - for deviceIdx, networkDevice := range networkDevices { - for poolIdx, addressPool := range networkDevice.AddressesFromPools { - claimName := GetIPAddressClaimName(machine, deviceIdx, poolIdx) - _, err := EnsureIPAddressClaim( - ctx, - runtimeClient, - claimName, - machine, - addressPool) - if err != nil { - return fmt.Errorf("error while ensuring IPAddressClaim exists: %w", err) - } - } - } - return nil -} diff --git a/vendor/gopkg.in/gcfg.v1/LICENSE b/vendor/gopkg.in/gcfg.v1/LICENSE deleted file mode 100644 index 87a5cede339..00000000000 --- a/vendor/gopkg.in/gcfg.v1/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go -Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/gopkg.in/gcfg.v1/README b/vendor/gopkg.in/gcfg.v1/README deleted file mode 100644 index 1ff233a529d..00000000000 --- a/vendor/gopkg.in/gcfg.v1/README +++ /dev/null @@ -1,4 +0,0 @@ -Gcfg reads INI-style configuration files into Go structs; -supports user-defined types and subsections. - -Package docs: https://godoc.org/gopkg.in/gcfg.v1 diff --git a/vendor/gopkg.in/gcfg.v1/doc.go b/vendor/gopkg.in/gcfg.v1/doc.go deleted file mode 100644 index 32f3e9d69c4..00000000000 --- a/vendor/gopkg.in/gcfg.v1/doc.go +++ /dev/null @@ -1,145 +0,0 @@ -// Package gcfg reads "INI-style" text-based configuration files with -// "name=value" pairs grouped into sections (gcfg files). -// -// This package is still a work in progress; see the sections below for planned -// changes. -// -// Syntax -// -// The syntax is based on that used by git config: -// http://git-scm.com/docs/git-config#_syntax . -// There are some (planned) differences compared to the git config format: -// - improve data portability: -// - must be encoded in UTF-8 (for now) and must not contain the 0 byte -// - include and "path" type is not supported -// (path type may be implementable as a user-defined type) -// - internationalization -// - section and variable names can contain unicode letters, unicode digits -// (as defined in http://golang.org/ref/spec#Characters ) and hyphens -// (U+002D), starting with a unicode letter -// - disallow potentially ambiguous or misleading definitions: -// - `[sec.sub]` format is not allowed (deprecated in gitconfig) -// - `[sec ""]` is not allowed -// - use `[sec]` for section name "sec" and empty subsection name -// - (planned) within a single file, definitions must be contiguous for each: -// - section: '[secA]' -> '[secB]' -> '[secA]' is an error -// - subsection: '[sec "A"]' -> '[sec "B"]' -> '[sec "A"]' is an error -// - multivalued variable: 'multi=a' -> 'other=x' -> 'multi=b' is an error -// -// Data structure -// -// The functions in this package read values into a user-defined struct. -// Each section corresponds to a struct field in the config struct, and each -// variable in a section corresponds to a data field in the section struct. -// The mapping of each section or variable name to fields is done either based -// on the "gcfg" struct tag or by matching the name of the section or variable, -// ignoring case. In the latter case, hyphens '-' in section and variable names -// correspond to underscores '_' in field names. -// Fields must be exported; to use a section or variable name starting with a -// letter that is neither upper- or lower-case, prefix the field name with 'X'. -// (See https://code.google.com/p/go/issues/detail?id=5763#c4 .) -// -// For sections with subsections, the corresponding field in config must be a -// map, rather than a struct, with string keys and pointer-to-struct values. -// Values for subsection variables are stored in the map with the subsection -// name used as the map key. -// (Note that unlike section and variable names, subsection names are case -// sensitive.) -// When using a map, and there is a section with the same section name but -// without a subsection name, its values are stored with the empty string used -// as the key. -// It is possible to provide default values for subsections in the section -// "default-" (or by setting values in the corresponding struct -// field "Default_"). -// -// The functions in this package panic if config is not a pointer to a struct, -// or when a field is not of a suitable type (either a struct or a map with -// string keys and pointer-to-struct values). -// -// Parsing of values -// -// The section structs in the config struct may contain single-valued or -// multi-valued variables. Variables of unnamed slice type (that is, a type -// starting with `[]`) are treated as multi-value; all others (including named -// slice types) are treated as single-valued variables. -// -// Single-valued variables are handled based on the type as follows. -// Unnamed pointer types (that is, types starting with `*`) are dereferenced, -// and if necessary, a new instance is allocated. -// -// For types implementing the encoding.TextUnmarshaler interface, the -// UnmarshalText method is used to set the value. Implementing this method is -// the recommended way for parsing user-defined types. -// -// For fields of string kind, the value string is assigned to the field, after -// unquoting and unescaping as needed. -// For fields of bool kind, the field is set to true if the value is "true", -// "yes", "on" or "1", and set to false if the value is "false", "no", "off" or -// "0", ignoring case. In addition, single-valued bool fields can be specified -// with a "blank" value (variable name without equals sign and value); in such -// case the value is set to true. -// -// Predefined integer types [u]int(|8|16|32|64) and big.Int are parsed as -// decimal or hexadecimal (if having '0x' prefix). (This is to prevent -// unintuitively handling zero-padded numbers as octal.) Other types having -// [u]int* as the underlying type, such as os.FileMode and uintptr allow -// decimal, hexadecimal, or octal values. -// Parsing mode for integer types can be overridden using the struct tag option -// ",int=mode" where mode is a combination of the 'd', 'h', and 'o' characters -// (each standing for decimal, hexadecimal, and octal, respectively.) -// -// All other types are parsed using fmt.Sscanf with the "%v" verb. -// -// For multi-valued variables, each individual value is parsed as above and -// appended to the slice. If the first value is specified as a "blank" value -// (variable name without equals sign and value), a new slice is allocated; -// that is any values previously set in the slice will be ignored. -// -// The types subpackage for provides helpers for parsing "enum-like" and integer -// types. -// -// Error handling -// -// There are 3 types of errors: -// -// - programmer errors / panics: -// - invalid configuration structure -// - data errors: -// - fatal errors: -// - invalid configuration syntax -// - warnings: -// - data that doesn't belong to any part of the config structure -// -// Programmer errors trigger panics. These are should be fixed by the programmer -// before releasing code that uses gcfg. -// -// Data errors cause gcfg to return a non-nil error value. This includes the -// case when there are extra unknown key-value definitions in the configuration -// data (extra data). -// However, in some occasions it is desirable to be able to proceed in -// situations when the only data error is that of extra data. -// These errors are handled at a different (warning) priority and can be -// filtered out programmatically. To ignore extra data warnings, wrap the -// gcfg.Read*Into invocation into a call to gcfg.FatalOnly. -// -// TODO -// -// The following is a list of changes under consideration: -// - documentation -// - self-contained syntax documentation -// - more practical examples -// - move TODOs to issue tracker (eventually) -// - syntax -// - reconsider valid escape sequences -// (gitconfig doesn't support \r in value, \t in subsection name, etc.) -// - reading / parsing gcfg files -// - define internal representation structure -// - support multiple inputs (readers, strings, files) -// - support declaring encoding (?) -// - support varying fields sets for subsections (?) -// - writing gcfg files -// - error handling -// - make error context accessible programmatically? -// - limit input size? -// -package gcfg // import "gopkg.in/gcfg.v1" diff --git a/vendor/gopkg.in/gcfg.v1/errors.go b/vendor/gopkg.in/gcfg.v1/errors.go deleted file mode 100644 index 83a591dac74..00000000000 --- a/vendor/gopkg.in/gcfg.v1/errors.go +++ /dev/null @@ -1,57 +0,0 @@ -package gcfg - -import warnings "gopkg.in/warnings.v0" - -// FatalOnly filters the results of a Read*Into invocation and returns only -// fatal errors. That is, errors (warnings) indicating data for unknown -// sections / variables is ignored. Example invocation: -// -// err := gcfg.FatalOnly(gcfg.ReadFileInto(&cfg, configFile)) -// if err != nil { -// ... -// -func FatalOnly(err error) error { - return warnings.FatalOnly(err) -} - -func isFatal(err error) bool { - _, ok := err.(extraData) - return !ok -} - -type loc struct { - section string - subsection *string - variable *string -} - -type extraData struct { - loc -} - -type locErr struct { - msg string - loc -} - -func (l loc) String() string { - s := "section \"" + l.section + "\"" - if l.subsection != nil { - s += ", subsection \"" + *l.subsection + "\"" - } - if l.variable != nil { - s += ", variable \"" + *l.variable + "\"" - } - return s -} - -func (e extraData) Error() string { - return "can't store data at " + e.loc.String() -} - -func (e locErr) Error() string { - return e.msg + " at " + e.loc.String() -} - -var _ error = extraData{} -var _ error = locErr{} diff --git a/vendor/gopkg.in/gcfg.v1/read.go b/vendor/gopkg.in/gcfg.v1/read.go deleted file mode 100644 index 06796653cc2..00000000000 --- a/vendor/gopkg.in/gcfg.v1/read.go +++ /dev/null @@ -1,257 +0,0 @@ -package gcfg - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - - "gopkg.in/gcfg.v1/scanner" - "gopkg.in/gcfg.v1/token" - "gopkg.in/warnings.v0" -) - -var unescape = map[rune]rune{'\\': '\\', '"': '"', 'n': '\n', 't': '\t'} -var utf8Bom = []byte("\ufeff") - -// no error: invalid literals should be caught by scanner -func unquote(s string) string { - u, q, esc := make([]rune, 0, len(s)), false, false - for _, c := range s { - if esc { - uc, ok := unescape[c] - switch { - case ok: - u = append(u, uc) - fallthrough - case !q && c == '\n': - esc = false - continue - } - panic("invalid escape sequence") - } - switch c { - case '"': - q = !q - case '\\': - esc = true - default: - u = append(u, c) - } - } - if q { - panic("missing end quote") - } - if esc { - panic("invalid escape sequence") - } - return string(u) -} - -func readIntoPass(c *warnings.Collector, config interface{}, fset *token.FileSet, - file *token.File, src []byte, subsectPass bool) error { - // - var s scanner.Scanner - var errs scanner.ErrorList - s.Init(file, src, func(p token.Position, m string) { errs.Add(p, m) }, 0) - sect, sectsub := "", "" - pos, tok, lit := s.Scan() - errfn := func(msg string) error { - return fmt.Errorf("%s: %s", fset.Position(pos), msg) - } - for { - if errs.Len() > 0 { - if err := c.Collect(errs.Err()); err != nil { - return err - } - } - switch tok { - case token.EOF: - return nil - case token.EOL, token.COMMENT: - pos, tok, lit = s.Scan() - case token.LBRACK: - pos, tok, lit = s.Scan() - if errs.Len() > 0 { - if err := c.Collect(errs.Err()); err != nil { - return err - } - } - if tok != token.IDENT { - if err := c.Collect(errfn("expected section name")); err != nil { - return err - } - } - sect, sectsub = lit, "" - pos, tok, lit = s.Scan() - if errs.Len() > 0 { - if err := c.Collect(errs.Err()); err != nil { - return err - } - } - if tok == token.STRING { - sectsub = unquote(lit) - if sectsub == "" { - if err := c.Collect(errfn("empty subsection name")); err != nil { - return err - } - } - pos, tok, lit = s.Scan() - if errs.Len() > 0 { - if err := c.Collect(errs.Err()); err != nil { - return err - } - } - } - if tok != token.RBRACK { - if sectsub == "" { - if err := c.Collect(errfn("expected subsection name or right bracket")); err != nil { - return err - } - } - if err := c.Collect(errfn("expected right bracket")); err != nil { - return err - } - } - pos, tok, lit = s.Scan() - if tok != token.EOL && tok != token.EOF && tok != token.COMMENT { - if err := c.Collect(errfn("expected EOL, EOF, or comment")); err != nil { - return err - } - } - // If a section/subsection header was found, ensure a - // container object is created, even if there are no - // variables further down. - err := c.Collect(set(c, config, sect, sectsub, "", true, "", subsectPass)) - if err != nil { - return err - } - case token.IDENT: - if sect == "" { - if err := c.Collect(errfn("expected section header")); err != nil { - return err - } - } - n := lit - pos, tok, lit = s.Scan() - if errs.Len() > 0 { - return errs.Err() - } - blank, v := tok == token.EOF || tok == token.EOL || tok == token.COMMENT, "" - if !blank { - if tok != token.ASSIGN { - if err := c.Collect(errfn("expected '='")); err != nil { - return err - } - } - pos, tok, lit = s.Scan() - if errs.Len() > 0 { - if err := c.Collect(errs.Err()); err != nil { - return err - } - } - if tok != token.STRING { - if err := c.Collect(errfn("expected value")); err != nil { - return err - } - } - v = unquote(lit) - pos, tok, lit = s.Scan() - if errs.Len() > 0 { - if err := c.Collect(errs.Err()); err != nil { - return err - } - } - if tok != token.EOL && tok != token.EOF && tok != token.COMMENT { - if err := c.Collect(errfn("expected EOL, EOF, or comment")); err != nil { - return err - } - } - } - err := set(c, config, sect, sectsub, n, blank, v, subsectPass) - if err != nil { - return err - } - default: - if sect == "" { - if err := c.Collect(errfn("expected section header")); err != nil { - return err - } - } - if err := c.Collect(errfn("expected section header or variable declaration")); err != nil { - return err - } - } - } -} - -func readInto(config interface{}, fset *token.FileSet, file *token.File, - src []byte) error { - // - c := warnings.NewCollector(isFatal) - err := readIntoPass(c, config, fset, file, src, false) - if err != nil { - return err - } - err = readIntoPass(c, config, fset, file, src, true) - if err != nil { - return err - } - return c.Done() -} - -// ReadInto reads gcfg formatted data from reader and sets the values into the -// corresponding fields in config. -func ReadInto(config interface{}, reader io.Reader) error { - src, err := ioutil.ReadAll(reader) - if err != nil { - return err - } - fset := token.NewFileSet() - file := fset.AddFile("", fset.Base(), len(src)) - return readInto(config, fset, file, src) -} - -// ReadStringInto reads gcfg formatted data from str and sets the values into -// the corresponding fields in config. -func ReadStringInto(config interface{}, str string) error { - r := strings.NewReader(str) - return ReadInto(config, r) -} - -// ReadFileInto reads gcfg formatted data from the file filename and sets the -// values into the corresponding fields in config. -// -// For compatibility with files created on Windows, the ReadFileInto skips a -// single leading UTF8 BOM sequence if it exists. -func ReadFileInto(config interface{}, filename string) error { - f, err := os.Open(filename) - if err != nil { - return err - } - defer f.Close() - src, err := ioutil.ReadAll(f) - if err != nil { - return err - } - - // Skips a single leading UTF8 BOM sequence if it exists. - src = skipLeadingUtf8Bom(src) - - fset := token.NewFileSet() - file := fset.AddFile(filename, fset.Base(), len(src)) - return readInto(config, fset, file, src) -} - -func skipLeadingUtf8Bom(src []byte) []byte { - lengthUtf8Bom := len(utf8Bom) - - if len(src) >= lengthUtf8Bom { - if bytes.Equal(src[:lengthUtf8Bom], utf8Bom) { - return src[lengthUtf8Bom:] - } - } - return src -} diff --git a/vendor/gopkg.in/gcfg.v1/scanner/errors.go b/vendor/gopkg.in/gcfg.v1/scanner/errors.go deleted file mode 100644 index 1a3c0f6563c..00000000000 --- a/vendor/gopkg.in/gcfg.v1/scanner/errors.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package scanner - -import ( - "fmt" - "io" - "sort" -) - -import ( - "gopkg.in/gcfg.v1/token" -) - -// In an ErrorList, an error is represented by an *Error. -// The position Pos, if valid, points to the beginning of -// the offending token, and the error condition is described -// by Msg. -// -type Error struct { - Pos token.Position - Msg string -} - -// Error implements the error interface. -func (e Error) Error() string { - if e.Pos.Filename != "" || e.Pos.IsValid() { - // don't print "" - // TODO(gri) reconsider the semantics of Position.IsValid - return e.Pos.String() + ": " + e.Msg - } - return e.Msg -} - -// ErrorList is a list of *Errors. -// The zero value for an ErrorList is an empty ErrorList ready to use. -// -type ErrorList []*Error - -// Add adds an Error with given position and error message to an ErrorList. -func (p *ErrorList) Add(pos token.Position, msg string) { - *p = append(*p, &Error{pos, msg}) -} - -// Reset resets an ErrorList to no errors. -func (p *ErrorList) Reset() { *p = (*p)[0:0] } - -// ErrorList implements the sort Interface. -func (p ErrorList) Len() int { return len(p) } -func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -func (p ErrorList) Less(i, j int) bool { - e := &p[i].Pos - f := &p[j].Pos - if e.Filename < f.Filename { - return true - } - if e.Filename == f.Filename { - return e.Offset < f.Offset - } - return false -} - -// Sort sorts an ErrorList. *Error entries are sorted by position, -// other errors are sorted by error message, and before any *Error -// entry. -// -func (p ErrorList) Sort() { - sort.Sort(p) -} - -// RemoveMultiples sorts an ErrorList and removes all but the first error per line. -func (p *ErrorList) RemoveMultiples() { - sort.Sort(p) - var last token.Position // initial last.Line is != any legal error line - i := 0 - for _, e := range *p { - if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line { - last = e.Pos - (*p)[i] = e - i++ - } - } - (*p) = (*p)[0:i] -} - -// An ErrorList implements the error interface. -func (p ErrorList) Error() string { - switch len(p) { - case 0: - return "no errors" - case 1: - return p[0].Error() - } - return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) -} - -// Err returns an error equivalent to this error list. -// If the list is empty, Err returns nil. -func (p ErrorList) Err() error { - if len(p) == 0 { - return nil - } - return p -} - -// PrintError is a utility function that prints a list of errors to w, -// one error per line, if the err parameter is an ErrorList. Otherwise -// it prints the err string. -// -func PrintError(w io.Writer, err error) { - if list, ok := err.(ErrorList); ok { - for _, e := range list { - fmt.Fprintf(w, "%s\n", e) - } - } else if err != nil { - fmt.Fprintf(w, "%s\n", err) - } -} diff --git a/vendor/gopkg.in/gcfg.v1/scanner/scanner.go b/vendor/gopkg.in/gcfg.v1/scanner/scanner.go deleted file mode 100644 index 6d0eee916cc..00000000000 --- a/vendor/gopkg.in/gcfg.v1/scanner/scanner.go +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package scanner implements a scanner for gcfg configuration text. -// It takes a []byte as source which can then be tokenized -// through repeated calls to the Scan method. -// -// Note that the API for the scanner package may change to accommodate new -// features or implementation changes in gcfg. -// -package scanner - -import ( - "fmt" - "path/filepath" - "unicode" - "unicode/utf8" -) - -import ( - "gopkg.in/gcfg.v1/token" -) - -// An ErrorHandler may be provided to Scanner.Init. If a syntax error is -// encountered and a handler was installed, the handler is called with a -// position and an error message. The position points to the beginning of -// the offending token. -// -type ErrorHandler func(pos token.Position, msg string) - -// A Scanner holds the scanner's internal state while processing -// a given text. It can be allocated as part of another data -// structure but must be initialized via Init before use. -// -type Scanner struct { - // immutable state - file *token.File // source file handle - dir string // directory portion of file.Name() - src []byte // source - err ErrorHandler // error reporting; or nil - mode Mode // scanning mode - - // scanning state - ch rune // current character - offset int // character offset - rdOffset int // reading offset (position after current character) - lineOffset int // current line offset - nextVal bool // next token is expected to be a value - - // public state - ok to modify - ErrorCount int // number of errors encountered -} - -// Read the next Unicode char into s.ch. -// s.ch < 0 means end-of-file. -// -func (s *Scanner) next() { - if s.rdOffset < len(s.src) { - s.offset = s.rdOffset - if s.ch == '\n' { - s.lineOffset = s.offset - s.file.AddLine(s.offset) - } - r, w := rune(s.src[s.rdOffset]), 1 - switch { - case r == 0: - s.error(s.offset, "illegal character NUL") - case r >= 0x80: - // not ASCII - r, w = utf8.DecodeRune(s.src[s.rdOffset:]) - if r == utf8.RuneError && w == 1 { - s.error(s.offset, "illegal UTF-8 encoding") - } - } - s.rdOffset += w - s.ch = r - } else { - s.offset = len(s.src) - if s.ch == '\n' { - s.lineOffset = s.offset - s.file.AddLine(s.offset) - } - s.ch = -1 // eof - } -} - -// A mode value is a set of flags (or 0). -// They control scanner behavior. -// -type Mode uint - -const ( - ScanComments Mode = 1 << iota // return comments as COMMENT tokens -) - -// Init prepares the scanner s to tokenize the text src by setting the -// scanner at the beginning of src. The scanner uses the file set file -// for position information and it adds line information for each line. -// It is ok to re-use the same file when re-scanning the same file as -// line information which is already present is ignored. Init causes a -// panic if the file size does not match the src size. -// -// Calls to Scan will invoke the error handler err if they encounter a -// syntax error and err is not nil. Also, for each error encountered, -// the Scanner field ErrorCount is incremented by one. The mode parameter -// determines how comments are handled. -// -// Note that Init may call err if there is an error in the first character -// of the file. -// -func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) { - // Explicitly initialize all fields since a scanner may be reused. - if file.Size() != len(src) { - panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src))) - } - s.file = file - s.dir, _ = filepath.Split(file.Name()) - s.src = src - s.err = err - s.mode = mode - - s.ch = ' ' - s.offset = 0 - s.rdOffset = 0 - s.lineOffset = 0 - s.ErrorCount = 0 - s.nextVal = false - - s.next() -} - -func (s *Scanner) error(offs int, msg string) { - if s.err != nil { - s.err(s.file.Position(s.file.Pos(offs)), msg) - } - s.ErrorCount++ -} - -func (s *Scanner) scanComment() string { - // initial [;#] already consumed - offs := s.offset - 1 // position of initial [;#] - - for s.ch != '\n' && s.ch >= 0 { - s.next() - } - return string(s.src[offs:s.offset]) -} - -func isLetter(ch rune) bool { - return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch >= 0x80 && unicode.IsLetter(ch) -} - -func isDigit(ch rune) bool { - return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) -} - -func (s *Scanner) scanIdentifier() string { - offs := s.offset - for isLetter(s.ch) || isDigit(s.ch) || s.ch == '-' { - s.next() - } - return string(s.src[offs:s.offset]) -} - -func (s *Scanner) scanEscape(val bool) { - offs := s.offset - ch := s.ch - s.next() // always make progress - switch ch { - case '\\', '"': - // ok - case 'n', 't': - if val { - break // ok - } - fallthrough - default: - s.error(offs, "unknown escape sequence") - } -} - -func (s *Scanner) scanString() string { - // '"' opening already consumed - offs := s.offset - 1 - - for s.ch != '"' { - ch := s.ch - s.next() - if ch == '\n' || ch < 0 { - s.error(offs, "string not terminated") - break - } - if ch == '\\' { - s.scanEscape(false) - } - } - - s.next() - - return string(s.src[offs:s.offset]) -} - -func stripCR(b []byte) []byte { - c := make([]byte, len(b)) - i := 0 - for _, ch := range b { - if ch != '\r' { - c[i] = ch - i++ - } - } - return c[:i] -} - -func (s *Scanner) scanValString() string { - offs := s.offset - - hasCR := false - end := offs - inQuote := false -loop: - for inQuote || s.ch >= 0 && s.ch != '\n' && s.ch != ';' && s.ch != '#' { - ch := s.ch - s.next() - switch { - case inQuote && ch == '\\': - s.scanEscape(true) - case !inQuote && ch == '\\': - if s.ch == '\r' { - hasCR = true - s.next() - } - if s.ch != '\n' && s.ch != '"' { - s.error(offs, "unquoted '\\' must be followed by new line or double quote") - break loop - } - s.next() - case ch == '"': - inQuote = !inQuote - case ch == '\r': - hasCR = true - case ch < 0 || inQuote && ch == '\n': - s.error(offs, "string not terminated") - break loop - } - if inQuote || !isWhiteSpace(ch) { - end = s.offset - } - } - - lit := s.src[offs:end] - if hasCR { - lit = stripCR(lit) - } - - return string(lit) -} - -func isWhiteSpace(ch rune) bool { - return ch == ' ' || ch == '\t' || ch == '\r' -} - -func (s *Scanner) skipWhitespace() { - for isWhiteSpace(s.ch) { - s.next() - } -} - -// Scan scans the next token and returns the token position, the token, -// and its literal string if applicable. The source end is indicated by -// token.EOF. -// -// If the returned token is a literal (token.IDENT, token.STRING) or -// token.COMMENT, the literal string has the corresponding value. -// -// If the returned token is token.ILLEGAL, the literal string is the -// offending character. -// -// In all other cases, Scan returns an empty literal string. -// -// For more tolerant parsing, Scan will return a valid token if -// possible even if a syntax error was encountered. Thus, even -// if the resulting token sequence contains no illegal tokens, -// a client may not assume that no error occurred. Instead it -// must check the scanner's ErrorCount or the number of calls -// of the error handler, if there was one installed. -// -// Scan adds line information to the file added to the file -// set with Init. Token positions are relative to that file -// and thus relative to the file set. -// -func (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) { -scanAgain: - s.skipWhitespace() - - // current token start - pos = s.file.Pos(s.offset) - - // determine token value - switch ch := s.ch; { - case s.nextVal: - lit = s.scanValString() - tok = token.STRING - s.nextVal = false - case isLetter(ch): - lit = s.scanIdentifier() - tok = token.IDENT - default: - s.next() // always make progress - switch ch { - case -1: - tok = token.EOF - case '\n': - tok = token.EOL - case '"': - tok = token.STRING - lit = s.scanString() - case '[': - tok = token.LBRACK - case ']': - tok = token.RBRACK - case ';', '#': - // comment - lit = s.scanComment() - if s.mode&ScanComments == 0 { - // skip comment - goto scanAgain - } - tok = token.COMMENT - case '=': - tok = token.ASSIGN - s.nextVal = true - default: - s.error(s.file.Offset(pos), fmt.Sprintf("illegal character %#U", ch)) - tok = token.ILLEGAL - lit = string(ch) - } - } - - return -} diff --git a/vendor/gopkg.in/gcfg.v1/set.go b/vendor/gopkg.in/gcfg.v1/set.go deleted file mode 100644 index 73aee500357..00000000000 --- a/vendor/gopkg.in/gcfg.v1/set.go +++ /dev/null @@ -1,329 +0,0 @@ -package gcfg - -import ( - "bytes" - "encoding" - "encoding/gob" - "fmt" - "math/big" - "reflect" - "strings" - "unicode" - "unicode/utf8" - - "gopkg.in/gcfg.v1/types" - "gopkg.in/warnings.v0" -) - -type tag struct { - ident string - intMode string -} - -func newTag(ts string) tag { - t := tag{} - s := strings.Split(ts, ",") - t.ident = s[0] - for _, tse := range s[1:] { - if strings.HasPrefix(tse, "int=") { - t.intMode = tse[len("int="):] - } - } - return t -} - -func fieldFold(v reflect.Value, name string) (reflect.Value, tag) { - var n string - r0, _ := utf8.DecodeRuneInString(name) - if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) { - n = "X" - } - n += strings.Replace(name, "-", "_", -1) - f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool { - if !v.FieldByName(fieldName).CanSet() { - return false - } - f, _ := v.Type().FieldByName(fieldName) - t := newTag(f.Tag.Get("gcfg")) - if t.ident != "" { - return strings.EqualFold(t.ident, name) - } - return strings.EqualFold(n, fieldName) - }) - if !ok { - return reflect.Value{}, tag{} - } - return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg")) -} - -type setter func(destp interface{}, blank bool, val string, t tag) error - -var errUnsupportedType = fmt.Errorf("unsupported type") -var errBlankUnsupported = fmt.Errorf("blank value not supported for type") - -var setters = []setter{ - typeSetter, textUnmarshalerSetter, kindSetter, scanSetter, -} - -func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error { - dtu, ok := d.(encoding.TextUnmarshaler) - if !ok { - return errUnsupportedType - } - if blank { - return errBlankUnsupported - } - return dtu.UnmarshalText([]byte(val)) -} - -func boolSetter(d interface{}, blank bool, val string, t tag) error { - if blank { - reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true)) - return nil - } - b, err := types.ParseBool(val) - if err == nil { - reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b)) - } - return err -} - -func intMode(mode string) types.IntMode { - var m types.IntMode - if strings.ContainsAny(mode, "dD") { - m |= types.Dec - } - if strings.ContainsAny(mode, "hH") { - m |= types.Hex - } - if strings.ContainsAny(mode, "oO") { - m |= types.Oct - } - return m -} - -var typeModes = map[reflect.Type]types.IntMode{ - reflect.TypeOf(int(0)): types.Dec | types.Hex, - reflect.TypeOf(int8(0)): types.Dec | types.Hex, - reflect.TypeOf(int16(0)): types.Dec | types.Hex, - reflect.TypeOf(int32(0)): types.Dec | types.Hex, - reflect.TypeOf(int64(0)): types.Dec | types.Hex, - reflect.TypeOf(uint(0)): types.Dec | types.Hex, - reflect.TypeOf(uint8(0)): types.Dec | types.Hex, - reflect.TypeOf(uint16(0)): types.Dec | types.Hex, - reflect.TypeOf(uint32(0)): types.Dec | types.Hex, - reflect.TypeOf(uint64(0)): types.Dec | types.Hex, - // use default mode (allow dec/hex/oct) for uintptr type - reflect.TypeOf(big.Int{}): types.Dec | types.Hex, -} - -func intModeDefault(t reflect.Type) types.IntMode { - m, ok := typeModes[t] - if !ok { - m = types.Dec | types.Hex | types.Oct - } - return m -} - -func intSetter(d interface{}, blank bool, val string, t tag) error { - if blank { - return errBlankUnsupported - } - mode := intMode(t.intMode) - if mode == 0 { - mode = intModeDefault(reflect.TypeOf(d).Elem()) - } - return types.ParseInt(d, val, mode) -} - -func stringSetter(d interface{}, blank bool, val string, t tag) error { - if blank { - return errBlankUnsupported - } - dsp, ok := d.(*string) - if !ok { - return errUnsupportedType - } - *dsp = val - return nil -} - -var kindSetters = map[reflect.Kind]setter{ - reflect.String: stringSetter, - reflect.Bool: boolSetter, - reflect.Int: intSetter, - reflect.Int8: intSetter, - reflect.Int16: intSetter, - reflect.Int32: intSetter, - reflect.Int64: intSetter, - reflect.Uint: intSetter, - reflect.Uint8: intSetter, - reflect.Uint16: intSetter, - reflect.Uint32: intSetter, - reflect.Uint64: intSetter, - reflect.Uintptr: intSetter, -} - -var typeSetters = map[reflect.Type]setter{ - reflect.TypeOf(big.Int{}): intSetter, -} - -func typeSetter(d interface{}, blank bool, val string, tt tag) error { - t := reflect.ValueOf(d).Type().Elem() - setter, ok := typeSetters[t] - if !ok { - return errUnsupportedType - } - return setter(d, blank, val, tt) -} - -func kindSetter(d interface{}, blank bool, val string, tt tag) error { - k := reflect.ValueOf(d).Type().Elem().Kind() - setter, ok := kindSetters[k] - if !ok { - return errUnsupportedType - } - return setter(d, blank, val, tt) -} - -func scanSetter(d interface{}, blank bool, val string, tt tag) error { - if blank { - return errBlankUnsupported - } - return types.ScanFully(d, val, 'v') -} - -func newValue(c *warnings.Collector, sect string, vCfg reflect.Value, - vType reflect.Type) (reflect.Value, error) { - // - pv := reflect.New(vType) - dfltName := "default-" + sect - dfltField, _ := fieldFold(vCfg, dfltName) - var err error - if dfltField.IsValid() { - b := bytes.NewBuffer(nil) - ge := gob.NewEncoder(b) - if err = c.Collect(ge.EncodeValue(dfltField)); err != nil { - return pv, err - } - gd := gob.NewDecoder(bytes.NewReader(b.Bytes())) - if err = c.Collect(gd.DecodeValue(pv.Elem())); err != nil { - return pv, err - } - } - return pv, nil -} - -func set(c *warnings.Collector, cfg interface{}, sect, sub, name string, - blank bool, value string, subsectPass bool) error { - // - vPCfg := reflect.ValueOf(cfg) - if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct { - panic(fmt.Errorf("config must be a pointer to a struct")) - } - vCfg := vPCfg.Elem() - vSect, _ := fieldFold(vCfg, sect) - l := loc{section: sect} - if !vSect.IsValid() { - err := extraData{loc: l} - return c.Collect(err) - } - isSubsect := vSect.Kind() == reflect.Map - if subsectPass != isSubsect { - return nil - } - if isSubsect { - l.subsection = &sub - vst := vSect.Type() - if vst.Key().Kind() != reflect.String || - vst.Elem().Kind() != reflect.Ptr || - vst.Elem().Elem().Kind() != reflect.Struct { - panic(fmt.Errorf("map field for section must have string keys and "+ - " pointer-to-struct values: section %q", sect)) - } - if vSect.IsNil() { - vSect.Set(reflect.MakeMap(vst)) - } - k := reflect.ValueOf(sub) - pv := vSect.MapIndex(k) - if !pv.IsValid() { - vType := vSect.Type().Elem().Elem() - var err error - if pv, err = newValue(c, sect, vCfg, vType); err != nil { - return err - } - vSect.SetMapIndex(k, pv) - } - vSect = pv.Elem() - } else if vSect.Kind() != reflect.Struct { - panic(fmt.Errorf("field for section must be a map or a struct: "+ - "section %q", sect)) - } else if sub != "" { - return c.Collect(extraData{loc: l}) - } - // Empty name is a special value, meaning that only the - // section/subsection object is to be created, with no values set. - if name == "" { - return nil - } - vVar, t := fieldFold(vSect, name) - l.variable = &name - if !vVar.IsValid() { - return c.Collect(extraData{loc: l}) - } - // vVal is either single-valued var, or newly allocated value within multi-valued var - var vVal reflect.Value - // multi-value if unnamed slice type - isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice || - vVar.Type().Name() == "" && vVar.Kind() == reflect.Ptr && vVar.Type().Elem().Name() == "" && vVar.Type().Elem().Kind() == reflect.Slice - if isMulti && vVar.Kind() == reflect.Ptr { - if vVar.IsNil() { - vVar.Set(reflect.New(vVar.Type().Elem())) - } - vVar = vVar.Elem() - } - if isMulti && blank { - vVar.Set(reflect.Zero(vVar.Type())) - return nil - } - if isMulti { - vVal = reflect.New(vVar.Type().Elem()).Elem() - } else { - vVal = vVar - } - isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr - isNew := isDeref && vVal.IsNil() - // vAddr is address of value to set (dereferenced & allocated as needed) - var vAddr reflect.Value - switch { - case isNew: - vAddr = reflect.New(vVal.Type().Elem()) - case isDeref && !isNew: - vAddr = vVal - default: - vAddr = vVal.Addr() - } - vAddrI := vAddr.Interface() - err, ok := error(nil), false - for _, s := range setters { - err = s(vAddrI, blank, value, t) - if err == nil { - ok = true - break - } - if err != errUnsupportedType { - return locErr{msg: err.Error(), loc: l} - } - } - if !ok { - // in case all setters returned errUnsupportedType - return locErr{msg: err.Error(), loc: l} - } - if isNew { // set reference if it was dereferenced and newly allocated - vVal.Set(vAddr) - } - if isMulti { // append if multi-valued - vVar.Set(reflect.Append(vVar, vVal)) - } - return nil -} diff --git a/vendor/gopkg.in/gcfg.v1/token/position.go b/vendor/gopkg.in/gcfg.v1/token/position.go deleted file mode 100644 index fc45c1e7693..00000000000 --- a/vendor/gopkg.in/gcfg.v1/token/position.go +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// TODO(gri) consider making this a separate package outside the go directory. - -package token - -import ( - "fmt" - "sort" - "sync" -) - -// ----------------------------------------------------------------------------- -// Positions - -// Position describes an arbitrary source position -// including the file, line, and column location. -// A Position is valid if the line number is > 0. -// -type Position struct { - Filename string // filename, if any - Offset int // offset, starting at 0 - Line int // line number, starting at 1 - Column int // column number, starting at 1 (character count) -} - -// IsValid returns true if the position is valid. -func (pos *Position) IsValid() bool { return pos.Line > 0 } - -// String returns a string in one of several forms: -// -// file:line:column valid position with file name -// line:column valid position without file name -// file invalid position with file name -// - invalid position without file name -// -func (pos Position) String() string { - s := pos.Filename - if pos.IsValid() { - if s != "" { - s += ":" - } - s += fmt.Sprintf("%d:%d", pos.Line, pos.Column) - } - if s == "" { - s = "-" - } - return s -} - -// Pos is a compact encoding of a source position within a file set. -// It can be converted into a Position for a more convenient, but much -// larger, representation. -// -// The Pos value for a given file is a number in the range [base, base+size], -// where base and size are specified when adding the file to the file set via -// AddFile. -// -// To create the Pos value for a specific source offset, first add -// the respective file to the current file set (via FileSet.AddFile) -// and then call File.Pos(offset) for that file. Given a Pos value p -// for a specific file set fset, the corresponding Position value is -// obtained by calling fset.Position(p). -// -// Pos values can be compared directly with the usual comparison operators: -// If two Pos values p and q are in the same file, comparing p and q is -// equivalent to comparing the respective source file offsets. If p and q -// are in different files, p < q is true if the file implied by p was added -// to the respective file set before the file implied by q. -// -type Pos int - -// The zero value for Pos is NoPos; there is no file and line information -// associated with it, and NoPos().IsValid() is false. NoPos is always -// smaller than any other Pos value. The corresponding Position value -// for NoPos is the zero value for Position. -// -const NoPos Pos = 0 - -// IsValid returns true if the position is valid. -func (p Pos) IsValid() bool { - return p != NoPos -} - -// ----------------------------------------------------------------------------- -// File - -// A File is a handle for a file belonging to a FileSet. -// A File has a name, size, and line offset table. -// -type File struct { - set *FileSet - name string // file name as provided to AddFile - base int // Pos value range for this file is [base...base+size] - size int // file size as provided to AddFile - - // lines and infos are protected by set.mutex - lines []int - infos []lineInfo -} - -// Name returns the file name of file f as registered with AddFile. -func (f *File) Name() string { - return f.name -} - -// Base returns the base offset of file f as registered with AddFile. -func (f *File) Base() int { - return f.base -} - -// Size returns the size of file f as registered with AddFile. -func (f *File) Size() int { - return f.size -} - -// LineCount returns the number of lines in file f. -func (f *File) LineCount() int { - f.set.mutex.RLock() - n := len(f.lines) - f.set.mutex.RUnlock() - return n -} - -// AddLine adds the line offset for a new line. -// The line offset must be larger than the offset for the previous line -// and smaller than the file size; otherwise the line offset is ignored. -// -func (f *File) AddLine(offset int) { - f.set.mutex.Lock() - if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size { - f.lines = append(f.lines, offset) - } - f.set.mutex.Unlock() -} - -// SetLines sets the line offsets for a file and returns true if successful. -// The line offsets are the offsets of the first character of each line; -// for instance for the content "ab\nc\n" the line offsets are {0, 3}. -// An empty file has an empty line offset table. -// Each line offset must be larger than the offset for the previous line -// and smaller than the file size; otherwise SetLines fails and returns -// false. -// -func (f *File) SetLines(lines []int) bool { - // verify validity of lines table - size := f.size - for i, offset := range lines { - if i > 0 && offset <= lines[i-1] || size <= offset { - return false - } - } - - // set lines table - f.set.mutex.Lock() - f.lines = lines - f.set.mutex.Unlock() - return true -} - -// SetLinesForContent sets the line offsets for the given file content. -func (f *File) SetLinesForContent(content []byte) { - var lines []int - line := 0 - for offset, b := range content { - if line >= 0 { - lines = append(lines, line) - } - line = -1 - if b == '\n' { - line = offset + 1 - } - } - - // set lines table - f.set.mutex.Lock() - f.lines = lines - f.set.mutex.Unlock() -} - -// A lineInfo object describes alternative file and line number -// information (such as provided via a //line comment in a .go -// file) for a given file offset. -type lineInfo struct { - // fields are exported to make them accessible to gob - Offset int - Filename string - Line int -} - -// AddLineInfo adds alternative file and line number information for -// a given file offset. The offset must be larger than the offset for -// the previously added alternative line info and smaller than the -// file size; otherwise the information is ignored. -// -// AddLineInfo is typically used to register alternative position -// information for //line filename:line comments in source files. -// -func (f *File) AddLineInfo(offset int, filename string, line int) { - f.set.mutex.Lock() - if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size { - f.infos = append(f.infos, lineInfo{offset, filename, line}) - } - f.set.mutex.Unlock() -} - -// Pos returns the Pos value for the given file offset; -// the offset must be <= f.Size(). -// f.Pos(f.Offset(p)) == p. -// -func (f *File) Pos(offset int) Pos { - if offset > f.size { - panic("illegal file offset") - } - return Pos(f.base + offset) -} - -// Offset returns the offset for the given file position p; -// p must be a valid Pos value in that file. -// f.Offset(f.Pos(offset)) == offset. -// -func (f *File) Offset(p Pos) int { - if int(p) < f.base || int(p) > f.base+f.size { - panic("illegal Pos value") - } - return int(p) - f.base -} - -// Line returns the line number for the given file position p; -// p must be a Pos value in that file or NoPos. -// -func (f *File) Line(p Pos) int { - // TODO(gri) this can be implemented much more efficiently - return f.Position(p).Line -} - -func searchLineInfos(a []lineInfo, x int) int { - return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1 -} - -// info returns the file name, line, and column number for a file offset. -func (f *File) info(offset int) (filename string, line, column int) { - filename = f.name - if i := searchInts(f.lines, offset); i >= 0 { - line, column = i+1, offset-f.lines[i]+1 - } - if len(f.infos) > 0 { - // almost no files have extra line infos - if i := searchLineInfos(f.infos, offset); i >= 0 { - alt := &f.infos[i] - filename = alt.Filename - if i := searchInts(f.lines, alt.Offset); i >= 0 { - line += alt.Line - i - 1 - } - } - } - return -} - -func (f *File) position(p Pos) (pos Position) { - offset := int(p) - f.base - pos.Offset = offset - pos.Filename, pos.Line, pos.Column = f.info(offset) - return -} - -// Position returns the Position value for the given file position p; -// p must be a Pos value in that file or NoPos. -// -func (f *File) Position(p Pos) (pos Position) { - if p != NoPos { - if int(p) < f.base || int(p) > f.base+f.size { - panic("illegal Pos value") - } - pos = f.position(p) - } - return -} - -// ----------------------------------------------------------------------------- -// FileSet - -// A FileSet represents a set of source files. -// Methods of file sets are synchronized; multiple goroutines -// may invoke them concurrently. -// -type FileSet struct { - mutex sync.RWMutex // protects the file set - base int // base offset for the next file - files []*File // list of files in the order added to the set - last *File // cache of last file looked up -} - -// NewFileSet creates a new file set. -func NewFileSet() *FileSet { - s := new(FileSet) - s.base = 1 // 0 == NoPos - return s -} - -// Base returns the minimum base offset that must be provided to -// AddFile when adding the next file. -// -func (s *FileSet) Base() int { - s.mutex.RLock() - b := s.base - s.mutex.RUnlock() - return b - -} - -// AddFile adds a new file with a given filename, base offset, and file size -// to the file set s and returns the file. Multiple files may have the same -// name. The base offset must not be smaller than the FileSet's Base(), and -// size must not be negative. -// -// Adding the file will set the file set's Base() value to base + size + 1 -// as the minimum base value for the next file. The following relationship -// exists between a Pos value p for a given file offset offs: -// -// int(p) = base + offs -// -// with offs in the range [0, size] and thus p in the range [base, base+size]. -// For convenience, File.Pos may be used to create file-specific position -// values from a file offset. -// -func (s *FileSet) AddFile(filename string, base, size int) *File { - s.mutex.Lock() - defer s.mutex.Unlock() - if base < s.base || size < 0 { - panic("illegal base or size") - } - // base >= s.base && size >= 0 - f := &File{s, filename, base, size, []int{0}, nil} - base += size + 1 // +1 because EOF also has a position - if base < 0 { - panic("token.Pos offset overflow (> 2G of source code in file set)") - } - // add the file to the file set - s.base = base - s.files = append(s.files, f) - s.last = f - return f -} - -// Iterate calls f for the files in the file set in the order they were added -// until f returns false. -// -func (s *FileSet) Iterate(f func(*File) bool) { - for i := 0; ; i++ { - var file *File - s.mutex.RLock() - if i < len(s.files) { - file = s.files[i] - } - s.mutex.RUnlock() - if file == nil || !f(file) { - break - } - } -} - -func searchFiles(a []*File, x int) int { - return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1 -} - -func (s *FileSet) file(p Pos) *File { - // common case: p is in last file - if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size { - return f - } - // p is not in last file - search all files - if i := searchFiles(s.files, int(p)); i >= 0 { - f := s.files[i] - // f.base <= int(p) by definition of searchFiles - if int(p) <= f.base+f.size { - s.last = f - return f - } - } - return nil -} - -// File returns the file that contains the position p. -// If no such file is found (for instance for p == NoPos), -// the result is nil. -// -func (s *FileSet) File(p Pos) (f *File) { - if p != NoPos { - s.mutex.RLock() - f = s.file(p) - s.mutex.RUnlock() - } - return -} - -// Position converts a Pos in the fileset into a general Position. -func (s *FileSet) Position(p Pos) (pos Position) { - if p != NoPos { - s.mutex.RLock() - if f := s.file(p); f != nil { - pos = f.position(p) - } - s.mutex.RUnlock() - } - return -} - -// ----------------------------------------------------------------------------- -// Helper functions - -func searchInts(a []int, x int) int { - // This function body is a manually inlined version of: - // - // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 - // - // With better compiler optimizations, this may not be needed in the - // future, but at the moment this change improves the go/printer - // benchmark performance by ~30%. This has a direct impact on the - // speed of gofmt and thus seems worthwhile (2011-04-29). - // TODO(gri): Remove this when compilers have caught up. - i, j := 0, len(a) - for i < j { - h := i + (j-i)/2 // avoid overflow when computing h - // i ≤ h < j - if a[h] <= x { - i = h + 1 - } else { - j = h - } - } - return i - 1 -} diff --git a/vendor/gopkg.in/gcfg.v1/token/serialize.go b/vendor/gopkg.in/gcfg.v1/token/serialize.go deleted file mode 100644 index 4adc8f9e334..00000000000 --- a/vendor/gopkg.in/gcfg.v1/token/serialize.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package token - -type serializedFile struct { - // fields correspond 1:1 to fields with same (lower-case) name in File - Name string - Base int - Size int - Lines []int - Infos []lineInfo -} - -type serializedFileSet struct { - Base int - Files []serializedFile -} - -// Read calls decode to deserialize a file set into s; s must not be nil. -func (s *FileSet) Read(decode func(interface{}) error) error { - var ss serializedFileSet - if err := decode(&ss); err != nil { - return err - } - - s.mutex.Lock() - s.base = ss.Base - files := make([]*File, len(ss.Files)) - for i := 0; i < len(ss.Files); i++ { - f := &ss.Files[i] - files[i] = &File{s, f.Name, f.Base, f.Size, f.Lines, f.Infos} - } - s.files = files - s.last = nil - s.mutex.Unlock() - - return nil -} - -// Write calls encode to serialize the file set s. -func (s *FileSet) Write(encode func(interface{}) error) error { - var ss serializedFileSet - - s.mutex.Lock() - ss.Base = s.base - files := make([]serializedFile, len(s.files)) - for i, f := range s.files { - files[i] = serializedFile{f.name, f.base, f.size, f.lines, f.infos} - } - ss.Files = files - s.mutex.Unlock() - - return encode(ss) -} diff --git a/vendor/gopkg.in/gcfg.v1/token/token.go b/vendor/gopkg.in/gcfg.v1/token/token.go deleted file mode 100644 index b3c7c83fa9e..00000000000 --- a/vendor/gopkg.in/gcfg.v1/token/token.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package token defines constants representing the lexical tokens of the gcfg -// configuration syntax and basic operations on tokens (printing, predicates). -// -// Note that the API for the token package may change to accommodate new -// features or implementation changes in gcfg. -// -package token - -import "strconv" - -// Token is the set of lexical tokens of the gcfg configuration syntax. -type Token int - -// The list of tokens. -const ( - // Special tokens - ILLEGAL Token = iota - EOF - COMMENT - - literal_beg - // Identifiers and basic type literals - // (these tokens stand for classes of literals) - IDENT // section-name, variable-name - STRING // "subsection-name", variable value - literal_end - - operator_beg - // Operators and delimiters - ASSIGN // = - LBRACK // [ - RBRACK // ] - EOL // \n - operator_end -) - -var tokens = [...]string{ - ILLEGAL: "ILLEGAL", - - EOF: "EOF", - COMMENT: "COMMENT", - - IDENT: "IDENT", - STRING: "STRING", - - ASSIGN: "=", - LBRACK: "[", - RBRACK: "]", - EOL: "\n", -} - -// String returns the string corresponding to the token tok. -// For operators and delimiters, the string is the actual token character -// sequence (e.g., for the token ASSIGN, the string is "="). For all other -// tokens the string corresponds to the token constant name (e.g. for the -// token IDENT, the string is "IDENT"). -// -func (tok Token) String() string { - s := "" - if 0 <= tok && tok < Token(len(tokens)) { - s = tokens[tok] - } - if s == "" { - s = "token(" + strconv.Itoa(int(tok)) + ")" - } - return s -} - -// Predicates - -// IsLiteral returns true for tokens corresponding to identifiers -// and basic type literals; it returns false otherwise. -// -func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end } - -// IsOperator returns true for tokens corresponding to operators and -// delimiters; it returns false otherwise. -// -func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end } diff --git a/vendor/gopkg.in/gcfg.v1/types/bool.go b/vendor/gopkg.in/gcfg.v1/types/bool.go deleted file mode 100644 index 8dcae0d8cfd..00000000000 --- a/vendor/gopkg.in/gcfg.v1/types/bool.go +++ /dev/null @@ -1,23 +0,0 @@ -package types - -// BoolValues defines the name and value mappings for ParseBool. -var BoolValues = map[string]interface{}{ - "true": true, "yes": true, "on": true, "1": true, - "false": false, "no": false, "off": false, "0": false, -} - -var boolParser = func() *EnumParser { - ep := &EnumParser{} - ep.AddVals(BoolValues) - return ep -}() - -// ParseBool parses bool values according to the definitions in BoolValues. -// Parsing is case-insensitive. -func ParseBool(s string) (bool, error) { - v, err := boolParser.Parse(s) - if err != nil { - return false, err - } - return v.(bool), nil -} diff --git a/vendor/gopkg.in/gcfg.v1/types/doc.go b/vendor/gopkg.in/gcfg.v1/types/doc.go deleted file mode 100644 index 9f9c345f6ea..00000000000 --- a/vendor/gopkg.in/gcfg.v1/types/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package types defines helpers for type conversions. -// -// The API for this package is not finalized yet. -package types diff --git a/vendor/gopkg.in/gcfg.v1/types/enum.go b/vendor/gopkg.in/gcfg.v1/types/enum.go deleted file mode 100644 index 1a0c7ef453d..00000000000 --- a/vendor/gopkg.in/gcfg.v1/types/enum.go +++ /dev/null @@ -1,44 +0,0 @@ -package types - -import ( - "fmt" - "reflect" - "strings" -) - -// EnumParser parses "enum" values; i.e. a predefined set of strings to -// predefined values. -type EnumParser struct { - Type string // type name; if not set, use type of first value added - CaseMatch bool // if true, matching of strings is case-sensitive - // PrefixMatch bool - vals map[string]interface{} -} - -// AddVals adds strings and values to an EnumParser. -func (ep *EnumParser) AddVals(vals map[string]interface{}) { - if ep.vals == nil { - ep.vals = make(map[string]interface{}) - } - for k, v := range vals { - if ep.Type == "" { - ep.Type = reflect.TypeOf(v).Name() - } - if !ep.CaseMatch { - k = strings.ToLower(k) - } - ep.vals[k] = v - } -} - -// Parse parses the string and returns the value or an error. -func (ep EnumParser) Parse(s string) (interface{}, error) { - if !ep.CaseMatch { - s = strings.ToLower(s) - } - v, ok := ep.vals[s] - if !ok { - return false, fmt.Errorf("failed to parse %s %#q", ep.Type, s) - } - return v, nil -} diff --git a/vendor/gopkg.in/gcfg.v1/types/int.go b/vendor/gopkg.in/gcfg.v1/types/int.go deleted file mode 100644 index af7e75c1250..00000000000 --- a/vendor/gopkg.in/gcfg.v1/types/int.go +++ /dev/null @@ -1,86 +0,0 @@ -package types - -import ( - "fmt" - "strings" -) - -// An IntMode is a mode for parsing integer values, representing a set of -// accepted bases. -type IntMode uint8 - -// IntMode values for ParseInt; can be combined using binary or. -const ( - Dec IntMode = 1 << iota - Hex - Oct -) - -// String returns a string representation of IntMode; e.g. `IntMode(Dec|Hex)`. -func (m IntMode) String() string { - var modes []string - if m&Dec != 0 { - modes = append(modes, "Dec") - } - if m&Hex != 0 { - modes = append(modes, "Hex") - } - if m&Oct != 0 { - modes = append(modes, "Oct") - } - return "IntMode(" + strings.Join(modes, "|") + ")" -} - -var errIntAmbig = fmt.Errorf("ambiguous integer value; must include '0' prefix") - -func prefix0(val string) bool { - return strings.HasPrefix(val, "0") || strings.HasPrefix(val, "-0") -} - -func prefix0x(val string) bool { - return strings.HasPrefix(val, "0x") || strings.HasPrefix(val, "-0x") -} - -// ParseInt parses val using mode into intptr, which must be a pointer to an -// integer kind type. Non-decimal value require prefix `0` or `0x` in the cases -// when mode permits ambiguity of base; otherwise the prefix can be omitted. -func ParseInt(intptr interface{}, val string, mode IntMode) error { - val = strings.TrimSpace(val) - verb := byte(0) - switch mode { - case Dec: - verb = 'd' - case Dec + Hex: - if prefix0x(val) { - verb = 'v' - } else { - verb = 'd' - } - case Dec + Oct: - if prefix0(val) && !prefix0x(val) { - verb = 'v' - } else { - verb = 'd' - } - case Dec + Hex + Oct: - verb = 'v' - case Hex: - if prefix0x(val) { - verb = 'v' - } else { - verb = 'x' - } - case Oct: - verb = 'o' - case Hex + Oct: - if prefix0(val) { - verb = 'v' - } else { - return errIntAmbig - } - } - if verb == 0 { - panic("unsupported mode") - } - return ScanFully(intptr, val, verb) -} diff --git a/vendor/gopkg.in/gcfg.v1/types/scan.go b/vendor/gopkg.in/gcfg.v1/types/scan.go deleted file mode 100644 index db2f6ed3caf..00000000000 --- a/vendor/gopkg.in/gcfg.v1/types/scan.go +++ /dev/null @@ -1,23 +0,0 @@ -package types - -import ( - "fmt" - "io" - "reflect" -) - -// ScanFully uses fmt.Sscanf with verb to fully scan val into ptr. -func ScanFully(ptr interface{}, val string, verb byte) error { - t := reflect.ValueOf(ptr).Elem().Type() - // attempt to read extra bytes to make sure the value is consumed - var b []byte - n, err := fmt.Sscanf(val, "%"+string(verb)+"%s", ptr, &b) - switch { - case n < 1 || n == 1 && err != io.EOF: - return fmt.Errorf("failed to parse %q as %v: %v", val, t, err) - case n > 1: - return fmt.Errorf("failed to parse %q as %v: extra characters %q", val, t, string(b)) - } - // n == 1 && err == io.EOF - return nil -} diff --git a/vendor/gopkg.in/warnings.v0/LICENSE b/vendor/gopkg.in/warnings.v0/LICENSE deleted file mode 100644 index d65f7e9d8cd..00000000000 --- a/vendor/gopkg.in/warnings.v0/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2016 Péter Surányi. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/gopkg.in/warnings.v0/README b/vendor/gopkg.in/warnings.v0/README deleted file mode 100644 index 974212ba1b9..00000000000 --- a/vendor/gopkg.in/warnings.v0/README +++ /dev/null @@ -1,77 +0,0 @@ -Package warnings implements error handling with non-fatal errors (warnings). - -import path: "gopkg.in/warnings.v0" -package docs: https://godoc.org/gopkg.in/warnings.v0 -issues: https://github.com/go-warnings/warnings/issues -pull requests: https://github.com/go-warnings/warnings/pulls - -A recurring pattern in Go programming is the following: - - func myfunc(params) error { - if err := doSomething(...); err != nil { - return err - } - if err := doSomethingElse(...); err != nil { - return err - } - if ok := doAnotherThing(...); !ok { - return errors.New("my error") - } - ... - return nil - } - -This pattern allows interrupting the flow on any received error. But what if -there are errors that should be noted but still not fatal, for which the flow -should not be interrupted? Implementing such logic at each if statement would -make the code complex and the flow much harder to follow. - -Package warnings provides the Collector type and a clean and simple pattern -for achieving such logic. The Collector takes care of deciding when to break -the flow and when to continue, collecting any non-fatal errors (warnings) -along the way. The only requirement is that fatal and non-fatal errors can be -distinguished programmatically; that is a function such as - - IsFatal(error) bool - -must be implemented. The following is an example of what the above snippet -could look like using the warnings package: - - import "gopkg.in/warnings.v0" - - func isFatal(err error) bool { - _, ok := err.(WarningType) - return !ok - } - - func myfunc(params) error { - c := warnings.NewCollector(isFatal) - c.FatalWithWarnings = true - if err := c.Collect(doSomething()); err != nil { - return err - } - if err := c.Collect(doSomethingElse(...)); err != nil { - return err - } - if ok := doAnotherThing(...); !ok { - if err := c.Collect(errors.New("my error")); err != nil { - return err - } - } - ... - return c.Done() - } - -For an example of a non-trivial code base using this library, see -gopkg.in/gcfg.v1 - -Rules for using warnings - - - ensure that warnings are programmatically distinguishable from fatal - errors (i.e. implement an isFatal function and any necessary error types) - - ensure that there is a single Collector instance for a call of each - exported function - - ensure that all errors (fatal or warning) are fed through Collect - - ensure that every time an error is returned, it is one returned by a - Collector (from Collect or Done) - - ensure that Collect is never called after Done diff --git a/vendor/gopkg.in/warnings.v0/warnings.go b/vendor/gopkg.in/warnings.v0/warnings.go deleted file mode 100644 index b849d1e3d9a..00000000000 --- a/vendor/gopkg.in/warnings.v0/warnings.go +++ /dev/null @@ -1,194 +0,0 @@ -// Package warnings implements error handling with non-fatal errors (warnings). -// -// A recurring pattern in Go programming is the following: -// -// func myfunc(params) error { -// if err := doSomething(...); err != nil { -// return err -// } -// if err := doSomethingElse(...); err != nil { -// return err -// } -// if ok := doAnotherThing(...); !ok { -// return errors.New("my error") -// } -// ... -// return nil -// } -// -// This pattern allows interrupting the flow on any received error. But what if -// there are errors that should be noted but still not fatal, for which the flow -// should not be interrupted? Implementing such logic at each if statement would -// make the code complex and the flow much harder to follow. -// -// Package warnings provides the Collector type and a clean and simple pattern -// for achieving such logic. The Collector takes care of deciding when to break -// the flow and when to continue, collecting any non-fatal errors (warnings) -// along the way. The only requirement is that fatal and non-fatal errors can be -// distinguished programmatically; that is a function such as -// -// IsFatal(error) bool -// -// must be implemented. The following is an example of what the above snippet -// could look like using the warnings package: -// -// import "gopkg.in/warnings.v0" -// -// func isFatal(err error) bool { -// _, ok := err.(WarningType) -// return !ok -// } -// -// func myfunc(params) error { -// c := warnings.NewCollector(isFatal) -// c.FatalWithWarnings = true -// if err := c.Collect(doSomething()); err != nil { -// return err -// } -// if err := c.Collect(doSomethingElse(...)); err != nil { -// return err -// } -// if ok := doAnotherThing(...); !ok { -// if err := c.Collect(errors.New("my error")); err != nil { -// return err -// } -// } -// ... -// return c.Done() -// } -// -// For an example of a non-trivial code base using this library, see -// gopkg.in/gcfg.v1 -// -// Rules for using warnings -// -// - ensure that warnings are programmatically distinguishable from fatal -// errors (i.e. implement an isFatal function and any necessary error types) -// - ensure that there is a single Collector instance for a call of each -// exported function -// - ensure that all errors (fatal or warning) are fed through Collect -// - ensure that every time an error is returned, it is one returned by a -// Collector (from Collect or Done) -// - ensure that Collect is never called after Done -// -// TODO -// -// - optionally limit the number of warnings (e.g. stop after 20 warnings) (?) -// - consider interaction with contexts -// - go vet-style invocations verifier -// - semi-automatic code converter -// -package warnings // import "gopkg.in/warnings.v0" - -import ( - "bytes" - "fmt" -) - -// List holds a collection of warnings and optionally one fatal error. -type List struct { - Warnings []error - Fatal error -} - -// Error implements the error interface. -func (l List) Error() string { - b := bytes.NewBuffer(nil) - if l.Fatal != nil { - fmt.Fprintln(b, "fatal:") - fmt.Fprintln(b, l.Fatal) - } - switch len(l.Warnings) { - case 0: - // nop - case 1: - fmt.Fprintln(b, "warning:") - default: - fmt.Fprintln(b, "warnings:") - } - for _, err := range l.Warnings { - fmt.Fprintln(b, err) - } - return b.String() -} - -// A Collector collects errors up to the first fatal error. -type Collector struct { - // IsFatal distinguishes between warnings and fatal errors. - IsFatal func(error) bool - // FatalWithWarnings set to true means that a fatal error is returned as - // a List together with all warnings so far. The default behavior is to - // only return the fatal error and discard any warnings that have been - // collected. - FatalWithWarnings bool - - l List - done bool -} - -// NewCollector returns a new Collector; it uses isFatal to distinguish between -// warnings and fatal errors. -func NewCollector(isFatal func(error) bool) *Collector { - return &Collector{IsFatal: isFatal} -} - -// Collect collects a single error (warning or fatal). It returns nil if -// collection can continue (only warnings so far), or otherwise the errors -// collected. Collect mustn't be called after the first fatal error or after -// Done has been called. -func (c *Collector) Collect(err error) error { - if c.done { - panic("warnings.Collector already done") - } - if err == nil { - return nil - } - if c.IsFatal(err) { - c.done = true - c.l.Fatal = err - } else { - c.l.Warnings = append(c.l.Warnings, err) - } - if c.l.Fatal != nil { - return c.erorr() - } - return nil -} - -// Done ends collection and returns the collected error(s). -func (c *Collector) Done() error { - c.done = true - return c.erorr() -} - -func (c *Collector) erorr() error { - if !c.FatalWithWarnings && c.l.Fatal != nil { - return c.l.Fatal - } - if c.l.Fatal == nil && len(c.l.Warnings) == 0 { - return nil - } - // Note that a single warning is also returned as a List. This is to make it - // easier to determine fatal-ness of the returned error. - return c.l -} - -// FatalOnly returns the fatal error, if any, **in an error returned by a -// Collector**. It returns nil if and only if err is nil or err is a List -// with err.Fatal == nil. -func FatalOnly(err error) error { - l, ok := err.(List) - if !ok { - return err - } - return l.Fatal -} - -// WarningsOnly returns the warnings **in an error returned by a Collector**. -func WarningsOnly(err error) []error { - l, ok := err.(List) - if !ok { - return nil - } - return l.Warnings -} diff --git a/vendor/k8s.io/cloud-provider-vsphere/LICENSE b/vendor/k8s.io/cloud-provider-vsphere/LICENSE deleted file mode 100644 index d6456956733..00000000000 --- a/vendor/k8s.io/cloud-provider-vsphere/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config.go b/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config.go deleted file mode 100644 index 5aaa9a82b18..00000000000 --- a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config.go +++ /dev/null @@ -1,285 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "fmt" - "os" - "strconv" - "strings" - - klog "k8s.io/klog/v2" -) - -/* - TODO: - When the INI based cloud-config is deprecated, this functions below should be preserved -*/ - -func getEnvKeyValue(match string, partial bool) (string, string, error) { - for _, e := range os.Environ() { - pair := strings.Split(e, "=") - if len(pair) != 2 { - continue - } - - key := pair[0] - value := pair[1] - - if partial && strings.Contains(key, match) { - return key, value, nil - } - - if strings.Compare(key, match) == 0 { - return key, value, nil - } - } - - matchType := "match" - if partial { - matchType = "partial match" - } - - return "", "", fmt.Errorf("Failed to find %s with %s", matchType, match) -} - -// FromEnv initializes the provided configuratoin object with values -// obtained from environment variables. If an environment variable is set -// for a property that's already initialized, the environment variable's value -// takes precedence. -func (cfg *Config) FromEnv() error { - - //Init - if cfg.VirtualCenter == nil { - cfg.VirtualCenter = make(map[string]*VirtualCenterConfig) - } - - //Globals - if v := os.Getenv("VSPHERE_VCENTER"); v != "" { - cfg.Global.VCenterIP = v - } - if v := os.Getenv("VSPHERE_VCENTER_PORT"); v != "" { - cfg.Global.VCenterPort = v - } - if v := os.Getenv("VSPHERE_USER"); v != "" { - cfg.Global.User = v - } - if v := os.Getenv("VSPHERE_PASSWORD"); v != "" { - cfg.Global.Password = v - } - if v := os.Getenv("VSPHERE_DATACENTER"); v != "" { - cfg.Global.Datacenters = v - } - if v := os.Getenv("VSPHERE_SECRET_NAME"); v != "" { - cfg.Global.SecretName = v - } - if v := os.Getenv("VSPHERE_SECRET_NAMESPACE"); v != "" { - cfg.Global.SecretNamespace = v - } - - if v := os.Getenv("VSPHERE_ROUNDTRIP_COUNT"); v != "" { - tmp, err := strconv.ParseUint(v, 10, 32) - if err != nil { - klog.Errorf("Failed to parse VSPHERE_ROUNDTRIP_COUNT: %s", err) - } else { - cfg.Global.RoundTripperCount = uint(tmp) - } - } - - if v := os.Getenv("VSPHERE_INSECURE"); v != "" { - InsecureFlag, err := strconv.ParseBool(v) - if err != nil { - klog.Errorf("Failed to parse VSPHERE_INSECURE: %s", err) - } else { - cfg.Global.InsecureFlag = InsecureFlag - } - } - - if v := os.Getenv("VSPHERE_SECRETS_DIRECTORY"); v != "" { - cfg.Global.SecretsDirectory = v - } - if cfg.Global.SecretsDirectory == "" { - cfg.Global.SecretsDirectory = DefaultSecretDirectory - } - if _, err := os.Stat(cfg.Global.SecretsDirectory); os.IsNotExist(err) { - cfg.Global.SecretsDirectory = "" //Dir does not exist, set to empty string - } - - if v := os.Getenv("VSPHERE_CAFILE"); v != "" { - cfg.Global.CAFile = v - } - if v := os.Getenv("VSPHERE_THUMBPRINT"); v != "" { - cfg.Global.Thumbprint = v - } - if v := os.Getenv("VSPHERE_LABEL_REGION"); v != "" { - cfg.Labels.Region = v - } - if v := os.Getenv("VSPHERE_LABEL_ZONE"); v != "" { - cfg.Labels.Zone = v - } - - //Build VirtualCenter from ENVs - for _, e := range os.Environ() { - pair := strings.Split(e, "=") - - if len(pair) != 2 { - continue - } - - key := pair[0] - value := pair[1] - - if strings.HasPrefix(key, "VSPHERE_VCENTER_") && len(value) > 0 { - id := strings.TrimPrefix(key, "VSPHERE_VCENTER_") - vcenter := value - - _, username, errUsername := getEnvKeyValue("VCENTER_"+id+"_USERNAME", false) - if errUsername != nil { - username = cfg.Global.User - } - _, password, errPassword := getEnvKeyValue("VCENTER_"+id+"_PASSWORD", false) - if errPassword != nil { - password = cfg.Global.Password - } - _, server, errServer := getEnvKeyValue("VCENTER_"+id+"_SERVER", false) - if errServer != nil { - server = "" - } - _, port, errPort := getEnvKeyValue("VCENTER_"+id+"_PORT", false) - if errPort != nil { - port = cfg.Global.VCenterPort - } - insecureFlag := false - _, insecureTmp, errInsecure := getEnvKeyValue("VCENTER_"+id+"_INSECURE", false) - if errInsecure != nil { - insecureFlagTmp, errTmp := strconv.ParseBool(insecureTmp) - if errTmp == nil { - insecureFlag = insecureFlagTmp - } - } - _, datacenters, errDatacenters := getEnvKeyValue("VCENTER_"+id+"_DATACENTERS", false) - if errDatacenters != nil { - datacenters = cfg.Global.Datacenters - } - roundtrip := DefaultRoundTripperCount - _, roundtripTmp, errRoundtrip := getEnvKeyValue("VCENTER_"+id+"_ROUNDTRIP", false) - if errRoundtrip != nil { - roundtripFlagTmp, errTmp := strconv.ParseUint(roundtripTmp, 10, 32) - if errTmp == nil { - roundtrip = uint(roundtripFlagTmp) - } - } - _, caFile, errCaFile := getEnvKeyValue("VCENTER_"+id+"_CAFILE", false) - if errCaFile != nil { - caFile = cfg.Global.CAFile - } - _, thumbprint, errThumbprint := getEnvKeyValue("VCENTER_"+id+"_THUMBPRINT", false) - if errThumbprint != nil { - thumbprint = cfg.Global.Thumbprint - } - - _, secretName, secretNameErr := getEnvKeyValue("VCENTER_"+id+"_SECRET_NAME", false) - _, secretNamespace, secretNamespaceErr := getEnvKeyValue("VCENTER_"+id+"_SECRET_NAMESPACE", false) - - if secretNameErr != nil || secretNamespaceErr != nil { - secretName = "" - secretNamespace = "" - } - secretRef := DefaultCredentialManager - if secretName != "" && secretNamespace != "" { - secretRef = vcenter - } - - iPFamilyPriority := []string{DefaultIPFamily} - _, ipFamily, errIPFamily := getEnvKeyValue("VCENTER_"+id+"_IP_FAMILY", false) - if errIPFamily != nil { - iPFamilyPriority = []string{ipFamily} - } - - // If server is explicitly set, that means the vcenter value above is the TenantRef - vcenterIP := vcenter - tenantRef := vcenter - if server != "" { - vcenterIP = server - tenantRef = vcenter - } - - var vcc *VirtualCenterConfig - if cfg.VirtualCenter[tenantRef] != nil { - vcc = cfg.VirtualCenter[tenantRef] - } else { - vcc = &VirtualCenterConfig{} - cfg.VirtualCenter[tenantRef] = vcc - } - - vcc.User = username - vcc.Password = password - vcc.TenantRef = tenantRef - vcc.VCenterIP = vcenterIP - vcc.VCenterPort = port - vcc.InsecureFlag = insecureFlag - vcc.Datacenters = datacenters - vcc.RoundTripperCount = roundtrip - vcc.CAFile = caFile - vcc.Thumbprint = thumbprint - vcc.SecretRef = secretRef - vcc.SecretName = secretName - vcc.SecretNamespace = secretNamespace - vcc.IPFamilyPriority = iPFamilyPriority - } - } - - return nil -} - -/* - TODO: - When the INI based cloud-config is deprecated, the references to the - INI based code (ie the call to ReadConfigINI) below should be deleted. -*/ - -// ReadConfig parses vSphere cloud config file and stores it into VSphereConfig. -// Environment variables are also checked -func ReadConfig(byConfig []byte) (*Config, error) { - if len(byConfig) == 0 { - return nil, fmt.Errorf("Invalid YAML/INI file") - } - - cfg, err := ReadConfigYAML(byConfig) - if err != nil { - klog.Warningf("ReadConfigYAML failed: %s", err) - - cfg, err = ReadConfigINI(byConfig) - if err != nil { - klog.Errorf("ReadConfigINI failed: %s", err) - return nil, err - } - - klog.Info("ReadConfig INI succeeded. INI-based cloud-config is deprecated and will be removed in 2.0. Please use YAML based cloud-config.") - } else { - klog.Info("ReadConfig YAML succeeded") - } - - // Env Vars should override config file entries if present - if err := cfg.FromEnv(); err != nil { - klog.Errorf("FromEnv failed: %s", err) - return nil, err - } - - klog.Info("Config initialized") - return cfg, nil -} diff --git a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config_ini_legacy.go b/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config_ini_legacy.go deleted file mode 100644 index 049707a12b1..00000000000 --- a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config_ini_legacy.go +++ /dev/null @@ -1,265 +0,0 @@ -/* -Copyright 2020 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "fmt" - "strings" - - ini "gopkg.in/gcfg.v1" - klog "k8s.io/klog/v2" -) - -/* - TODO: - When the INI based cloud-config is deprecated. This file should be deleted. -*/ - -// CreateConfig generates a common Config object based on what other structs and funcs -// are already dependent upon in other packages. -func (cci *CommonConfigINI) CreateConfig() *Config { - cfg := &Config{ - VirtualCenter: make(map[string]*VirtualCenterConfig), - } - - cfg.Global.User = cci.Global.User - cfg.Global.Password = cci.Global.Password - cfg.Global.VCenterIP = cci.Global.VCenterIP - cfg.Global.VCenterPort = cci.Global.VCenterPort - cfg.Global.InsecureFlag = cci.Global.InsecureFlag - cfg.Global.Datacenters = cci.Global.Datacenters - cfg.Global.RoundTripperCount = cci.Global.RoundTripperCount - cfg.Global.CAFile = cci.Global.CAFile - cfg.Global.Thumbprint = cci.Global.Thumbprint - cfg.Global.SecretName = cci.Global.SecretName - cfg.Global.SecretNamespace = cci.Global.SecretNamespace - cfg.Global.SecretsDirectory = cci.Global.SecretsDirectory - - for keyVcConfig, valVcConfig := range cci.VirtualCenter { - cfg.VirtualCenter[keyVcConfig] = &VirtualCenterConfig{ - User: valVcConfig.User, - Password: valVcConfig.Password, - TenantRef: valVcConfig.TenantRef, - VCenterIP: valVcConfig.VCenterIP, - VCenterPort: valVcConfig.VCenterPort, - InsecureFlag: valVcConfig.InsecureFlag, - Datacenters: valVcConfig.Datacenters, - RoundTripperCount: valVcConfig.RoundTripperCount, - CAFile: valVcConfig.CAFile, - Thumbprint: valVcConfig.Thumbprint, - SecretRef: valVcConfig.SecretRef, - SecretName: valVcConfig.SecretName, - SecretNamespace: valVcConfig.SecretNamespace, - IPFamilyPriority: valVcConfig.IPFamilyPriority, - } - } - - cfg.Labels.Region = cci.Labels.Region - cfg.Labels.Zone = cci.Labels.Zone - - return cfg -} - -// validateIPFamily takes the possible values of IPFamily and initializes the -// slice as determined bby priority -func (vcci *VirtualCenterConfigINI) validateIPFamily() error { - if len(vcci.IPFamily) == 0 { - vcci.IPFamily = DefaultIPFamily - } - - ipFamilies := strings.Split(vcci.IPFamily, ",") - for i, ipFamily := range ipFamilies { - ipFamily = strings.TrimSpace(ipFamily) - if len(ipFamily) == 0 { - copy(ipFamilies[i:], ipFamilies[i+1:]) // Shift a[i+1:] left one index. - ipFamilies[len(ipFamilies)-1] = "" // Erase last element (write zero value). - ipFamilies = ipFamilies[:len(ipFamilies)-1] // Truncate slice. - continue - } - if !strings.EqualFold(ipFamily, IPv4Family) && !strings.EqualFold(ipFamily, IPv6Family) { - return ErrInvalidIPFamilyType - } - } - - vcci.IPFamilyPriority = ipFamilies - return nil -} - -// isSecretInfoProvided returns true if k8s secret is set or using generic CO secret method. -// If both k8s secret and generic CO both are true, we don't know which to use, so return false. -func (cci *CommonConfigINI) isSecretInfoProvided() bool { - return (cci.Global.SecretName != "" && cci.Global.SecretNamespace != "" && cci.Global.SecretsDirectory == "") || - (cci.Global.SecretName == "" && cci.Global.SecretNamespace == "" && cci.Global.SecretsDirectory != "") -} - -// isSecretInfoProvided returns true if the secret per VC has been configured -func (vcci *VirtualCenterConfigINI) isSecretInfoProvided() bool { - return vcci.SecretName != "" && vcci.SecretNamespace != "" -} - -func (cci *CommonConfigINI) validateConfig() error { - //Fix default global values - if cci.Global.RoundTripperCount == 0 { - cci.Global.RoundTripperCount = DefaultRoundTripperCount - } - if cci.Global.VCenterPort == "" { - cci.Global.VCenterPort = DefaultVCenterPortStr - } - if cci.Global.APIBinding == "" { - cci.Global.APIBinding = DefaultAPIBinding - } - if cci.Global.IPFamily == "" { - cci.Global.IPFamily = DefaultIPFamily - } - - // Create a single instance of VSphereInstance for the Global VCenterIP if the - // VirtualCenter does not already exist in the map - if cci.Global.VCenterIP != "" && cci.VirtualCenter[cci.Global.VCenterIP] == nil { - cci.VirtualCenter[cci.Global.VCenterIP] = &VirtualCenterConfigINI{ - User: cci.Global.User, - Password: cci.Global.Password, - TenantRef: cci.Global.VCenterIP, - VCenterIP: cci.Global.VCenterIP, - VCenterPort: cci.Global.VCenterPort, - InsecureFlag: cci.Global.InsecureFlag, - Datacenters: cci.Global.Datacenters, - RoundTripperCount: cci.Global.RoundTripperCount, - CAFile: cci.Global.CAFile, - Thumbprint: cci.Global.Thumbprint, - SecretRef: DefaultCredentialManager, - SecretName: cci.Global.SecretName, - SecretNamespace: cci.Global.SecretNamespace, - IPFamily: cci.Global.IPFamily, - } - } - - // Must have at least one vCenter defined - if len(cci.VirtualCenter) == 0 { - klog.Error(ErrMissingVCenter) - return ErrMissingVCenter - } - - // vsphere.conf is no longer supported in the old format. - for vcServer, vcConfig := range cci.VirtualCenter { - klog.V(4).Infof("Initializing vc server %s", vcServer) - if vcServer == "" { - klog.Error(ErrInvalidVCenterIP) - return ErrInvalidVCenterIP - } - - // If vcConfig.VCenterIP is explicitly set, that means the vcServer - // above is the TenantRef - if vcConfig.VCenterIP != "" { - //vcConfig.VCenterIP is already set - vcConfig.TenantRef = vcServer - } else { - vcConfig.VCenterIP = vcServer - vcConfig.TenantRef = vcServer - } - - if !cci.isSecretInfoProvided() && !vcConfig.isSecretInfoProvided() { - if vcConfig.User == "" { - vcConfig.User = cci.Global.User - if vcConfig.User == "" { - klog.Errorf("vcConfig.User is empty for vc %s!", vcServer) - return ErrUsernameMissing - } - } - if vcConfig.Password == "" { - vcConfig.Password = cci.Global.Password - if vcConfig.Password == "" { - klog.Errorf("vcConfig.Password is empty for vc %s!", vcServer) - return ErrPasswordMissing - } - } - } else if cci.isSecretInfoProvided() && !vcConfig.isSecretInfoProvided() { - vcConfig.SecretRef = DefaultCredentialManager - } else if vcConfig.isSecretInfoProvided() { - vcConfig.SecretRef = vcConfig.SecretNamespace + "/" + vcConfig.SecretName - } - - if vcConfig.VCenterPort == "" { - vcConfig.VCenterPort = cci.Global.VCenterPort - } - - if vcConfig.Datacenters == "" { - if cci.Global.Datacenters != "" { - vcConfig.Datacenters = cci.Global.Datacenters - } - } - if vcConfig.RoundTripperCount == 0 { - vcConfig.RoundTripperCount = cci.Global.RoundTripperCount - } - if vcConfig.CAFile == "" { - vcConfig.CAFile = cci.Global.CAFile - } - if vcConfig.Thumbprint == "" { - vcConfig.Thumbprint = cci.Global.Thumbprint - } - - if vcConfig.IPFamily == "" { - vcConfig.IPFamily = cci.Global.IPFamily - } - - err := vcConfig.validateIPFamily() - if err != nil { - klog.Errorf("Invalid vcConfig IPFamily: %s, err=%s", vcConfig.IPFamily, err) - return err - } - - insecure := vcConfig.InsecureFlag - if !insecure { - vcConfig.InsecureFlag = cci.Global.InsecureFlag - } - } - - return nil -} - -// ReadRawConfigINI parses vSphere cloud config file and stores it into ConfigINI -func ReadRawConfigINI(byConfig []byte) (*CommonConfigINI, error) { - if len(byConfig) == 0 { - return nil, fmt.Errorf("Invalid INI file") - } - - strConfig := string(byConfig[:]) - - cfg := &CommonConfigINI{ - VirtualCenter: make(map[string]*VirtualCenterConfigINI), - } - - if err := ini.FatalOnly(ini.ReadStringInto(cfg, strConfig)); err != nil { - return nil, err - } - - err := cfg.validateConfig() - if err != nil { - return nil, err - } - - return cfg, nil -} - -// ReadConfigINI parses vSphere cloud config file and stores it into Config -func ReadConfigINI(byConfig []byte) (*Config, error) { - cfg, err := ReadRawConfigINI(byConfig) - if err != nil { - return nil, err - } - - return cfg.CreateConfig(), nil -} diff --git a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config_yaml.go b/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config_yaml.go deleted file mode 100644 index 56a67b3e114..00000000000 --- a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/config_yaml.go +++ /dev/null @@ -1,229 +0,0 @@ -/* -Copyright 2020 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "fmt" - "strings" - - yaml "gopkg.in/yaml.v2" - klog "k8s.io/klog/v2" -) - -/* - TODO: - When the INI based cloud-config is deprecated, this file should be merged into config.go - and this file should be deleted. -*/ - -// CreateConfig generates a common Config object based on what other structs and funcs -// are already dependent upon in other packages. -func (ccy *CommonConfigYAML) CreateConfig() *Config { - cfg := &Config{ - VirtualCenter: make(map[string]*VirtualCenterConfig), - } - - cfg.Global.User = ccy.Global.User - cfg.Global.Password = ccy.Global.Password - cfg.Global.VCenterIP = ccy.Global.VCenterIP - cfg.Global.VCenterPort = fmt.Sprint(ccy.Global.VCenterPort) - cfg.Global.InsecureFlag = ccy.Global.InsecureFlag - cfg.Global.Datacenters = strings.Join(ccy.Global.Datacenters, ",") - cfg.Global.RoundTripperCount = ccy.Global.RoundTripperCount - cfg.Global.CAFile = ccy.Global.CAFile - cfg.Global.Thumbprint = ccy.Global.Thumbprint - cfg.Global.SecretName = ccy.Global.SecretName - cfg.Global.SecretNamespace = ccy.Global.SecretNamespace - cfg.Global.SecretsDirectory = ccy.Global.SecretsDirectory - - for keyVcConfig, valVcConfig := range ccy.Vcenter { - cfg.VirtualCenter[keyVcConfig] = &VirtualCenterConfig{ - User: valVcConfig.User, - Password: valVcConfig.Password, - TenantRef: valVcConfig.TenantRef, - VCenterIP: valVcConfig.VCenterIP, - VCenterPort: fmt.Sprint(valVcConfig.VCenterPort), - InsecureFlag: valVcConfig.InsecureFlag, - Datacenters: strings.Join(valVcConfig.Datacenters, ","), - RoundTripperCount: valVcConfig.RoundTripperCount, - CAFile: valVcConfig.CAFile, - Thumbprint: valVcConfig.Thumbprint, - SecretRef: valVcConfig.SecretRef, - SecretName: valVcConfig.SecretName, - SecretNamespace: valVcConfig.SecretNamespace, - IPFamilyPriority: valVcConfig.IPFamilyPriority, - } - } - - cfg.Labels.Region = ccy.Labels.Region - cfg.Labels.Zone = ccy.Labels.Zone - - return cfg -} - -// isSecretInfoProvided returns true if k8s secret is set or using generic CO secret method. -// If both k8s secret and generic CO both are true, we don't know which to use, so return false. -func (ccy *CommonConfigYAML) isSecretInfoProvided() bool { - return (ccy.Global.SecretName != "" && ccy.Global.SecretNamespace != "" && ccy.Global.SecretsDirectory == "") || - (ccy.Global.SecretName == "" && ccy.Global.SecretNamespace == "" && ccy.Global.SecretsDirectory != "") -} - -// isSecretInfoProvided returns true if the secret per VC has been configured -func (vccy *VirtualCenterConfigYAML) isSecretInfoProvided() bool { - return vccy.SecretName != "" && vccy.SecretNamespace != "" -} - -func (ccy *CommonConfigYAML) validateConfig() error { - //Fix default global values - if ccy.Global.RoundTripperCount == 0 { - ccy.Global.RoundTripperCount = DefaultRoundTripperCount - } - if ccy.Global.VCenterPort == 0 { - ccy.Global.VCenterPort = DefaultVCenterPort - } - if ccy.Global.APIBinding == "" { - ccy.Global.APIBinding = DefaultAPIBinding - } - if len(ccy.Global.IPFamilyPriority) == 0 { - ccy.Global.IPFamilyPriority = []string{DefaultIPFamily} - } - - // Create a single instance of VSphereInstance for the Global VCenterIP if the - // VirtualCenter does not already exist in the map - if ccy.Global.VCenterIP != "" && ccy.Vcenter[ccy.Global.VCenterIP] == nil { - ccy.Vcenter[ccy.Global.VCenterIP] = &VirtualCenterConfigYAML{ - User: ccy.Global.User, - Password: ccy.Global.Password, - TenantRef: ccy.Global.VCenterIP, - VCenterIP: ccy.Global.VCenterIP, - VCenterPort: ccy.Global.VCenterPort, - InsecureFlag: ccy.Global.InsecureFlag, - Datacenters: ccy.Global.Datacenters, - RoundTripperCount: ccy.Global.RoundTripperCount, - CAFile: ccy.Global.CAFile, - Thumbprint: ccy.Global.Thumbprint, - SecretRef: DefaultCredentialManager, - SecretName: ccy.Global.SecretName, - SecretNamespace: ccy.Global.SecretNamespace, - IPFamilyPriority: ccy.Global.IPFamilyPriority, - } - } - - // Must have at least one vCenter defined - if len(ccy.Vcenter) == 0 { - klog.Error(ErrMissingVCenter) - return ErrMissingVCenter - } - - // vsphere.conf is no longer supported in the old format. - for tenantRef, vcConfig := range ccy.Vcenter { - klog.V(4).Infof("Initializing vc server %s", tenantRef) - if vcConfig.VCenterIP == "" { - klog.Error(ErrInvalidVCenterIP) - return ErrInvalidVCenterIP - } - - // in the YAML-based config, the tenant ref is required in the config - vcConfig.TenantRef = tenantRef - - if !ccy.isSecretInfoProvided() && !vcConfig.isSecretInfoProvided() { - if vcConfig.User == "" { - vcConfig.User = ccy.Global.User - if vcConfig.User == "" { - klog.Errorf("vcConfig.User is empty for vc %s!", tenantRef) - return ErrUsernameMissing - } - } - if vcConfig.Password == "" { - vcConfig.Password = ccy.Global.Password - if vcConfig.Password == "" { - klog.Errorf("vcConfig.Password is empty for vc %s!", tenantRef) - return ErrPasswordMissing - } - } - } else if ccy.isSecretInfoProvided() && !vcConfig.isSecretInfoProvided() { - vcConfig.SecretRef = DefaultCredentialManager - } else if vcConfig.isSecretInfoProvided() { - vcConfig.SecretRef = vcConfig.SecretNamespace + "/" + vcConfig.SecretName - } - - if vcConfig.VCenterPort == 0 { - vcConfig.VCenterPort = ccy.Global.VCenterPort - } - - if len(vcConfig.Datacenters) == 0 { - if len(ccy.Global.Datacenters) != 0 { - vcConfig.Datacenters = ccy.Global.Datacenters - } - } - if vcConfig.RoundTripperCount == 0 { - vcConfig.RoundTripperCount = ccy.Global.RoundTripperCount - } - if vcConfig.CAFile == "" { - vcConfig.CAFile = ccy.Global.CAFile - } - if vcConfig.Thumbprint == "" { - vcConfig.Thumbprint = ccy.Global.Thumbprint - } - - if len(vcConfig.IPFamilyPriority) == 0 { - vcConfig.IPFamilyPriority = ccy.Global.IPFamilyPriority - } - - insecure := vcConfig.InsecureFlag - if !insecure { - vcConfig.InsecureFlag = ccy.Global.InsecureFlag - } - } - - return nil -} - -// ReadRawConfigYAML parses vSphere cloud config file and stores it into ConfigYAML -func ReadRawConfigYAML(byConfig []byte) (*CommonConfigYAML, error) { - if len(byConfig) == 0 { - klog.Errorf("Invalid YAML file") - return nil, fmt.Errorf("Invalid YAML file") - } - - cfg := CommonConfigYAML{ - Vcenter: make(map[string]*VirtualCenterConfigYAML), - } - - if err := yaml.Unmarshal(byConfig, &cfg); err != nil { - klog.Errorf("Unmarshal failed: %s", err) - return nil, err - } - - err := cfg.validateConfig() - if err != nil { - klog.Errorf("validateConfig failed: %s", err) - return nil, err - } - - return &cfg, nil -} - -// ReadConfigYAML parses vSphere cloud config file and stores it into Config -func ReadConfigYAML(byConfig []byte) (*Config, error) { - cfg, err := ReadRawConfigYAML(byConfig) - if err != nil { - return nil, err - } - - return cfg.CreateConfig(), nil -} diff --git a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/consts_and_errors.go b/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/consts_and_errors.go deleted file mode 100644 index 0ed200ffb6a..00000000000 --- a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/consts_and_errors.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "errors" -) - -const ( - // DefaultRoundTripperCount is the number of allowed round trips - // before an error is returned. - DefaultRoundTripperCount uint = 3 - - // DefaultAPIBinding is the default ADDRESS:PORT binding used for - // exposing the API service. - DefaultAPIBinding string = ":43001" - - // DefaultVCenterPortStr is the default port used to access vCenter in string form - DefaultVCenterPortStr string = "443" - // DefaultVCenterPort is the default port used to access vCenter in uint form - DefaultVCenterPort uint = 443 - - // DefaultSecretDirectory is the default path to the secrets directory. - DefaultSecretDirectory string = "/etc/cloud/secrets" - - // IPv6Family string representation for IPv6 - IPv6Family = "ipv6" - // IPv4Family string representation for IPv4 - IPv4Family = "ipv4" - - // DefaultIPFamily is the default IP addressing to use for networking - DefaultIPFamily = IPv4Family - - // DefaultCredentialManager used for the Global CredMgr/Lister - DefaultCredentialManager string = "Global" -) - -var ( - // ErrUsernameMissing is returned when the provided username is empty. - ErrUsernameMissing = errors.New("Username is missing") - - // ErrPasswordMissing is returned when the provided password is empty. - ErrPasswordMissing = errors.New("Password is missing") - - // ErrInvalidVCenterIP is returned when the provided vCenter IP address is - // missing from the provided configuration. - ErrInvalidVCenterIP = errors.New("vsphere.conf does not have the VirtualCenter IP address specified") - - // ErrMissingVCenter is returned when the provided configuration does not - // define any vCenters. - ErrMissingVCenter = errors.New("No Virtual Center hosts defined") - - // ErrInvalidIPFamilyType is returned when an invalid IPFamily type is encountered - ErrInvalidIPFamilyType = errors.New("Invalid IP Family type") -) diff --git a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_common.go b/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_common.go deleted file mode 100644 index 493f2d4dba0..00000000000 --- a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_common.go +++ /dev/null @@ -1,117 +0,0 @@ -/* -Copyright 2020 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -/* - TODO: - When the INI based cloud-config is deprecated. This file should be deleted and - the structs in types_yaml.go will be renamed to replace the ones in this file. -*/ - -// Global struct -type Global struct { - // vCenter username. - User string - // vCenter password in clear text. - Password string - // Deprecated. Use VirtualCenter to specify multiple vCenter Servers. - // vCenter IP. - VCenterIP string - // vCenter port. - VCenterPort string - // True if vCenter uses self-signed cert. - InsecureFlag bool - // Datacenter in which VMs are located. - Datacenters string - // Soap round tripper count (retries = RoundTripper - 1) - RoundTripperCount uint - // Specifies the path to a CA certificate in PEM format. Optional; if not - // configured, the system's CA certificates will be used. - CAFile string - // Thumbprint of the VCenter's certificate thumbprint - Thumbprint string - // Name of the secret were vCenter credentials are present. - SecretName string - // Secret Namespace where secret will be present that has vCenter credentials. - SecretNamespace string - // Secret directory in the event that: - // 1) we don't want to use the k8s API to listen for changes to secrets - // 2) we are not in a k8s env, namely DC/OS, since CSI is CO agnostic - // Default: /etc/cloud/credentials - SecretsDirectory string -} - -// VirtualCenterConfig struct -type VirtualCenterConfig struct { - // vCenter username. - User string - // vCenter password in clear text. - Password string - // TenantRef (intentionally not exposed via the config) is a unique tenant ref to - // be used in place of the vcServer as the primary connection key. If one label is set, - // all virtual center configs must have a unique label. - TenantRef string - // vCenterIP - If this field in the config is set, it is assumed then that value in [VirtualCenter ""] - // is now the TenantRef above and this field is the actual VCenterIP. Otherwise for backward - // compatibility, the value by default is the IP or FQDN of the vCenter Server. - VCenterIP string - // vCenter port. - VCenterPort string - // True if vCenter uses self-signed cert. - InsecureFlag bool - // Datacenter in which VMs are located. - Datacenters string - // Soap round tripper count (retries = RoundTripper - 1) - RoundTripperCount uint - // Specifies the path to a CA certificate in PEM format. Optional; if not - // configured, the system's CA certificates will be used. - CAFile string - // Thumbprint of the VCenter's certificate thumbprint - Thumbprint string - // SecretRef (intentionally not exposed via the config) is a key to identify which - // InformerManager holds the secret - SecretRef string - // Name of the secret where vCenter credentials are present. - SecretName string - // Namespace where the secret will be present containing vCenter credentials. - SecretNamespace string - // IP Family enables the ability to support IPv4 or IPv6 - // Supported values are: - // ipv4 - IPv4 addresses only (Default) - // ipv6 - IPv6 addresses only - IPFamilyPriority []string -} - -// Labels struct -type Labels struct { - // Zone describes a zone - Zone string - // Region describes a region - Region string -} - -// Config is used to read and store information from the cloud configuration file -type Config struct { - // Global settings - Global Global - - // Virtual Center configurations - VirtualCenter map[string]*VirtualCenterConfig - - // Tag categories and tags which correspond to "built-in node labels: zones and region" - Labels Labels -} diff --git a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_ini_legacy.go b/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_ini_legacy.go deleted file mode 100644 index d31eae21569..00000000000 --- a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_ini_legacy.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -/* - TODO: - When the INI based cloud-config is deprecated. This file should be deleted. -*/ - -// GlobalINI are global values -type GlobalINI struct { - // vCenter username. - User string `gcfg:"user"` - // vCenter password in clear text. - Password string `gcfg:"password"` - // Deprecated. Use VirtualCenter to specify multiple vCenter Servers. - // vCenter IP. - VCenterIP string `gcfg:"server"` - // vCenter port. - VCenterPort string `gcfg:"port"` - // True if vCenter uses self-signed cert. - InsecureFlag bool `gcfg:"insecure-flag"` - // Datacenter in which VMs are located. - Datacenters string `gcfg:"datacenters"` - // Soap round tripper count (retries = RoundTripper - 1) - RoundTripperCount uint `gcfg:"soap-roundtrip-count"` - // Specifies the path to a CA certificate in PEM format. Optional; if not - // configured, the system's CA certificates will be used. - CAFile string `gcfg:"ca-file"` - // Thumbprint of the VCenter's certificate thumbprint - Thumbprint string `gcfg:"thumbprint"` - // Name of the secret were vCenter credentials are present. - SecretName string `gcfg:"secret-name"` - // Secret Namespace where secret will be present that has vCenter credentials. - SecretNamespace string `gcfg:"secret-namespace"` - // Secret directory in the event that: - // 1) we don't want to use the k8s API to listen for changes to secrets - // 2) we are not in a k8s env, namely DC/OS, since CSI is CO agnostic - // Default: /etc/cloud/credentials - SecretsDirectory string `gcfg:"secrets-directory"` - // Disable the vSphere CCM API - // Default: true - APIDisable bool `gcfg:"api-disable"` - // Configurable vSphere CCM API port - // Default: 43001 - APIBinding string `gcfg:"api-binding"` - // IP Family enables the ability to support IPv4 or IPv6 - // Supported values are: - // ipv4 - IPv4 addresses only (Default) - // ipv6 - IPv6 addresses only - IPFamily string `gcfg:"ip-family"` -} - -// VirtualCenterConfigINI contains information used to access a remote vCenter -// endpoint. -type VirtualCenterConfigINI struct { - // vCenter username. - User string `gcfg:"user"` - // vCenter password in clear text. - Password string `gcfg:"password"` - // TenantRef (intentionally not exposed via the config) is a unique tenant ref to - // be used in place of the vcServer as the primary connection key. If one label is set, - // all virtual center configs must have a unique label. - TenantRef string - // vCenterIP - If this field in the config is set, it is assumed then that value in [VirtualCenter ""] - // is now the TenantRef above and this field is the actual VCenterIP. Otherwise for backward - // compatibility, the value by default is the IP or FQDN of the vCenter Server. - VCenterIP string `gcfg:"server"` - // vCenter port. - VCenterPort string `gcfg:"port"` - // True if vCenter uses self-signed cert. - InsecureFlag bool `gcfg:"insecure-flag"` - // Datacenter in which VMs are located. - Datacenters string `gcfg:"datacenters"` - // Soap round tripper count (retries = RoundTripper - 1) - RoundTripperCount uint `gcfg:"soap-roundtrip-count"` - // Specifies the path to a CA certificate in PEM format. Optional; if not - // configured, the system's CA certificates will be used. - CAFile string `gcfg:"ca-file"` - // Thumbprint of the VCenter's certificate thumbprint - Thumbprint string `gcfg:"thumbprint"` - // SecretRef (intentionally not exposed via the config) is a key to identify which - // InformerManager holds the secret - SecretRef string - // Name of the secret where vCenter credentials are present. - SecretName string `gcfg:"secret-name"` - // Namespace where the secret will be present containing vCenter credentials. - SecretNamespace string `gcfg:"secret-namespace"` - // IP Family enables the ability to support IPv4 or IPv6 - // Supported values are: - // ipv4 - IPv4 addresses only (Default) - // ipv6 - IPv6 addresses only - IPFamily string `gcfg:"ip-family"` - // IPFamilyPriority (intentionally not exposed via the config) the list/priority of IP versions - IPFamilyPriority []string -} - -// LabelsINI tags categories and tags which correspond to "built-in node labels: zones and region" -type LabelsINI struct { - Zone string `gcfg:"zone"` - Region string `gcfg:"region"` -} - -// CommonConfigINI is used to read and store information from the cloud configuration file -type CommonConfigINI struct { - // Global values... - Global GlobalINI - - // Virtual Center configurations - VirtualCenter map[string]*VirtualCenterConfigINI - - // Tag categories and tags which correspond to "built-in node labels: zones and region" - Labels LabelsINI -} diff --git a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_yaml.go b/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_yaml.go deleted file mode 100644 index 472c279ec5a..00000000000 --- a/vendor/k8s.io/cloud-provider-vsphere/pkg/common/config/types_yaml.go +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright 2020 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -/* - TODO: - When the INI based cloud-config is deprecated, this file should be renamed - from types_yaml.go to types.go and the structs within this file should be named: - - GlobalYAML -> Global - VirtualCenterConfigYAML -> VirtualCenterConfig - LabelsYAML -> Labels - ConfigYAML -> Config -*/ - -// GlobalYAML are global values -type GlobalYAML struct { - // vCenter username. - User string `yaml:"user"` - // vCenter password in clear text. - Password string `yaml:"password"` - // Deprecated. Use VirtualCenter to specify multiple vCenter Servers. - // vCenter IP. - VCenterIP string `yaml:"server"` - // vCenter port. - VCenterPort uint `yaml:"port"` - // True if vCenter uses self-signed cert. - InsecureFlag bool `yaml:"insecureFlag"` - // Datacenter in which VMs are located. - Datacenters []string `yaml:"datacenters"` - // Soap round tripper count (retries = RoundTripper - 1) - RoundTripperCount uint `yaml:"soapRoundtripCount"` - // Specifies the path to a CA certificate in PEM format. Optional; if not - // configured, the system's CA certificates will be used. - CAFile string `yaml:"caFile"` - // Thumbprint of the VCenter's certificate thumbprint - Thumbprint string `yaml:"thumbprint"` - // Name of the secret were vCenter credentials are present. - SecretName string `yaml:"secretName"` - // Secret Namespace where secret will be present that has vCenter credentials. - SecretNamespace string `yaml:"secretNamespace"` - // Secret directory in the event that: - // 1) we don't want to use the k8s API to listen for changes to secrets - // 2) we are not in a k8s env, namely DC/OS, since CSI is CO agnostic - // Default: /etc/cloud/credentials - SecretsDirectory string `yaml:"secretsDirectory"` - // Disable the vSphere CCM API - // Default: true - APIDisable bool `yaml:"apiDisable"` - // Configurable vSphere CCM API port - // Default: 43001 - APIBinding string `yaml:"apiBinding"` - // IP Family enables the ability to support IPv4 or IPv6 - // Supported values are: - // ipv4 - IPv4 addresses only (Default) - // ipv6 - IPv6 addresses only - IPFamilyPriority []string `yaml:"ipFamily"` -} - -// VirtualCenterConfigYAML contains information used to access a remote vCenter -// endpoint. -type VirtualCenterConfigYAML struct { - // vCenter username. - User string `yaml:"user"` - // vCenter password in clear text. - Password string `yaml:"password"` - // TenantRef (intentionally not exposed via the config) is a unique tenant ref to - // be used in place of the vcServer as the primary connection key. If one label is set, - // all virtual center configs must have a unique label. - TenantRef string - // vCenterIP - If this field in the config is set, it is assumed then that value in [VirtualCenter ""] - // is now the TenantRef above and this field is the actual VCenterIP. Otherwise for backward - // compatibility, the value by default is the IP or FQDN of the vCenter Server. - VCenterIP string `yaml:"server"` - // vCenter port. - VCenterPort uint `yaml:"port"` - // True if vCenter uses self-signed cert. - InsecureFlag bool `yaml:"insecureFlag"` - // Datacenter in which VMs are located. - Datacenters []string `yaml:"datacenters"` - // Soap round tripper count (retries = RoundTripper - 1) - RoundTripperCount uint `yaml:"soapRoundtripCount"` - // Specifies the path to a CA certificate in PEM format. Optional; if not - // configured, the system's CA certificates will be used. - CAFile string `yaml:"caFile"` - // Thumbprint of the VCenter's certificate thumbprint - Thumbprint string `yaml:"thumbprint"` - // SecretRef (intentionally not exposed via the config) is a key to identify which - // InformerManager holds the secret - SecretRef string - // Name of the secret where vCenter credentials are present. - SecretName string `yaml:"secretName"` - // Namespace where the secret will be present containing vCenter credentials. - SecretNamespace string `yaml:"secretNamespace"` - // IP Family enables the ability to support IPv4 or IPv6 - // Supported values are: - // ipv4 - IPv4 addresses only (Default) - // ipv6 - IPv6 addresses only - IPFamilyPriority []string `yaml:"ipFamily"` -} - -// LabelsYAML tags categories and tags which correspond to "built-in node labels: zones and region" -type LabelsYAML struct { - Zone string `yaml:"zone"` - Region string `yaml:"region"` -} - -// CommonConfigYAML is used to read and store information from the cloud configuration file -type CommonConfigYAML struct { - // Global values... - Global GlobalYAML - - // Virtual Center configurations - Vcenter map[string]*VirtualCenterConfigYAML - - // Tag categories and tags which correspond to "built-in node labels: zones and region" - Labels LabelsYAML -} diff --git a/vendor/modules.txt b/vendor/modules.txt index e0d4f5524d8..b0ba9c1d182 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2049,12 +2049,9 @@ github.com/openshift/library-go/pkg/verify/util # github.com/openshift/machine-api-operator v0.2.1-0.20251128002018-85c00c0d525f ## explicit; go 1.24.0 github.com/openshift/machine-api-operator/pkg/controller/machine -github.com/openshift/machine-api-operator/pkg/controller/vsphere -github.com/openshift/machine-api-operator/pkg/controller/vsphere/session github.com/openshift/machine-api-operator/pkg/metrics github.com/openshift/machine-api-operator/pkg/util github.com/openshift/machine-api-operator/pkg/util/conditions -github.com/openshift/machine-api-operator/pkg/util/ipam # github.com/openshift/machine-api-provider-gcp v0.0.1-0.20260113091719-80740861bb2a ## explicit; go 1.24.0 github.com/openshift/machine-api-provider-gcp/pkg/apis/gcpprovider/v1beta1 @@ -3043,12 +3040,6 @@ google.golang.org/protobuf/types/known/wrapperspb # gopkg.in/evanphx/json-patch.v4 v4.13.0 ## explicit gopkg.in/evanphx/json-patch.v4 -# gopkg.in/gcfg.v1 v1.2.3 -## explicit -gopkg.in/gcfg.v1 -gopkg.in/gcfg.v1/scanner -gopkg.in/gcfg.v1/token -gopkg.in/gcfg.v1/types # gopkg.in/inf.v0 v0.9.1 ## explicit gopkg.in/inf.v0 @@ -3061,9 +3052,6 @@ gopkg.in/natefinch/lumberjack.v2 # gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 ## explicit gopkg.in/tomb.v1 -# gopkg.in/warnings.v0 v0.1.2 -## explicit -gopkg.in/warnings.v0 # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 gopkg.in/yaml.v2 @@ -3932,9 +3920,6 @@ k8s.io/client-go/util/keyutil k8s.io/client-go/util/retry k8s.io/client-go/util/watchlist k8s.io/client-go/util/workqueue -# k8s.io/cloud-provider-vsphere v1.33.3 => github.com/openshift/cloud-provider-vsphere v1.19.1-0.20240626105621-6464d0bb4928 -## explicit; go 1.22.0 -k8s.io/cloud-provider-vsphere/pkg/common/config # k8s.io/cluster-registry v0.0.6 ## explicit k8s.io/cluster-registry/pkg/apis/clusterregistry/v1alpha1