Skip to content

Commit

Permalink
Merge pull request #4147 from timoreimann/digitalocean/support-readin…
Browse files Browse the repository at this point in the history
…g-access-token-from-file

digitalocean: support reading access token from file
  • Loading branch information
k8s-ci-robot authored Jun 16, 2021
2 parents 7d7df8c + 05e2011 commit 1b502e0
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 4 deletions.
14 changes: 14 additions & 0 deletions cluster-autoscaler/cloudprovider/digitalocean/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ offering which can be enabled/disable dynamically for an existing cluster.

# Configuration

## Cloud config file

The (JSON) configuration file of the DigitalOcean cloud provider supports the
following values:

- `cluster_id`: the ID of the cluster (a UUID)
- `token`: the DigitalOcean access token literally defined
- `token_file`: a file path containing the DigitalOcean access token
- `url`: the DigitalOcean URL (optional; defaults to `https://api.digitalocean.com/`)

Exactly one of `token` or `token_file` must be provided.

## Behavior

Parameters of the autoscaler (such as whether it is on or off, and the
minimum/maximum values) are configured through the public DOKS API and
subsequently reflected by the node pool objects. The cloud provider periodically
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package digitalocean

import (
"bytes"
"context"
"encoding/json"
"errors"
Expand All @@ -26,7 +27,7 @@ import (

"github.com/digitalocean/godo"
"golang.org/x/oauth2"
klog "k8s.io/klog/v2"
"k8s.io/klog/v2"
)

var (
Expand Down Expand Up @@ -62,6 +63,10 @@ type Config struct {
// DigitalOcean Cluster Autoscaler is running.
Token string `json:"token"`

// TokenFile references the token from the given file. It cannot be specified
// together with Token.
TokenFile string `json:"token_file"`

// URL points to DigitalOcean API. If empty, defaults to
// https://api.digitalocean.com/
URL string `json:"url"`
Expand All @@ -80,13 +85,28 @@ func newManager(configReader io.Reader) (*Manager, error) {
}
}

if cfg.Token == "" {
if cfg.Token == "" && cfg.TokenFile == "" {
return nil, errors.New("access token is not provided")
}
if cfg.Token != "" && cfg.TokenFile != "" {
return nil, errors.New("access token literal and access token file must not be provided together")
}
if cfg.ClusterID == "" {
return nil, errors.New("cluster ID is not provided")
}

if cfg.TokenFile != "" {
tokenData, err := ioutil.ReadFile(cfg.TokenFile)
if err != nil {
return nil, fmt.Errorf("failed to read token file: %s", err)
}
tokenData = bytes.TrimSpace(tokenData)
if len(tokenData) == 0 {
return nil, fmt.Errorf("token file %q is empty", cfg.TokenFile)
}
cfg.Token = string(tokenData)
}

tokenSource := oauth2.StaticTokenSource(&oauth2.Token{
AccessToken: cfg.Token,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,22 @@ import (

"github.com/digitalocean/godo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNewManager(t *testing.T) {
t.Run("success", func(t *testing.T) {
t.Run("success with literal token", func(t *testing.T) {
cfg := `{"cluster_id": "123456", "token": "123-123-123", "url": "https://api.digitalocean.com/v2", "version": "dev"}`

manager, err := newManager(bytes.NewBufferString(cfg))
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, manager.clusterID, "123456", "cluster ID does not match")
})
t.Run("success with token file", func(t *testing.T) {
cfg := `{"cluster_id": "123456", "token_file": "testdata/correct_token", "url": "https://api.digitalocean.com/v2", "version": "dev"}`

manager, err := newManager(bytes.NewBufferString(cfg))
require.NoError(t, err)
assert.Equal(t, manager.clusterID, "123456", "cluster ID does not match")
})

Expand All @@ -41,7 +49,31 @@ func TestNewManager(t *testing.T) {
_, err := newManager(bytes.NewBufferString(cfg))
assert.EqualError(t, err, errors.New("access token is not provided").Error())
})
t.Run("literal and token file", func(t *testing.T) {
cfg := `{"cluster_id": "123456", "token": "123-123-123", "token_file": "tokendata/correct_token", "url": "https://api.digitalocean.com/v2", "version": "dev"}`

_, err := newManager(bytes.NewBufferString(cfg))
assert.EqualError(t, err, errors.New("access token literal and access token file must not be provided together").Error())
})
t.Run("missing token file", func(t *testing.T) {
cfg := `{"cluster_id": "123456", "token_file": "testdata/missing_token", "url": "https://api.digitalocean.com/v2", "version": "dev"}`

_, err := newManager(bytes.NewBufferString(cfg))
require.NotNil(t, err)
assert.Contains(t, err.Error(), "failed to read token file")
})
t.Run("empty token file", func(t *testing.T) {
cfg := `{"cluster_id": "123456", "token_file": "testdata/empty_token", "url": "https://api.digitalocean.com/v2", "version": "dev"}`

_, err := newManager(bytes.NewBufferString(cfg))
assert.EqualError(t, err, errors.New(`token file "testdata/empty_token" is empty`).Error())
})
t.Run("all whitespace token file", func(t *testing.T) {
cfg := `{"cluster_id": "123456", "token_file": "testdata/whitespace_token", "url": "https://api.digitalocean.com/v2", "version": "dev"}`

_, err := newManager(bytes.NewBufferString(cfg))
assert.EqualError(t, err, errors.New(`token file "testdata/whitespace_token" is empty`).Error())
})
t.Run("empty cluster ID", func(t *testing.T) {
cfg := `{"cluster_id": "", "token": "123-123-123", "url": "https://api.digitalocean.com/v2", "version": "dev"}`

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
123-123-123
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@


0 comments on commit 1b502e0

Please sign in to comment.