Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/vela-server/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func setupStorage(_ context.Context, c *cli.Command) (storage.Storage, error) {
SecretKey: c.String("storage.secret.key"),
Bucket: c.String("storage.bucket.name"),
Secure: c.Bool("storage.use.ssl"),
UseIAM: c.Bool("storage.use.iam"),
}
// setup the storage
//
Expand Down
95 changes: 95 additions & 0 deletions cmd/vela-server/storage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: Apache-2.0

package main

import (
"context"
"testing"

"github.com/urfave/cli/v3"

"github.com/go-vela/server/storage"
)

func TestSetupStorage(t *testing.T) {
tests := []struct {
name string
args []string
wantNil bool
wantErr bool
}{
{
name: "storage disabled",
args: []string{"test"},
wantNil: true,
wantErr: false,
},
{
name: "storage enabled with static credentials",
args: []string{
"test",
"--storage.enable=true",
"--storage.driver=minio",
"--storage.addr=http://localhost:9000",
"--storage.access.key=access",
"--storage.secret.key=secret",
"--storage.bucket.name=bucket",
},
wantNil: false,
wantErr: false,
},
{
name: "storage enabled with IAM credentials",
args: []string{
"test",
"--storage.enable=true",
"--storage.driver=minio",
"--storage.addr=http://localhost:9000",
"--storage.bucket.name=bucket",
"--storage.use.iam=true",
},
wantNil: false,
wantErr: false,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var (
got storage.Storage
gotErr error
)

cmd := &cli.Command{
Name: "test",
Flags: storage.Flags,
Action: func(ctx context.Context, c *cli.Command) error {
got, gotErr = setupStorage(ctx, c)
return nil
},
}

_ = cmd.Run(context.Background(), tc.args)

if tc.wantErr {
if gotErr == nil {
t.Error("expected error, got nil")
}

return
}

if gotErr != nil {
t.Errorf("unexpected error: %v", gotErr)
}

if tc.wantNil && got != nil {
t.Errorf("expected nil storage, got %v", got)
}

if !tc.wantNil && got == nil {
t.Error("expected storage client, got nil")
}
})
}
}
6 changes: 6 additions & 0 deletions storage/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,10 @@ var Flags = []cli.Flag{
Value: false,
Sources: cli.EnvVars("VELA_STORAGE_USE_SSL"),
},
&cli.BoolFlag{
Name: "storage.use.iam",
Usage: "AWS IAM role credentials for storage",
Value: false,
Sources: cli.EnvVars("VELA_STORAGE_USE_IAM"),
},
}
12 changes: 10 additions & 2 deletions storage/minio/minio.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type config struct {
Secure bool
Token string
Driver string
UseIAM bool
}

// Client implements the Storage interface using MinIO.
Expand Down Expand Up @@ -56,7 +57,14 @@ func New(endpoint string, opts ...ClientOpt) (*Client, error) {
}
}

c.Options.Creds = credentials.NewStaticV4(c.config.AccessKey, c.config.SecretKey, c.config.Token)
// resolve credentials either from an AWS IAM role (EC2/ECS/EKS instance
// metadata or a web identity token) or from static access/secret keys.
if c.config.UseIAM {
c.Options.Creds = credentials.NewIAM("")
} else {
c.Options.Creds = credentials.NewStaticV4(c.config.AccessKey, c.config.SecretKey, c.config.Token)
}

c.Options.Secure = c.config.Secure

urlEndpoint, err := url.Parse(endpoint)
Expand All @@ -81,6 +89,6 @@ func New(endpoint string, opts ...ClientOpt) (*Client, error) {
// This function is intended for running tests only.
func NewTest(endpoint, accessKey, secretKey, bucket string, secure bool) (*Client, error) {
return New(endpoint,
WithOptions(true, secure,
WithOptions(true, secure, false,
endpoint, accessKey, secretKey, bucket, "", constants.DriverMinio))
}
2 changes: 1 addition & 1 deletion storage/minio/minio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestMinio_New(t *testing.T) {
for _, test := range tests {
_, err := New(
test.endpoint,
WithOptions(true, _useSSL,
WithOptions(true, _useSSL, false,
test.endpoint, _accessKey, _secretKey, _bucket, "", constants.DriverMinio),
)

Expand Down
18 changes: 11 additions & 7 deletions storage/minio/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import (
type ClientOpt func(client *Client) error

// WithOptions sets multiple options in the MinIO client.
func WithOptions(enable, secure bool, endpoint, accessKey, secretKey, bucket, token, driver string) ClientOpt {
func WithOptions(enable, secure, useIAM bool, endpoint, accessKey, secretKey, bucket, token, driver string) ClientOpt {
return func(c *Client) error {
c.Logger.Trace("configuring multiple options in minio client")

if len(accessKey) == 0 {
return fmt.Errorf("no MinIO access key provided")
}
// check if the secret key provided is empty
if len(secretKey) == 0 {
return fmt.Errorf("no MinIO secret key provided")
if !useIAM {
if len(accessKey) == 0 {
return fmt.Errorf("no MinIO access key provided")
}
// check if the secret key provided is empty
if len(secretKey) == 0 {
return fmt.Errorf("no MinIO secret key provided")
}
}
// check if the bucket name provided is empty
if len(bucket) == 0 {
Expand All @@ -35,6 +37,8 @@ func WithOptions(enable, secure bool, endpoint, accessKey, secretKey, bucket, to
c.config.AccessKey = accessKey
// set the secure connection mode in the minio client
c.config.Secure = secure
// set whether to use IAM role credentials in the minio client
c.config.UseIAM = useIAM
// set the bucket name in the minio client
c.config.Bucket = bucket
// set the token in the minio client
Expand Down
58 changes: 58 additions & 0 deletions storage/minio/opts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,61 @@ func TestWithBucket(t *testing.T) {
}
}
}

func TestWithUseIAM(t *testing.T) {
// setup tests
tests := []struct {
name string
useIAM bool
accessKey string
secretKey string
failure bool
}{
{
name: "iam enabled tolerates empty static keys",
useIAM: true,
accessKey: "",
secretKey: "",
failure: false,
},
{
name: "static mode requires keys",
useIAM: false,
accessKey: "",
secretKey: "",
failure: true,
},
{
name: "iam enabled with keys still succeeds",
useIAM: true,
accessKey: "minioaccess",
secretKey: "miniosecret",
failure: false,
},
}

// run tests
for _, test := range tests {
client, err := New("https://minio.example.com",
WithOptions(true, false, test.useIAM,
"https://minio.example.com", test.accessKey, test.secretKey, "foo", "", "minio"))

if test.failure {
if err == nil {
t.Errorf("%s: WithOptions should have returned err", test.name)
}

continue
}

if err != nil {
t.Errorf("%s: WithOptions returned err: %v", test.name, err)

continue
}

if client.config.UseIAM != test.useIAM {
t.Errorf("%s: UseIAM is %v, want %v", test.name, client.config.UseIAM, test.useIAM)
}
}
}
4 changes: 3 additions & 1 deletion storage/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Setup struct {
Region string
Secure bool
Token string
UseIAM bool
}

// Minio creates and returns a Vela service capable
Expand All @@ -35,6 +36,7 @@ func (s *Setup) Minio() (Storage, error) {
minio.WithOptions(
s.Enable,
s.Secure,
s.UseIAM,
s.Endpoint,
s.AccessKey,
s.SecretKey,
Expand Down Expand Up @@ -64,7 +66,7 @@ func (s *Setup) Validate() error {
return fmt.Errorf("storage is enabled but no endpoint provided")
}

if s.AccessKey == "" || s.SecretKey == "" {
if !s.UseIAM && (s.AccessKey == "" || s.SecretKey == "") {
return fmt.Errorf("storage is enabled but no access key or secret key provided")
}

Expand Down
Loading