Skip to content

Commit

Permalink
migrate to instances v2
Browse files Browse the repository at this point in the history
  • Loading branch information
varshavaradarajan committed Nov 18, 2024
1 parent c20703f commit 0805a4c
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 433 deletions.
15 changes: 6 additions & 9 deletions cloud-controller-manager/do/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ func (t *tokenSource) Token() (*oauth2.Token, error) {

type cloud struct {
client *godo.Client
instances cloudprovider.Instances
zones cloudprovider.Zones
instances cloudprovider.InstancesV2
loadbalancers cloudprovider.LoadBalancer
metrics metrics

Expand Down Expand Up @@ -161,7 +160,6 @@ func newCloud() (cloudprovider.Interface, error) {
return &cloud{
client: doClient,
instances: newInstances(resources, region),
zones: newZones(resources, region),
loadbalancers: newLoadBalancers(resources, region),
metrics: newMetrics(addr),
resources: resources,
Expand Down Expand Up @@ -247,18 +245,17 @@ func (c *cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
}

func (c *cloud) Instances() (cloudprovider.Instances, bool) {
return c.instances, true
return nil, false
}

func (c *cloud) InstancesV2() (cloudprovider.InstancesV2, bool) {
// TODO: Implement the InstancesV2 interface. Our API should be sufficient
// to fetch all the necessary implementation, but it's not required at the
// moment.
return nil, false
return c.instances, true
}

func (c *cloud) Zones() (cloudprovider.Zones, bool) {
return c.zones, true
// Won't be called when using InstancesV2
// For adding zone info in the future, update the InstanceMetadata method
return nil, false
}

func (c *cloud) Clusters() (cloudprovider.Clusters, bool) {
Expand Down
161 changes: 39 additions & 122 deletions cloud-controller-manager/do/droplets.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,123 +26,39 @@ import (

"github.com/digitalocean/godo"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
cloudprovider "k8s.io/cloud-provider"
)

const (
dropletShutdownStatus = "off"
)

// instances implements the InstancesV2() interface
type instances struct {
region string
resources *resources
}

func newInstances(resources *resources, region string) cloudprovider.Instances {
func newInstances(resources *resources, region string) cloudprovider.InstancesV2 {
return &instances{
resources: resources,
region: region,
}
}

// NodeAddresses returns all the valid addresses of the droplet identified by
// nodeName. Only the public/private IPv4 addresses are considered for now.
//
// When nodeName identifies more than one droplet, only the first will be
// considered.
func (i *instances) NodeAddresses(ctx context.Context, nodeName types.NodeName) ([]v1.NodeAddress, error) {
droplet, err := dropletByName(ctx, i.resources.gclient, nodeName)
if err != nil {
return nil, err
}

return nodeAddresses(droplet)
}

// NodeAddressesByProviderID returns all the valid addresses of the droplet
// identified by providerID. Only the public/private IPv4 addresses will be
// considered for now.
func (i *instances) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) {
id, err := dropletIDFromProviderID(providerID)
if err != nil {
return nil, err
}

droplet, err := dropletByID(ctx, i.resources.gclient, id)
if err != nil {
return nil, err
}

return nodeAddresses(droplet)
}

// ExternalID returns the cloud provider ID of the droplet identified by
// nodeName. If the droplet does not exist or is no longer running, the
// returned error will be cloudprovider.InstanceNotFound.
//
// When nodeName identifies more than one droplet, only the first will be
// considered.
func (i *instances) ExternalID(ctx context.Context, nodeName types.NodeName) (string, error) {
return i.InstanceID(ctx, nodeName)
}

// InstanceID returns the cloud provider ID of the droplet identified by nodeName.
func (i *instances) InstanceID(ctx context.Context, nodeName types.NodeName) (string, error) {
droplet, err := dropletByName(ctx, i.resources.gclient, nodeName)
if err != nil {
return "", err
}
return strconv.Itoa(droplet.ID), nil
}

// InstanceType returns the type of the droplet identified by name.
func (i *instances) InstanceType(ctx context.Context, name types.NodeName) (string, error) {
droplet, err := dropletByName(ctx, i.resources.gclient, name)
if err != nil {
return "", err
}

return droplet.SizeSlug, nil
}

// InstanceTypeByProviderID returns the type of the droplet identified by providerID.
func (i *instances) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) {
id, err := dropletIDFromProviderID(providerID)
if err != nil {
return "", err
}
// cloudprovider.InstancesV2 methods
// InstancesV2 require ProviderID to be present, so the interface methods all use providerID to get droplet.

droplet, err := dropletByID(ctx, i.resources.gclient, id)
func (i *instances) InstanceExists(ctx context.Context, node *v1.Node) (bool, error) {
dropletID, err := dropletIDFromProviderID(node.Spec.ProviderID)
if err != nil {
return "", err
return false, fmt.Errorf("determining droplet ID from providerID: %s", err.Error())
}

return droplet.SizeSlug, err
}

// AddSSHKeyToAllInstances is not implemented; it always returns an error.
func (i *instances) AddSSHKeyToAllInstances(_ context.Context, _ string, _ []byte) error {
return errors.New("not implemented")
}

// CurrentNodeName returns hostname as a NodeName value.
func (i *instances) CurrentNodeName(_ context.Context, hostname string) (types.NodeName, error) {
return types.NodeName(hostname), nil
}

// InstanceExistsByProviderID returns true if the droplet identified by
// providerID is running.
func (i *instances) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) {
// NOTE: when false is returned with no error, the instance will be
// immediately deleted by the cloud controller manager.

id, err := dropletIDFromProviderID(providerID)
if err != nil {
return false, err
}

_, err = dropletByID(ctx, i.resources.gclient, id)
_, err = dropletByID(ctx, i.resources.gclient, dropletID)
if err == nil {
return true, nil
}
Expand All @@ -159,51 +75,52 @@ func (i *instances) InstanceExistsByProviderID(ctx context.Context, providerID s
return false, nil
}

// InstanceShutdownByProviderID returns true if the droplet is turned off
func (i *instances) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) {
dropletID, err := dropletIDFromProviderID(providerID)
func (i *instances) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error) {
dropletID, err := dropletIDFromProviderID(node.Spec.ProviderID)
if err != nil {
return false, fmt.Errorf("error getting droplet ID from provider ID %q: %s", providerID, err)
return false, fmt.Errorf("determining droplet ID from providerID: %s", err.Error())
}

droplet, err := dropletByID(ctx, i.resources.gclient, dropletID)
if err != nil {
return false, fmt.Errorf("error getting droplet \"%d\" by ID: %s", dropletID, err)
return false, fmt.Errorf("getting droplet by ID: %s: ", err.Error())
}
if droplet == nil {
return false, fmt.Errorf("droplet %d for node %s does not exist", dropletID, node.Name)
}

return droplet.Status == dropletShutdownStatus, nil
}

// dropletByID returns a *godo.Droplet value for the droplet identified by id.
func dropletByID(ctx context.Context, client *godo.Client, id int) (*godo.Droplet, error) {
droplet, _, err := client.Droplets.Get(ctx, id)
return droplet, err
}

// dropletByName returns a *godo.Droplet for the droplet identified by nodeName.
//
// When nodeName identifies more than one droplet, only the first will be
// considered.
func dropletByName(ctx context.Context, client *godo.Client, nodeName types.NodeName) (*godo.Droplet, error) {
// TODO (andrewsykim): list by tag once a tagging format is determined
droplets, err := allDropletList(ctx, client)
func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
dropletID, err := dropletIDFromProviderID(node.Spec.ProviderID)
if err != nil {
return nil, err
return nil, fmt.Errorf("determining droplet ID from providerID: %s", err.Error())
}

for _, droplet := range droplets {
if droplet.Name == string(nodeName) {
return &droplet, nil
}
addresses, _ := nodeAddresses(&droplet)
for _, address := range addresses {
if address.Address == string(nodeName) {
return &droplet, nil
}
}
droplet, err := dropletByID(ctx, i.resources.gclient, dropletID)
if err != nil {
return nil, fmt.Errorf("getting droplet by ID: %s: ", err.Error())
}
if droplet == nil {
return nil, fmt.Errorf("droplet %d for node %s does not exist", dropletID, node.Name)
}
nodeAddrs, err := nodeAddresses(droplet)
if err != nil {
return nil, fmt.Errorf("getting node addresses of droplet %d for node %s: %s", dropletID, node.Name, err.Error())
}
return &cloudprovider.InstanceMetadata{
ProviderID: node.Spec.ProviderID, // the providerID may or may not be present according to the interface doc. However, we set this from kubelet.
InstanceType: droplet.SizeSlug,
Region: droplet.Region.Slug,
NodeAddresses: nodeAddrs,
}, nil
}

return nil, cloudprovider.InstanceNotFound
// dropletByID returns a *godo.Droplet value for the droplet identified by id.
func dropletByID(ctx context.Context, client *godo.Client, id int) (*godo.Droplet, error) {
droplet, _, err := client.Droplets.Get(ctx, id)
return droplet, err
}

// dropletIDFromProviderID returns a droplet's ID from providerID.
Expand Down
Loading

0 comments on commit 0805a4c

Please sign in to comment.