Skip to content

Commit

Permalink
[feat] Support usage of separate Linode API for the domain client (#436)
Browse files Browse the repository at this point in the history
* Use default http transport instead of oauth2.Transport

* Improve client/import naming to avoid confusion

* Pull and apply new custom dns client envs

* Inform about fallback of dns token to general api token

* Handle timeouts with compatible client

* Don't count emitted log lines in tests

* Update tests with new dns params

* Impl. ClientConfig

* Update getting started docs

* object_storage_bucket: Rm unnecessary nolint:dupl

* Populate system cert pool if root cert not provided

* Rework doc additions into dns-loadbalancing.md
  • Loading branch information
mabojars authored Aug 5, 2024
1 parent 1b4a2b9 commit 1590966
Show file tree
Hide file tree
Showing 26 changed files with 257 additions and 168 deletions.
6 changes: 3 additions & 3 deletions cloud/scope/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func validateClusterScopeParams(params ClusterScopeParams) error {

// NewClusterScope creates a new Scope from the supplied parameters.
// This is meant to be called for each reconcile iteration.
func NewClusterScope(ctx context.Context, apiKey string, params ClusterScopeParams) (*ClusterScope, error) {
func NewClusterScope(ctx context.Context, linodeClientConfig ClientConfig, params ClusterScopeParams) (*ClusterScope, error) {
if err := validateClusterScopeParams(params); err != nil {
return nil, err
}
Expand All @@ -62,9 +62,9 @@ func NewClusterScope(ctx context.Context, apiKey string, params ClusterScopePara
if err != nil {
return nil, fmt.Errorf("credentials from secret ref: %w", err)
}
apiKey = string(apiToken)
linodeClientConfig.Token = string(apiToken)
}
linodeClient, err := CreateLinodeClient(apiKey, defaultClientTimeout)
linodeClient, err := CreateLinodeClient(linodeClientConfig)
if err != nil {
return nil, fmt.Errorf("failed to create linode client: %w", err)
}
Expand Down
10 changes: 5 additions & 5 deletions cloud/scope/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func TestClusterScopeMethods(t *testing.T) {

cScope, err := NewClusterScope(
context.Background(),
"test-key",
ClientConfig{Token: "test-key"},
ClusterScopeParams{
Cluster: testcase.fields.Cluster,
LinodeCluster: testcase.fields.LinodeCluster,
Expand Down Expand Up @@ -297,7 +297,7 @@ func TestNewClusterScope(t *testing.T) {
LinodeCluster: &infrav1alpha2.LinodeCluster{},
},
},
expectedError: fmt.Errorf("failed to create linode client: missing Linode API key"),
expectedError: fmt.Errorf("failed to create linode client: token cannot be empty"),
expects: func(mock *mock.MockK8sClient) {},
},
}
Expand All @@ -316,7 +316,7 @@ func TestNewClusterScope(t *testing.T) {

testcase.args.params.Client = mockK8sClient

got, err := NewClusterScope(context.Background(), testcase.args.apiKey, testcase.args.params)
got, err := NewClusterScope(context.Background(), ClientConfig{Token: testcase.args.apiKey}, testcase.args.params)

if testcase.expectedError != nil {
assert.ErrorContains(t, err, testcase.expectedError.Error())
Expand Down Expand Up @@ -411,7 +411,7 @@ func TestClusterAddCredentialsRefFinalizer(t *testing.T) {

cScope, err := NewClusterScope(
context.Background(),
"test-key",
ClientConfig{Token: "test-key"},
ClusterScopeParams{
Cluster: testcase.fields.Cluster,
LinodeCluster: testcase.fields.LinodeCluster,
Expand Down Expand Up @@ -512,7 +512,7 @@ func TestRemoveCredentialsRefFinalizer(t *testing.T) {

cScope, err := NewClusterScope(
context.Background(),
"test-key",
ClientConfig{Token: "test-key"},
ClusterScopeParams{
Cluster: testcase.fields.Cluster,
LinodeCluster: testcase.fields.LinodeCluster,
Expand Down
57 changes: 44 additions & 13 deletions cloud/scope/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package scope

import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net/http"
Expand All @@ -13,7 +15,6 @@ import (
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/edgegrid"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/session"
"github.com/linode/linodego"
"golang.org/x/oauth2"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
Expand Down Expand Up @@ -44,29 +45,59 @@ func WithRetryCount(c int) Option {
}
}

func CreateLinodeClient(apiKey string, timeout time.Duration, opts ...Option) (LinodeClient, error) {
if apiKey == "" {
return nil, errors.New("missing Linode API key")
type ClientConfig struct {
Token string
BaseUrl string
RootCertificatePath string

Timeout time.Duration
}

func CreateLinodeClient(config ClientConfig, opts ...Option) (LinodeClient, error) {
if config.Token == "" {
return nil, errors.New("token cannot be empty")
}

tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: apiKey})
timeout := defaultClientTimeout
if config.Timeout != 0 {
timeout = config.Timeout
}

oauth2Client := &http.Client{
Transport: &oauth2.Transport{
Source: tokenSource,
},
// Use system cert pool if root CA cert was not provided explicitly for this client.
// Works around linodego not using system certs if LINODE_CA is provided,
// which affects all clients spawned via linodego.NewClient
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12}
if config.RootCertificatePath == "" {
systemCertPool, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("failed to load system cert pool: %w", err)
}
tlsConfig.RootCAs = systemCertPool
}

httpClient := &http.Client{
Timeout: timeout,
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
linodeClient := linodego.NewClient(oauth2Client)

linodeClient.SetUserAgent(fmt.Sprintf("CAPL/%s", version.GetVersion()))
newClient := linodego.NewClient(httpClient)
newClient.SetToken(config.Token)
if config.RootCertificatePath != "" {
newClient.SetRootCertificate(config.RootCertificatePath)
}
if config.BaseUrl != "" {
newClient.SetBaseURL(config.BaseUrl)
}
newClient.SetUserAgent(fmt.Sprintf("CAPL/%s", version.GetVersion()))

for _, opt := range opts {
opt.set(&linodeClient)
opt.set(&newClient)
}

return linodeclient.NewLinodeClientWithTracing(
&linodeClient,
&newClient,
linodeclient.DefaultDecorator(),
), nil
}
Expand Down
32 changes: 23 additions & 9 deletions cloud/scope/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"testing"
"time"

"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
Expand All @@ -22,29 +23,42 @@ func TestCreateLinodeClient(t *testing.T) {
t.Parallel()

tests := []struct {
name string
apiKey string
expectedErr error
name string
token string
baseUrl string
rootCertificatePath string
timeout time.Duration
expectedErr error
}{
{
"Success - Valid API Key",
"Success - Valid API token",
"test-key",
"",
"",
0,
nil,
},
{
"Error - Empty API Key",
"Error - Empty API token",
"",
"",
"",
errors.New("missing Linode API key"),
0,
errors.New("token cannot be empty"),
},
}

for _, tt := range tests {
testCase := tt
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()

got, err := CreateLinodeClient(testCase.apiKey, defaultClientTimeout)

clientConfig := ClientConfig{
Token: testCase.token,
Timeout: testCase.timeout,
BaseUrl: testCase.baseUrl,
RootCertificatePath: testCase.rootCertificatePath,
}
got, err := CreateLinodeClient(clientConfig)
if testCase.expectedErr != nil {
assert.EqualError(t, err, testCase.expectedErr.Error())
} else {
Expand Down
10 changes: 5 additions & 5 deletions cloud/scope/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func validateMachineScopeParams(params MachineScopeParams) error {
return nil
}

func NewMachineScope(ctx context.Context, apiKey, dnsKey string, params MachineScopeParams) (*MachineScope, error) {
func NewMachineScope(ctx context.Context, linodeClientConfig, dnsClientConfig ClientConfig, params MachineScopeParams) (*MachineScope, error) {
if err := validateMachineScopeParams(params); err != nil {
return nil, err
}
Expand Down Expand Up @@ -84,22 +84,22 @@ func NewMachineScope(ctx context.Context, apiKey, dnsKey string, params MachineS
if err != nil {
return nil, fmt.Errorf("credentials from secret ref: %w", err)
}
apiKey = string(apiToken)
linodeClientConfig.Token = string(apiToken)

dnsToken, err := getCredentialDataFromRef(ctx, params.Client, *credentialRef, defaultNamespace, "dnsToken")
if err != nil || len(dnsToken) == 0 {
dnsToken = apiToken
}
dnsKey = string(dnsToken)
dnsClientConfig.Token = string(dnsToken)
}

linodeClient, err := CreateLinodeClient(apiKey, defaultClientTimeout,
linodeClient, err := CreateLinodeClient(linodeClientConfig,
WithRetryCount(0),
)
if err != nil {
return nil, fmt.Errorf("failed to create linode client: %w", err)
}
linodeDomainsClient, err := CreateLinodeClient(dnsKey, defaultClientTimeout,
linodeDomainsClient, err := CreateLinodeClient(dnsClientConfig,
WithRetryCount(0),
)
if err != nil {
Expand Down
Loading

0 comments on commit 1590966

Please sign in to comment.