krystal/kce-ccm

View on GitHub
kce/interface.go

Summary

Maintainability
A
0 mins
Test Coverage
D
60%
package kce

import (
    "context"
    "fmt"
    "github.com/go-logr/logr"
    "github.com/krystal/go-katapult"
    "github.com/krystal/go-katapult/core"
    "github.com/sethvargo/go-envconfig"
    "io"
    cloudprovider "k8s.io/cloud-provider"
    "k8s.io/klog/v2/klogr"
    "net/url"
)

type Config struct {
    APIKey  string `env:"KATAPULT_API_TOKEN"`
    APIHost string `env:"KATAPULT_API_HOST"`

    OrganizationID string `env:"KATAPULT_ORGANIZATION_RID"`
    DataCenterID   string `env:"KATAPULT_DATA_CENTER_RID"`

    NodeTagID string `env:"KATAPULT_NODE_TAG_RID"`
}

func (c Config) orgRef() core.OrganizationRef {
    return core.OrganizationRef{
        ID: c.OrganizationID,
    }
}

func (c Config) dcRef() core.DataCenterRef {
    return core.DataCenterRef{
        ID: c.DataCenterID,
    }
}

func loadConfig(lookuper envconfig.Lookuper) (*Config, error) {
    c := Config{}

    err := envconfig.ProcessWith(context.TODO(), &c, lookuper)
    if err != nil {
        return nil, err
    }

    if c.APIKey == "" {
        return nil, fmt.Errorf("api key is not configured")
    }

    if c.OrganizationID == "" {
        return nil, fmt.Errorf("organization id is not set")
    }

    if c.DataCenterID == "" {
        return nil, fmt.Errorf("data center id is not set")
    }

    if c.NodeTagID == "" {
        return nil, fmt.Errorf("node tag id is not set")
    }

    return &c, nil
}

// providerFactory creates any dependencies needed by the provider and passes
// them into New. For now, we will source config from the environment, but we
// k8s CCM provides us with an io.Reader which can be used to read a config
// file.
func providerFactory(_ io.Reader) (cloudprovider.Interface, error) {
    log := klogr.NewWithOptions(klogr.WithFormat(klogr.FormatKlog))
    c, err := loadConfig(envconfig.OsLookuper())
    if err != nil {
        return nil, err
    }

    apiUrl := katapult.DefaultURL
    if c.APIHost != "" {
        log.Info("default API base URL overrided",
            "url", c.APIHost)
        apiUrl, err = url.Parse(c.APIHost)
        if err != nil {
            return nil, fmt.Errorf("failed to parse provided api url: %w", err)
        }
    }

    rm, err := katapult.New(
        katapult.WithAPIKey(c.APIKey),
        katapult.WithBaseURL(apiUrl),
        katapult.WithUserAgent("kce-ccm"), // TODO: Add version.
    )
    if err != nil {
        return nil, err
    }
    client := core.New(rm)

    return &provider{
        log:      log,
        katapult: client,
        config:   *c,
        loadBalancer: &loadBalancerManager{
            log:                        log,
            config:                     *c,
            loadBalancerController:     client.LoadBalancers,
            loadBalancerRuleController: client.LoadBalancerRules,
        },
    }, nil
}

type provider struct {
    log          logr.Logger
    katapult     *core.Client
    config       Config
    loadBalancer *loadBalancerManager
}

func (p *provider) Initialize(
    clientBuilder cloudprovider.ControllerClientBuilder,
    stop <-chan struct{}) {
    // TODO: Assess if we actually need anything here
}

// LoadBalancer returns our implementation of the loadBalancerManager provider
func (p *provider) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
    return p.loadBalancer, true
}

func (p *provider) Instances() (cloudprovider.Instances, bool) {
    return nil, false
}

func (p *provider) InstancesV2() (cloudprovider.InstancesV2, bool) {
    return nil, false
}

func (p *provider) Zones() (cloudprovider.Zones, bool) {
    return nil, false
}

func (p *provider) Clusters() (cloudprovider.Clusters, bool) {
    return nil, false
}

func (p *provider) Routes() (cloudprovider.Routes, bool) {
    return nil, false
}

func (p *provider) ProviderName() string {
    return ProviderName
}

func (p *provider) HasClusterID() bool {
    return true
}