Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,8 @@ required = [
branch = "master"
name = "github.com/openshift/machine-api-operator"
source = "github.com/openshift/machine-api-operator"

[[constraint]]
branch = "master"
name = "github.com/vmware/govmomi"
source = "https://github.com/vmware/govmomi"
1 change: 1 addition & 0 deletions cmd/openshift-install/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
_ "github.com/openshift/installer/pkg/destroy/libvirt"
_ "github.com/openshift/installer/pkg/destroy/openstack"
_ "github.com/openshift/installer/pkg/destroy/ovirt"
_ "github.com/openshift/installer/pkg/destroy/vsphere"
)

func newDestroyCmd() *cobra.Command {
Expand Down
5 changes: 4 additions & 1 deletion pkg/asset/cluster/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/openshift/installer/pkg/asset/cluster/libvirt"
"github.com/openshift/installer/pkg/asset/cluster/openstack"
"github.com/openshift/installer/pkg/asset/cluster/ovirt"
"github.com/openshift/installer/pkg/asset/cluster/vsphere"
"github.com/openshift/installer/pkg/asset/installconfig"
"github.com/openshift/installer/pkg/types"
awstypes "github.com/openshift/installer/pkg/types/aws"
Expand Down Expand Up @@ -80,7 +81,9 @@ func (m *Metadata) Generate(parents asset.Parents) (err error) {
metadata.ClusterPlatformMetadata.BareMetal = baremetal.Metadata(installConfig.Config)
case ovirttypes.Name:
metadata.ClusterPlatformMetadata.Ovirt = ovirt.Metadata(installConfig.Config)
case nonetypes.Name, vspheretypes.Name:
case vspheretypes.Name:
metadata.ClusterPlatformMetadata.VSphere = vsphere.Metadata(installConfig.Config)
case nonetypes.Name:
default:
return errors.Errorf("no known platform")
}
Expand Down
15 changes: 15 additions & 0 deletions pkg/asset/cluster/vsphere/vsphere.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package vsphere

import (
"github.com/openshift/installer/pkg/types"
"github.com/openshift/installer/pkg/types/vsphere"
)

// Metadata converts an install configuration to vSphere metadata.
func Metadata(config *types.InstallConfig) *vsphere.Metadata {
return &vsphere.Metadata{
VCenter: config.VSphere.VCenter,
Username: config.VSphere.Username,
Password: config.VSphere.Password,
}
}
2 changes: 2 additions & 0 deletions pkg/destroy/vsphere/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package vsphere provides a cluster-destroyer for vsphere clusters
package vsphere
10 changes: 10 additions & 0 deletions pkg/destroy/vsphere/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Package vsphere provides a cluster-destroyer for vsphere clusters.
package vsphere

import (
"github.com/openshift/installer/pkg/destroy/providers"
)

func init() {
providers.Registry["vsphere"] = New
}
198 changes: 198 additions & 0 deletions pkg/destroy/vsphere/vsphere.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package vsphere

import (
"context"
"time"

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

"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vapi/rest"
"github.com/vmware/govmomi/vapi/tags"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"

"github.com/openshift/installer/pkg/destroy/providers"
installertypes "github.com/openshift/installer/pkg/types"
vspheretypes "github.com/openshift/installer/pkg/types/vsphere"
)

// ClusterUninstaller holds the various options for the cluster we want to delete.
type ClusterUninstaller struct {
ClusterID string
InfraID string

Client *vim25.Client
RestClient *rest.Client

Logger logrus.FieldLogger
}

// New returns an VSphere destroyer from ClusterMetadata.
func New(logger logrus.FieldLogger, metadata *installertypes.ClusterMetadata) (providers.Destroyer, error) {

vim25Client, restClient, err := vspheretypes.CreateVSphereClients(context.TODO(),
metadata.ClusterPlatformMetadata.VSphere.VCenter,
metadata.ClusterPlatformMetadata.VSphere.Username,
metadata.ClusterPlatformMetadata.VSphere.Password)

if err != nil {
return nil, err
}

return &ClusterUninstaller{
ClusterID: metadata.ClusterID,
InfraID: metadata.InfraID,
Client: vim25Client,
RestClient: restClient,
Logger: logger,
}, nil
}

func deleteVirtualMachines(ctx context.Context, client *vim25.Client, virtualMachineMoList []mo.VirtualMachine, parentFolder mo.Folder, logger logrus.FieldLogger) error {
ctx, cancel := context.WithTimeout(ctx, time.Minute*30)
defer cancel()

if len(virtualMachineMoList) != 0 {
for _, vmMO := range virtualMachineMoList {
virtualMachineLogger := logger.WithField("VirtualMachine", vmMO.Name)
// Checking if the Folder mobRef is the same as the VirtualMachine parent mobRef
if parentFolder.Reference() == vmMO.Parent.Reference() {
vm := object.NewVirtualMachine(client, vmMO.Reference())
if vmMO.Summary.Runtime.PowerState == "poweredOn" {
task, err := vm.PowerOff(ctx)
if err != nil {
return err
}
task.Wait(ctx)
virtualMachineLogger.Debug("Powered off")
}

task, err := vm.Destroy(ctx)
if err != nil {
return err
}
task.Wait(ctx)
virtualMachineLogger.Info("Destroyed")
}
}
}
return nil
}
func deleteFolder(ctx context.Context, client *vim25.Client, folderMoList []mo.Folder, logger logrus.FieldLogger) error {
ctx, cancel := context.WithTimeout(ctx, time.Second*60)
defer cancel()

// If there are no children in the folder go ahead an remove it
if len(folderMoList[0].ChildEntity) == 0 {
folderLogger := logger.WithField("Folder", folderMoList[0].Name)

folder := object.NewFolder(client, folderMoList[0].Reference())
task, err := folder.Destroy(ctx)
if err != nil {
return err
}
task.Wait(ctx)
folderLogger.Info("Destroyed")
} else {
return errors.Errorf("Expected Folder %s to be empty", folderMoList[0].Name)
}

return nil
}
func getFolderManagedObjects(ctx context.Context, client *vim25.Client, moRef []types.ManagedObjectReference) ([]mo.Folder, error) {
var folderMoList []mo.Folder
pc := property.DefaultCollector(client)
err := pc.Retrieve(ctx, moRef, nil, &folderMoList)
if err != nil {
return nil, err
}
return folderMoList, nil
}
func getVirtualMachineManagedObjects(ctx context.Context, client *vim25.Client, moRef []types.ManagedObjectReference) ([]mo.VirtualMachine, error) {
var virtualMachineMoList []mo.VirtualMachine

pc := property.DefaultCollector(client)
err := pc.Retrieve(ctx, moRef, nil, &virtualMachineMoList)
if err != nil {
return nil, err
}
return virtualMachineMoList, nil
}

func getAttachedObjectsOnTag(ctx context.Context, client *rest.Client, tagName string) ([]tags.AttachedObjects, error) {
tagManager := tags.NewManager(client)
attached, err := tagManager.GetAttachedObjectsOnTags(ctx, []string{tagName})
if err != nil {
return nil, err
}

return attached, nil
}

// Run is the entrypoint to start the uninstall process.
func (o *ClusterUninstaller) Run() error {
var folderList []types.ManagedObjectReference
var virtualMachineList []types.ManagedObjectReference

o.Logger.Debug("find attached objects on tag")
tagAttachedObjects, err := getAttachedObjectsOnTag(context.TODO(), o.RestClient, o.InfraID)
if err != nil {
return err
}

// Seperate the objects attached to the tag based on type
// We only need Folder and VirtualMachine
for _, attachedObject := range tagAttachedObjects {
for _, ref := range attachedObject.ObjectIDs {
if ref.Reference().Type == "Folder" {
folderList = append(folderList, ref.Reference())
}
if ref.Reference().Type == "VirtualMachine" {
virtualMachineList = append(virtualMachineList, ref.Reference())
}
}
}

// There should be exactly one Folder which is created by
// terraform. That is the parent to the VirtualMachines.
// If there are more or less fail with error message.
if len(folderList) != 1 {
return errors.Errorf("Expected 1 Folder per tag but got %d", len(folderList))
}

o.Logger.Debug("find Folder objects")
folderMoList, err := getFolderManagedObjects(context.TODO(), o.Client, folderList)

o.Logger.Debug("find VirtualMachine objects")
virtualMachineMoList, err := getVirtualMachineManagedObjects(context.TODO(), o.Client, virtualMachineList)
if err != nil {
return err
}
o.Logger.Debug("delete VirtualMachines")
err = deleteVirtualMachines(context.TODO(), o.Client, virtualMachineMoList, folderMoList[0], o.Logger)
if err != nil {
return err
}
// When removing virtual machines the mo.Folder object does not change
// We need to re-retrieve the list again
o.Logger.Debug("find Folder objects")
folderMoList = nil
folderMoList, err = getFolderManagedObjects(context.TODO(), o.Client, folderList)
if err != nil {
o.Logger.Errorln(err)
return err
}

o.Logger.Debug("delete Folder")
err = deleteFolder(context.TODO(), o.Client, folderMoList, o.Logger)
if err != nil {
o.Logger.Errorln(err)
return err
}

return nil
}
5 changes: 5 additions & 0 deletions pkg/types/clustermetadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/openshift/installer/pkg/types/libvirt"
"github.com/openshift/installer/pkg/types/openstack"
"github.com/openshift/installer/pkg/types/ovirt"
"github.com/openshift/installer/pkg/types/vsphere"
)

// ClusterMetadata contains information
Expand All @@ -31,6 +32,7 @@ type ClusterPlatformMetadata struct {
GCP *gcp.Metadata `json:"gcp,omitempty"`
BareMetal *baremetal.Metadata `json:"baremetal,omitempty"`
Ovirt *ovirt.Metadata `json:"ovirt,omitempty"`
VSphere *vsphere.Metadata `json:"vsphere,omitempty"`
}

// Platform returns a string representation of the platform
Expand Down Expand Up @@ -61,5 +63,8 @@ func (cpm *ClusterPlatformMetadata) Platform() string {
if cpm.Ovirt != nil {
return ovirt.Name
}
if cpm.VSphere != nil {
return vsphere.Name
}
return ""
}
39 changes: 39 additions & 0 deletions pkg/types/vsphere/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package vsphere

import (
"context"
"net/url"
"time"

"github.com/vmware/govmomi"
"github.com/vmware/govmomi/vapi/rest"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/soap"
)

// CreateVSphereClients creates the SOAP and REST client to access
// different portions of the vSphere API
// e.g. tags are only available in REST
func CreateVSphereClients(ctx context.Context, vcenter, username, password string) (*vim25.Client, *rest.Client, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()

u, err := soap.ParseURL(vcenter)
if err != nil {
return nil, nil, err
}
u.User = url.UserPassword(username, password)
c, err := govmomi.NewClient(ctx, u, false)

if err != nil {
return nil, nil, err
}

restClient := rest.NewClient(c.Client)
err = restClient.Login(ctx, u.User)
if err != nil {
return nil, nil, err
}

return c.Client, restClient, nil
}
11 changes: 11 additions & 0 deletions pkg/types/vsphere/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package vsphere

// Metadata contains vSphere metadata (e.g. for uninstalling the cluster).
type Metadata struct {
// VCenter is the domain name or IP address of the vCenter.
VCenter string `json:"vCenter"`
// Username is the name of the user to use to connect to the vCenter.
Username string `json:"username"`
// Password is the password for the user to use to connect to the vCenter.
Password string `json:"password"`
}
Loading