diff --git a/vultr/cloud.go b/vultr/cloud.go index 4606a0b1..9dce1eb8 100644 --- a/vultr/cloud.go +++ b/vultr/cloud.go @@ -32,7 +32,7 @@ var Options struct { type cloud struct { client *govultr.Client - instances cloudprovider.Instances + instances cloudprovider.InstancesV2 zones cloudprovider.Zones loadbalancers cloudprovider.LoadBalancer } @@ -78,7 +78,7 @@ func newCloud() (cloudprovider.Interface, error) { return &cloud{ client: vultr, - instances: newInstances(vultr), + instances: newInstancesV2(vultr), zones: newZones(vultr, strings.ToLower(meta.Region.RegionCode)), loadbalancers: newLoadbalancers(vultr, strings.ToLower(meta.Region.RegionCode)), }, nil @@ -88,21 +88,22 @@ func (c *cloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, } func (c *cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { + klog.V(5).Info("called LoadBalancer") //nolint return c.loadbalancers, true } func (c *cloud) Instances() (cloudprovider.Instances, bool) { - return c.instances, true + return nil, false } func (c *cloud) InstancesV2() (cloudprovider.InstancesV2, bool) { - // TODO we will need to implement this but for now it is not required and experimental - return nil, false + klog.V(5).Info("called InstancesV2") //nolint + return c.instances, true } func (c *cloud) Zones() (cloudprovider.Zones, bool) { klog.V(5).Info("called Zones") //nolint - return c.zones, true + return nil, false } func (c *cloud) Clusters() (cloudprovider.Clusters, bool) { diff --git a/vultr/instancesv2.go b/vultr/instancesv2.go new file mode 100644 index 00000000..aedbf76e --- /dev/null +++ b/vultr/instancesv2.go @@ -0,0 +1,98 @@ +// Package vultr is vultr cloud specific implementation +package vultr + +import ( + "context" + "fmt" + + "github.com/vultr/govultr/v2" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + cloudprovider "k8s.io/cloud-provider" +) + +var _ cloudprovider.InstancesV2 = &instancesv2{} + +type instancesv2 struct { + client *govultr.Client +} + +const ( + PENDING = "pending" //nolint + ACTIVE = "active" //nolint + RESIZING = "resizing" //nolint +) + +func newInstancesV2(client *govultr.Client) cloudprovider.InstancesV2 { + return &instancesv2{client} +} + +// InstanceExists return bool whether or not the instance exists +func (i *instancesv2) InstanceExists(ctx context.Context, node *v1.Node) (bool, error) { + newNode, err := vultrByName(ctx, i.client, types.NodeName(node.Name)) + if err != nil { + return false, nil + } + + if newNode.Status == ACTIVE || newNode.Status == PENDING || newNode.Status == RESIZING { + return true, nil + } + + return false, nil +} + +// InstanceShutdown returns bool whether or not the instance is running or powered off +func (i *instancesv2) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error) { + newNode, err := vultrByName(ctx, i.client, types.NodeName(node.Name)) + if err != nil { + return false, err + } + + if newNode.PowerStatus != "running" { + return true, nil + } + + return false, nil +} + +// InstanceMetadata returns a struct of type InstanceMetadata containing the node information +func (i *instancesv2) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { + newNode, err := vultrByName(ctx, i.client, types.NodeName(node.Name)) + if err != nil { + return nil, err + } + + nodeAddress, err := i.nodeAddresses(newNode) + if err != nil { + return nil, err + } + + return &cloudprovider.InstanceMetadata{ + InstanceType: newNode.Plan, + ProviderID: fmt.Sprintf("vultr://%s", newNode.ID), + Region: newNode.Region, + NodeAddresses: nodeAddress, + }, nil +} + +// nodeAddresses gathers public/private IP addresses and returns a []v1.NodeAddress . +func (i *instancesv2) nodeAddresses(instance *govultr.Instance) ([]v1.NodeAddress, error) { + var addresses []v1.NodeAddress + + addresses = append(addresses, v1.NodeAddress{ + Type: v1.NodeHostName, + Address: instance.Label, + }) + + // make sure we have either pubic and private ip + if instance.InternalIP == "" || instance.MainIP == "" { + return nil, fmt.Errorf("require both public and private IP") + } + + addresses = append(addresses, + v1.NodeAddress{Type: v1.NodeInternalIP, Address: instance.InternalIP}, // private IP + v1.NodeAddress{Type: v1.NodeExternalIP, Address: instance.MainIP}, // public IP + ) + + return addresses, nil +}