blob: 4332b3cb016a9802b8ed69631040c2c45aae35a4 [file] [log] [blame]
/*
Copyright 2016 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 vsphere
import (
"context"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
"testing"
lookup "github.com/vmware/govmomi/lookup/simulator"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/simulator"
"github.com/vmware/govmomi/simulator/vpx"
sts "github.com/vmware/govmomi/sts/simulator"
"github.com/vmware/govmomi/vapi/rest"
vapi "github.com/vmware/govmomi/vapi/simulator"
"github.com/vmware/govmomi/vapi/tags"
"github.com/vmware/govmomi/vim25/mo"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/rand"
cloudprovider "k8s.io/cloud-provider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere/vclib"
"k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere/vclib/fixtures"
)
// localhostCert was generated from crypto/tls/generate_cert.go with the following command:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = `-----BEGIN CERTIFICATE-----
MIIBjzCCATmgAwIBAgIRAKpi2WmTcFrVjxrl5n5YDUEwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2
MDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC
QQC9fEbRszP3t14Gr4oahV7zFObBI4TfA5i7YnlMXeLinb7MnvT4bkfOJzE6zktn
59zP7UiHs3l4YOuqrjiwM413AgMBAAGjaDBmMA4GA1UdDwEB/wQEAwICpDATBgNV
HSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MC4GA1UdEQQnMCWCC2V4
YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUA
A0EAUsVE6KMnza/ZbodLlyeMzdo7EM/5nb5ywyOxgIOCf0OOLHsPS9ueGLQX9HEG
//yjTXuhNcUugExIjM/AIwAZPQ==
-----END CERTIFICATE-----`
// localhostKey is the private key for localhostCert.
var localhostKey = `-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAL18RtGzM/e3XgavihqFXvMU5sEjhN8DmLtieUxd4uKdvsye9Phu
R84nMTrOS2fn3M/tSIezeXhg66quOLAzjXcCAwEAAQJBAKcRxH9wuglYLBdI/0OT
BLzfWPZCEw1vZmMR2FF1Fm8nkNOVDPleeVGTWoOEcYYlQbpTmkGSxJ6ya+hqRi6x
goECIQDx3+X49fwpL6B5qpJIJMyZBSCuMhH4B7JevhGGFENi3wIhAMiNJN5Q3UkL
IuSvv03kaPR5XVQ99/UeEetUgGvBcABpAiBJSBzVITIVCGkGc7d+RCf49KTCIklv
bGWObufAR8Ni4QIgWpILjW8dkGg8GOUZ0zaNA6Nvt6TIv2UWGJ4v5PoV98kCIQDx
rIiZs5QbKdycsv9gQJzwQAogC8o04X3Zz3dsoX+h4A==
-----END RSA PRIVATE KEY-----`
func configFromEnv() (cfg VSphereConfig, ok bool) {
var InsecureFlag bool
var err error
cfg.Global.VCenterIP = os.Getenv("VSPHERE_VCENTER")
cfg.Global.VCenterPort = os.Getenv("VSPHERE_VCENTER_PORT")
cfg.Global.User = os.Getenv("VSPHERE_USER")
cfg.Global.Password = os.Getenv("VSPHERE_PASSWORD")
cfg.Global.Datacenter = os.Getenv("VSPHERE_DATACENTER")
cfg.Network.PublicNetwork = os.Getenv("VSPHERE_PUBLIC_NETWORK")
cfg.Global.DefaultDatastore = os.Getenv("VSPHERE_DATASTORE")
cfg.Disk.SCSIControllerType = os.Getenv("VSPHERE_SCSICONTROLLER_TYPE")
cfg.Global.WorkingDir = os.Getenv("VSPHERE_WORKING_DIR")
cfg.Global.VMName = os.Getenv("VSPHERE_VM_NAME")
if os.Getenv("VSPHERE_INSECURE") != "" {
InsecureFlag, err = strconv.ParseBool(os.Getenv("VSPHERE_INSECURE"))
} else {
InsecureFlag = false
}
if err != nil {
log.Fatal(err)
}
cfg.Global.InsecureFlag = InsecureFlag
ok = (cfg.Global.VCenterIP != "" &&
cfg.Global.User != "")
return
}
// configFromSim starts a vcsim instance and returns config for use against the vcsim instance.
// The vcsim instance is configured with an empty tls.Config.
func configFromSim() (VSphereConfig, func()) {
return configFromSimWithTLS(new(tls.Config), true)
}
// configFromSimWithTLS starts a vcsim instance and returns config for use against the vcsim instance.
// The vcsim instance is configured with a tls.Config. The returned client
// config can be configured to allow/decline insecure connections.
func configFromSimWithTLS(tlsConfig *tls.Config, insecureAllowed bool) (VSphereConfig, func()) {
var cfg VSphereConfig
model := simulator.VPX()
err := model.Create()
if err != nil {
log.Fatal(err)
}
model.Service.TLS = tlsConfig
s := model.Service.NewServer()
// STS simulator
path, handler := sts.New(s.URL, vpx.Setting)
model.Service.ServeMux.Handle(path, handler)
// vAPI simulator
path, handler = vapi.New(s.URL, vpx.Setting)
model.Service.ServeMux.Handle(path, handler)
// Lookup Service simulator
model.Service.RegisterSDK(lookup.New())
cfg.Global.InsecureFlag = insecureAllowed
cfg.Global.VCenterIP = s.URL.Hostname()
cfg.Global.VCenterPort = s.URL.Port()
cfg.Global.User = s.URL.User.Username()
cfg.Global.Password, _ = s.URL.User.Password()
cfg.Global.Datacenter = vclib.TestDefaultDatacenter
cfg.Network.PublicNetwork = vclib.TestDefaultNetwork
cfg.Global.DefaultDatastore = vclib.TestDefaultDatastore
cfg.Disk.SCSIControllerType = os.Getenv("VSPHERE_SCSICONTROLLER_TYPE")
cfg.Global.WorkingDir = os.Getenv("VSPHERE_WORKING_DIR")
cfg.Global.VMName = os.Getenv("VSPHERE_VM_NAME")
if cfg.Global.WorkingDir == "" {
cfg.Global.WorkingDir = "vm" // top-level Datacenter.VmFolder
}
uuid := simulator.Map.Any("VirtualMachine").(*simulator.VirtualMachine).Config.Uuid
getVMUUID = func() (string, error) { return uuid, nil }
return cfg, func() {
getVMUUID = GetVMUUID
s.Close()
model.Remove()
}
}
// configFromEnvOrSim returns config from configFromEnv if set, otherwise returns configFromSim.
func configFromEnvOrSim() (VSphereConfig, func()) {
cfg, ok := configFromEnv()
if ok {
return cfg, func() {}
}
return configFromSim()
}
func TestReadConfig(t *testing.T) {
_, err := readConfig(nil)
if err == nil {
t.Errorf("Should fail when no config is provided: %s", err)
}
cfg, err := readConfig(strings.NewReader(`
[Global]
server = 0.0.0.0
port = 443
user = user
password = password
insecure-flag = true
datacenter = us-west
vm-uuid = 1234
vm-name = vmname
ca-file = /some/path/to/a/ca.pem
`))
if err != nil {
t.Fatalf("Should succeed when a valid config is provided: %s", err)
}
if cfg.Global.VCenterIP != "0.0.0.0" {
t.Errorf("incorrect vcenter ip: %s", cfg.Global.VCenterIP)
}
if cfg.Global.Datacenter != "us-west" {
t.Errorf("incorrect datacenter: %s", cfg.Global.Datacenter)
}
if cfg.Global.VMUUID != "1234" {
t.Errorf("incorrect vm-uuid: %s", cfg.Global.VMUUID)
}
if cfg.Global.VMName != "vmname" {
t.Errorf("incorrect vm-name: %s", cfg.Global.VMName)
}
if cfg.Global.CAFile != "/some/path/to/a/ca.pem" {
t.Errorf("incorrect ca-file: %s", cfg.Global.CAFile)
}
}
func TestNewVSphere(t *testing.T) {
cfg, ok := configFromEnv()
if !ok {
t.Skipf("No config found in environment")
}
_, err := newControllerNode(cfg)
if err != nil {
t.Fatalf("Failed to construct/authenticate vSphere: %s", err)
}
}
func TestVSphereLogin(t *testing.T) {
cfg, cleanup := configFromEnvOrSim()
defer cleanup()
// Create vSphere configuration object
vs, err := newControllerNode(cfg)
if err != nil {
t.Fatalf("Failed to construct/authenticate vSphere: %s", err)
}
// Create context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Create vSphere client
vcInstance, ok := vs.vsphereInstanceMap[cfg.Global.VCenterIP]
if !ok {
t.Fatalf("Couldn't get vSphere instance: %s", cfg.Global.VCenterIP)
}
err = vcInstance.conn.Connect(ctx)
if err != nil {
t.Errorf("Failed to connect to vSphere: %s", err)
}
vcInstance.conn.Logout(ctx)
}
func TestVSphereLoginByToken(t *testing.T) {
cfg, cleanup := configFromSim()
defer cleanup()
// Configure for SAML token auth
cfg.Global.User = localhostCert
cfg.Global.Password = localhostKey
// Create vSphere configuration object
vs, err := newControllerNode(cfg)
if err != nil {
t.Fatalf("Failed to construct/authenticate vSphere: %s", err)
}
ctx := context.Background()
// Create vSphere client
vcInstance, ok := vs.vsphereInstanceMap[cfg.Global.VCenterIP]
if !ok {
t.Fatalf("Couldn't get vSphere instance: %s", cfg.Global.VCenterIP)
}
err = vcInstance.conn.Connect(ctx)
if err != nil {
t.Errorf("Failed to connect to vSphere: %s", err)
}
vcInstance.conn.Logout(ctx)
}
func TestVSphereLoginWithCaCert(t *testing.T) {
caCertPEM, err := ioutil.ReadFile(fixtures.CaCertPath)
if err != nil {
t.Fatalf("Could not read ca cert from file")
}
serverCert, err := tls.LoadX509KeyPair(fixtures.ServerCertPath, fixtures.ServerKeyPath)
if err != nil {
t.Fatalf("Could not load server cert and server key from files: %#v", err)
}
certPool := x509.NewCertPool()
if ok := certPool.AppendCertsFromPEM(caCertPEM); !ok {
t.Fatalf("Cannot add CA to CAPool")
}
tlsConfig := tls.Config{
Certificates: []tls.Certificate{serverCert},
RootCAs: certPool,
}
cfg, cleanup := configFromSimWithTLS(&tlsConfig, false)
defer cleanup()
cfg.Global.CAFile = fixtures.CaCertPath
// Create vSphere configuration object
vs, err := newControllerNode(cfg)
if err != nil {
t.Fatalf("Failed to construct/authenticate vSphere: %s", err)
}
ctx := context.Background()
// Create vSphere client
vcInstance, ok := vs.vsphereInstanceMap[cfg.Global.VCenterIP]
if !ok {
t.Fatalf("Couldn't get vSphere instance: %s", cfg.Global.VCenterIP)
}
err = vcInstance.conn.Connect(ctx)
if err != nil {
t.Errorf("Failed to connect to vSphere: %s", err)
}
vcInstance.conn.Logout(ctx)
}
func TestZonesNoConfig(t *testing.T) {
_, ok := new(VSphere).Zones()
if ok {
t.Fatalf("Zones() should return false without VCP configured")
}
}
func TestZones(t *testing.T) {
// Any context will do
ctx := context.Background()
// Create a vcsim instance
cfg, cleanup := configFromSim()
defer cleanup()
// Create vSphere configuration object
vs, err := newControllerNode(cfg)
if err != nil {
t.Fatalf("Failed to construct/authenticate vSphere: %s", err)
}
// Configure region and zone categories
vs.cfg.Labels.Region = "k8s-region"
vs.cfg.Labels.Zone = "k8s-zone"
// Create vSphere client
vsi, ok := vs.vsphereInstanceMap[cfg.Global.VCenterIP]
if !ok {
t.Fatalf("Couldn't get vSphere instance: %s", cfg.Global.VCenterIP)
}
err = vsi.conn.Connect(ctx)
if err != nil {
t.Errorf("Failed to connect to vSphere: %s", err)
}
// Lookup Datacenter for this test's Workspace
dc, err := vclib.GetDatacenter(ctx, vsi.conn, vs.cfg.Workspace.Datacenter)
if err != nil {
t.Fatal(err)
}
// Lookup VM's host where we'll attach tags
host, err := dc.GetHostByVMUUID(ctx, vs.vmUUID)
if err != nil {
t.Fatal(err)
}
// Property Collector instance
pc := property.DefaultCollector(vsi.conn.Client)
// Tag manager instance
m := tags.NewManager(rest.NewClient(vsi.conn.Client))
// Create a region category
regionID, err := m.CreateCategory(ctx, &tags.Category{Name: vs.cfg.Labels.Region})
if err != nil {
t.Fatal(err)
}
// Create a region tag
regionID, err = m.CreateTag(ctx, &tags.Tag{CategoryID: regionID, Name: "k8s-region-US"})
if err != nil {
t.Fatal(err)
}
// Create a zone category
zoneID, err := m.CreateCategory(ctx, &tags.Category{Name: vs.cfg.Labels.Zone})
if err != nil {
t.Fatal(err)
}
// Create a zone tag
zoneID, err = m.CreateTag(ctx, &tags.Tag{CategoryID: zoneID, Name: "k8s-zone-US-CA1"})
if err != nil {
t.Fatal(err)
}
// Create a random category
randomID, err := m.CreateCategory(ctx, &tags.Category{Name: "random-cat"})
if err != nil {
t.Fatal(err)
}
// Create a random tag
randomID, err = m.CreateTag(ctx, &tags.Tag{CategoryID: randomID, Name: "random-tag"})
if err != nil {
t.Fatal(err)
}
// Attach a random tag to VM's host
if err = m.AttachTag(ctx, randomID, host); err != nil {
t.Fatal(err)
}
// Expecting Zones() to return true, indicating VCP supports the Zones interface
zones, ok := vs.Zones()
if !ok {
t.Fatalf("zones=%t", ok)
}
// GetZone() tests, covering error and success paths
tests := []struct {
name string // name of the test for logging
fail bool // expect GetZone() to return error if true
prep func() // prepare vCenter state for the test
}{
{"no tags", true, func() {
// no prep
}},
{"no zone tag", true, func() {
if err = m.AttachTag(ctx, regionID, host); err != nil {
t.Fatal(err)
}
}},
{"host tags set", false, func() {
if err = m.AttachTag(ctx, zoneID, host); err != nil {
t.Fatal(err)
}
}},
{"host tags removed", true, func() {
if err = m.DetachTag(ctx, zoneID, host); err != nil {
t.Fatal(err)
}
if err = m.DetachTag(ctx, regionID, host); err != nil {
t.Fatal(err)
}
}},
{"dc region, cluster zone", false, func() {
var h mo.HostSystem
if err = pc.RetrieveOne(ctx, host.Reference(), []string{"parent"}, &h); err != nil {
t.Fatal(err)
}
// Attach region tag to Datacenter
if err = m.AttachTag(ctx, regionID, dc); err != nil {
t.Fatal(err)
}
// Attach zone tag to Cluster
if err = m.AttachTag(ctx, zoneID, h.Parent); err != nil {
t.Fatal(err)
}
}},
}
for _, test := range tests {
test.prep()
zone, err := zones.GetZone(ctx)
if test.fail {
if err == nil {
t.Errorf("%s: expected error", test.name)
} else {
t.Logf("%s: expected error=%s", test.name, err)
}
} else {
if err != nil {
t.Errorf("%s: %s", test.name, err)
}
t.Logf("zone=%#v", zone)
}
}
}
func TestInstances(t *testing.T) {
cfg, ok := configFromEnv()
if !ok {
t.Skipf("No config found in environment")
}
vs, err := newControllerNode(cfg)
if err != nil {
t.Fatalf("Failed to construct/authenticate vSphere: %s", err)
}
i, ok := vs.Instances()
if !ok {
t.Fatalf("Instances() returned false")
}
nodeName, err := vs.CurrentNodeName(context.TODO(), "")
if err != nil {
t.Fatalf("CurrentNodeName() failed: %s", err)
}
nonExistingVM := types.NodeName(rand.String(15))
instanceID, err := i.InstanceID(context.TODO(), nodeName)
if err != nil {
t.Fatalf("Instances.InstanceID(%s) failed: %s", nodeName, err)
}
t.Logf("Found InstanceID(%s) = %s\n", nodeName, instanceID)
_, err = i.InstanceID(context.TODO(), nonExistingVM)
if err == cloudprovider.InstanceNotFound {
t.Logf("VM %s was not found as expected\n", nonExistingVM)
} else if err == nil {
t.Fatalf("Instances.InstanceID did not fail as expected, VM %s was found", nonExistingVM)
} else {
t.Fatalf("Instances.InstanceID did not fail as expected, err: %v", err)
}
addrs, err := i.NodeAddresses(context.TODO(), nodeName)
if err != nil {
t.Fatalf("Instances.NodeAddresses(%s) failed: %s", nodeName, err)
}
found := false
for _, addr := range addrs {
if addr.Type == v1.NodeHostName {
found = true
}
}
if found == false {
t.Fatalf("NodeAddresses does not report hostname, %s %s", nodeName, addrs)
}
t.Logf("Found NodeAddresses(%s) = %s\n", nodeName, addrs)
}
func TestVolumes(t *testing.T) {
cfg, ok := configFromEnv()
if !ok {
t.Skipf("No config found in environment")
}
vs, err := newControllerNode(cfg)
if err != nil {
t.Fatalf("Failed to construct/authenticate vSphere: %s", err)
}
nodeName, err := vs.CurrentNodeName(context.TODO(), "")
if err != nil {
t.Fatalf("CurrentNodeName() failed: %s", err)
}
volumeOptions := &vclib.VolumeOptions{
CapacityKB: 1 * 1024 * 1024,
Tags: nil,
Name: "kubernetes-test-volume-" + rand.String(10),
DiskFormat: "thin"}
volPath, err := vs.CreateVolume(volumeOptions)
if err != nil {
t.Fatalf("Cannot create a new VMDK volume: %v", err)
}
_, err = vs.AttachDisk(volPath, "", "")
if err != nil {
t.Fatalf("Cannot attach volume(%s) to VM(%s): %v", volPath, nodeName, err)
}
err = vs.DetachDisk(volPath, "")
if err != nil {
t.Fatalf("Cannot detach disk(%s) from VM(%s): %v", volPath, nodeName, err)
}
// todo: Deleting a volume after detach currently not working through API or UI (vSphere)
// err = vs.DeleteVolume(volPath)
// if err != nil {
// t.Fatalf("Cannot delete VMDK volume %s: %v", volPath, err)
// }
}
func TestSecretVSphereConfig(t *testing.T) {
var vs *VSphere
var (
username = "user"
password = "password"
)
var testcases = []struct {
testName string
conf string
expectedIsSecretProvided bool
expectedUsername string
expectedPassword string
expectedError error
expectedThumbprints map[string]string
}{
{
testName: "Username and password with old configuration",
conf: `[Global]
server = 0.0.0.0
user = user
password = password
datacenter = us-west
working-dir = kubernetes
`,
expectedUsername: username,
expectedPassword: password,
expectedError: nil,
},
{
testName: "SecretName and SecretNamespace in old configuration",
conf: `[Global]
server = 0.0.0.0
datacenter = us-west
secret-name = "vccreds"
secret-namespace = "kube-system"
working-dir = kubernetes
`,
expectedIsSecretProvided: true,
expectedError: nil,
},
{
testName: "SecretName and SecretNamespace with Username and Password in old configuration",
conf: `[Global]
server = 0.0.0.0
user = user
password = password
datacenter = us-west
secret-name = "vccreds"
secret-namespace = "kube-system"
working-dir = kubernetes
`,
expectedIsSecretProvided: true,
expectedError: nil,
},
{
testName: "SecretName and SecretNamespace with Username missing in old configuration",
conf: `[Global]
server = 0.0.0.0
password = password
datacenter = us-west
secret-name = "vccreds"
secret-namespace = "kube-system"
working-dir = kubernetes
`,
expectedIsSecretProvided: true,
expectedError: nil,
},
{
testName: "SecretNamespace missing with Username and Password in old configuration",
conf: `[Global]
server = 0.0.0.0
user = user
password = password
datacenter = us-west
secret-name = "vccreds"
working-dir = kubernetes
`,
expectedUsername: username,
expectedPassword: password,
expectedError: nil,
},
{
testName: "SecretNamespace and Username missing in old configuration",
conf: `[Global]
server = 0.0.0.0
password = password
datacenter = us-west
secret-name = "vccreds"
working-dir = kubernetes
`,
expectedError: ErrUsernameMissing,
},
{
testName: "SecretNamespace and Password missing in old configuration",
conf: `[Global]
server = 0.0.0.0
user = user
datacenter = us-west
secret-name = "vccreds"
working-dir = kubernetes
`,
expectedError: ErrPasswordMissing,
},
{
testName: "SecretNamespace, Username and Password missing in old configuration",
conf: `[Global]
server = 0.0.0.0
datacenter = us-west
secret-name = "vccreds"
working-dir = kubernetes
`,
expectedError: ErrUsernameMissing,
},
{
testName: "Username and password with new configuration but username and password in global section",
conf: `[Global]
user = user
password = password
datacenter = us-west
[VirtualCenter "0.0.0.0"]
[Workspace]
server = 0.0.0.0
datacenter = us-west
folder = kubernetes
`,
expectedUsername: username,
expectedPassword: password,
expectedError: nil,
},
{
testName: "Username and password with new configuration, username and password in virtualcenter section",
conf: `[Global]
server = 0.0.0.0
port = 443
insecure-flag = true
datacenter = us-west
[VirtualCenter "0.0.0.0"]
user = user
password = password
[Workspace]
server = 0.0.0.0
datacenter = us-west
folder = kubernetes
`,
expectedUsername: username,
expectedPassword: password,
expectedError: nil,
},
{
testName: "SecretName and SecretNamespace with new configuration",
conf: `[Global]
server = 0.0.0.0
secret-name = "vccreds"
secret-namespace = "kube-system"
datacenter = us-west
[VirtualCenter "0.0.0.0"]
[Workspace]
server = 0.0.0.0
datacenter = us-west
folder = kubernetes
`,
expectedIsSecretProvided: true,
expectedError: nil,
},
{
testName: "SecretName and SecretNamespace with Username missing in new configuration",
conf: `[Global]
server = 0.0.0.0
port = 443
insecure-flag = true
datacenter = us-west
secret-name = "vccreds"
secret-namespace = "kube-system"
[VirtualCenter "0.0.0.0"]
password = password
[Workspace]
server = 0.0.0.0
datacenter = us-west
folder = kubernetes
`,
expectedIsSecretProvided: true,
expectedError: nil,
},
{
testName: "virtual centers with a thumbprint",
conf: `[Global]
server = global
user = user
password = password
datacenter = us-west
thumbprint = "thumbprint:global"
working-dir = kubernetes
`,
expectedUsername: username,
expectedPassword: password,
expectedError: nil,
expectedThumbprints: map[string]string{
"global": "thumbprint:global",
},
},
{
testName: "Multiple virtual centers with different thumbprints",
conf: `[Global]
user = user
password = password
datacenter = us-west
[VirtualCenter "0.0.0.0"]
thumbprint = thumbprint:0
[VirtualCenter "no_thumbprint"]
[VirtualCenter "1.1.1.1"]
thumbprint = thumbprint:1
[Workspace]
server = 0.0.0.0
datacenter = us-west
folder = kubernetes
`,
expectedUsername: username,
expectedPassword: password,
expectedError: nil,
expectedThumbprints: map[string]string{
"0.0.0.0": "thumbprint:0",
"1.1.1.1": "thumbprint:1",
},
},
{
testName: "Multiple virtual centers use the global CA cert",
conf: `[Global]
user = user
password = password
datacenter = us-west
ca-file = /some/path/to/my/trusted/ca.pem
[VirtualCenter "0.0.0.0"]
user = user
password = password
[VirtualCenter "1.1.1.1"]
user = user
password = password
[Workspace]
server = 0.0.0.0
datacenter = us-west
folder = kubernetes
`,
expectedUsername: username,
expectedPassword: password,
expectedError: nil,
},
}
for _, testcase := range testcases {
t.Logf("Executing Testcase: %s", testcase.testName)
cfg, err := readConfig(strings.NewReader(testcase.conf))
if err != nil {
t.Fatalf("Should succeed when a valid config is provided: %s", err)
}
vs, err = buildVSphereFromConfig(cfg)
if err != testcase.expectedError {
t.Fatalf("Should succeed when a valid config is provided: %s", err)
}
if err != nil {
continue
}
if vs.isSecretInfoProvided != testcase.expectedIsSecretProvided {
t.Fatalf("SecretName and SecretNamespace was expected in config %s. error: %s",
testcase.conf, err)
}
if !testcase.expectedIsSecretProvided {
for _, vsInstance := range vs.vsphereInstanceMap {
if vsInstance.conn.Username != testcase.expectedUsername {
t.Fatalf("Expected username %s doesn't match actual username %s in config %s. error: %s",
testcase.expectedUsername, vsInstance.conn.Username, testcase.conf, err)
}
if vsInstance.conn.Password != testcase.expectedPassword {
t.Fatalf("Expected password %s doesn't match actual password %s in config %s. error: %s",
testcase.expectedPassword, vsInstance.conn.Password, testcase.conf, err)
}
}
}
// Check, if all the expected thumbprints are configured
for instanceName, expectedThumbprint := range testcase.expectedThumbprints {
instanceConfig, ok := vs.vsphereInstanceMap[instanceName]
if !ok {
t.Fatalf("Could not find configuration for instance %s", instanceName)
}
if actualThumbprint := instanceConfig.conn.Thumbprint; actualThumbprint != expectedThumbprint {
t.Fatalf(
"Expected thumbprint for instance '%s' to be '%s', got '%s'",
instanceName, expectedThumbprint, actualThumbprint,
)
}
}
// Check, if all all connections are configured with the global CA certificate
if expectedCaPath := cfg.Global.CAFile; expectedCaPath != "" {
for name, instance := range vs.vsphereInstanceMap {
if actualCaPath := instance.conn.CACert; actualCaPath != expectedCaPath {
t.Fatalf(
"Expected CA certificate path for instance '%s' to be the globally configured one ('%s'), got '%s'",
name, expectedCaPath, actualCaPath,
)
}
}
}
}
}