Skip to content

Commit 91f0afa

Browse files
authored
defang --provider aws shows improved error if unauthenticated (#808)
* updated tests for NewByocClient * renamed to NewByocProvider * more minor revisions to variable names * edited os to t getenv in integration teest * edited to use AccountInfo rather than region * made error-wrapping for ErrMissingAwsCreds * fixes missing struct field error
1 parent 0d63dbc commit 91f0afa

File tree

5 files changed

+101
-17
lines changed

5 files changed

+101
-17
lines changed

src/pkg/cli/client/byoc/aws/byoc.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,29 @@ type ByocAws struct {
6262

6363
var _ client.Provider = (*ByocAws)(nil)
6464

65-
func NewByocProvider(ctx context.Context, tenantId types.TenantID) *ByocAws {
65+
type ErrMissingAwsCreds struct {
66+
err error
67+
}
68+
69+
func (e ErrMissingAwsCreds) Error() string {
70+
return "AWS credentials must be set (https://docs.defang.io/docs/providers/aws/#getting-started)"
71+
}
72+
73+
func (e ErrMissingAwsCreds) Unwrap() error {
74+
return e.err
75+
}
76+
77+
func NewByocProvider(ctx context.Context, tenantId types.TenantID) (*ByocAws, error) {
6678
b := &ByocAws{
6779
driver: cfn.New(byoc.CdTaskPrefix, aws.Region("")), // default region
6880
}
6981
b.ByocBaseClient = byoc.NewByocBaseClient(ctx, tenantId, b)
70-
return b
82+
83+
_, err := b.AccountInfo(ctx)
84+
if err != nil {
85+
return b, ErrMissingAwsCreds{err: err}
86+
}
87+
return b, nil
7188
}
7289

7390
func (b *ByocAws) setUpCD(ctx context.Context, projectName string) (string, error) {

src/pkg/cli/client/byoc/aws/byoc_integration_test.go

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package aws
44

55
import (
66
"context"
7+
"errors"
78
"strings"
89
"testing"
910

@@ -15,7 +16,14 @@ import (
1516
var ctx = context.Background()
1617

1718
func TestDeploy(t *testing.T) {
18-
b := NewByocProvider(ctx, client.GrpcClient{}, "ten ant") // no domain
19+
b, err := NewByocProvider(ctx, client.GrpcClient{}, "ten ant") // no domain
20+
if err != nil {
21+
var credErr ErrMissingAwsCreds
22+
if errors.As(err, &credErr) {
23+
t.Skip("skipping test; not authenticated")
24+
}
25+
t.Fatalf("unexpected error: %v", err)
26+
}
1927
b.ProjectName = "byoc_integration_test"
2028

2129
t.Run("multiple ingress without domain", func(t *testing.T) {
@@ -41,17 +49,24 @@ func TestDeploy(t *testing.T) {
4149
}
4250

4351
func TestTail(t *testing.T) {
44-
b := NewByocProvider(ctx, client.GrpcClient{}, "TestTail")
52+
b, err := NewByocProvider(ctx, client.GrpcClient{}, "TestTail")
53+
if err != nil {
54+
var credErr ErrMissingAwsCreds
55+
if errors.As(err, &credErr) {
56+
t.Skip("skipping test; not authenticated")
57+
}
58+
t.Fatalf("unexpected error: %v", err)
59+
}
4560
b.ProjectName = "byoc_integration_test"
4661
b.ProjectDomain = "example.com" // avoid rpc call
4762

4863
ss, err := b.Follow(context.Background(), &defangv1.TailRequest{})
4964
if err != nil {
5065
// the only acceptable error is "unauthorized"
51-
if connect.CodeOf(err) != connect.CodeUnauthenticated {
52-
t.Fatal(err)
66+
if connect.CodeOf(err) == connect.CodeUnauthenticated {
67+
t.Skip("skipping test; not authorized")
5368
}
54-
t.Skip("skipping test; not authorized")
69+
t.Fatalf("unexpected error: %v", err)
5570
}
5671
defer ss.Close()
5772

@@ -69,7 +84,14 @@ func TestTail(t *testing.T) {
6984
}
7085

7186
func TestGetServices(t *testing.T) {
72-
b := NewByocProvider(ctx, client.GrpcClient{}, "TestGetServices")
87+
b, err := NewByocProvider(ctx, client.GrpcClient{}, "TestGetServices")
88+
if err != nil {
89+
var credErr ErrMissingAwsCreds
90+
if errors.As(err, &credErr) {
91+
t.Skip("skipping test; not authenticated")
92+
}
93+
t.Fatalf("unexpected error: %v", err)
94+
}
7395
b.ProjectName = "byoc_integration_test"
7496

7597
services, err := b.GetServices(context.Background())
@@ -78,7 +100,7 @@ func TestGetServices(t *testing.T) {
78100
t.Skip("skipping test; not authorized")
79101
}
80102
// the only acceptable error is "unauthorized"
81-
t.Fatal(err)
103+
t.Fatalf("unexpected error: %v", err)
82104
}
83105

84106
if len(services.Services) != 0 {
@@ -89,7 +111,14 @@ func TestGetServices(t *testing.T) {
89111
func TestPutSecret(t *testing.T) {
90112
const secretName = "hello"
91113

92-
b := NewByocProvider(ctx, client.GrpcClient{}, "TestPutSecret")
114+
b, err := NewByocProvider(ctx, client.GrpcClient{}, "TestPutSecret")
115+
if err != nil {
116+
var credErr ErrMissingAwsCreds
117+
if errors.As(err, &credErr) {
118+
t.Skip("skipping test; not authenticated")
119+
}
120+
t.Fatalf("unexpected error: %v", err)
121+
}
93122
b.ProjectName = "byoc_integration_test"
94123

95124
t.Run("delete non-existent", func(t *testing.T) {
@@ -141,7 +170,14 @@ func TestPutSecret(t *testing.T) {
141170
}
142171

143172
func TestListSecrets(t *testing.T) {
144-
b := NewByocProvider(ctx, client.GrpcClient{}, "TestListSecrets")
173+
b, err := NewByocProvider(ctx, client.GrpcClient{}, "TestListSecrets")
174+
if err != nil {
175+
var credErr ErrMissingAwsCreds
176+
if errors.As(err, &credErr) {
177+
t.Skip("skipping test; not authenticated")
178+
}
179+
t.Fatalf("unexpected error: %v", err)
180+
}
145181
b.ProjectName = "byoc_integration_test2" // ensure we don't accidentally see the secrets from the other test
146182

147183
t.Run("list", func(t *testing.T) {

src/pkg/cli/client/byoc/aws/byoc_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"context"
77
"embed"
88
"encoding/json"
9+
"errors"
910
"io"
1011
"path"
1112
"strings"
@@ -14,7 +15,9 @@ import (
1415

1516
"github.com/DefangLabs/defang/src/pkg/cli/client/byoc"
1617
"github.com/DefangLabs/defang/src/pkg/cli/compose"
18+
"github.com/DefangLabs/defang/src/pkg/clouds/aws"
1719
"github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs"
20+
"github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs/cfn"
1821
"github.com/DefangLabs/defang/src/pkg/types"
1922
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
2023
composeTypes "github.com/compose-spec/compose-go/v2/types"
@@ -44,11 +47,17 @@ func TestDomainMultipleProjectSupport(t *testing.T) {
4447
{"Project1", "tenant1", "web", port80, "web--80.project1.example.com", "web.project1.example.com", "web.project1.internal"},
4548
{"Tenant2", "tenant1", "web", port80, "web--80.tenant2.example.com", "web.tenant2.example.com", "web.tenant2.internal"},
4649
{"tenant1", "tenAnt1", "web", port80, "web--80.example.com", "web.example.com", "web.tenant1.internal"},
50+
{"tenant1", "tenant1", "web", port80, "web--80.example.com", "web.example.com", "web.tenant1.internal"},
4751
}
4852

4953
for _, tt := range tests {
5054
t.Run(tt.ProjectName+","+string(tt.TenantID), func(t *testing.T) {
51-
b := NewByocProvider(context.Background(), tt.TenantID)
55+
//like calling NewByocProvider(), but without needing real AccountInfo data
56+
b := &ByocAws{
57+
driver: cfn.New(byoc.CdTaskPrefix, aws.Region("")), // default region
58+
}
59+
b.ByocBaseClient = byoc.NewByocBaseClient(context.Background(), tt.TenantID, b)
60+
5261
const delegateDomain = "example.com"
5362

5463
endpoint := b.getEndpoint(tt.Fqn, tt.ProjectName, delegateDomain, tt.Port)
@@ -161,9 +170,28 @@ func TestSubscribe(t *testing.T) {
161170
}
162171
}
163172

173+
func TestNewByocProvider(t *testing.T) {
174+
t.Run("no aws credentials", func(t *testing.T) {
175+
_, err := NewByocProvider(context.Background(), "tenant1")
176+
if err != nil {
177+
var credErr ErrMissingAwsCreds
178+
if !errors.As(err, &credErr) {
179+
t.Fatalf("NewByocProvider() failed: %v", err)
180+
}
181+
} else {
182+
t.Fatal("NewByocProvider() failed: expected MissingAwsCreds error but didn't get one")
183+
}
184+
})
185+
}
186+
164187
func TestGetCDImageTag(t *testing.T) {
165188
ctx := context.Background()
166-
b := NewByocProvider(ctx, "tenant1")
189+
190+
//like calling NewByocProvider(), but without needing real AccountInfo data
191+
b := &ByocAws{
192+
driver: cfn.New(byoc.CdTaskPrefix, aws.Region("")), // default region
193+
}
194+
b.ByocBaseClient = byoc.NewByocBaseClient(context.Background(), "tenant1", b)
167195

168196
t.Run("no project should use latest", func(t *testing.T) {
169197
const expected = byoc.CdLatestImageTag

src/pkg/cli/connect.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,18 @@ func NewProvider(ctx context.Context, providerID client.ProviderID, grpcClient c
6060
switch providerID {
6161
case client.ProviderAWS:
6262
term.Info("Using AWS provider")
63-
awsProvider := aws.NewByocProvider(ctx, grpcClient.TenantID)
63+
awsProvider, err := aws.NewByocProvider(ctx, grpcClient.TenantID)
64+
if err != nil {
65+
term.Fatal(err)
66+
}
6467
return awsProvider
6568
case client.ProviderDO:
6669
term.Info("Using DigitalOcean provider")
67-
byocProvider, err := do.NewByocProvider(ctx, grpcClient.TenantID)
70+
doProvider, err := do.NewByocProvider(ctx, grpcClient.TenantID)
6871
if err != nil {
6972
term.Fatal(err)
7073
}
71-
return byocProvider
74+
return doProvider
7275
default:
7376
term.Info("Using Defang Playground; consider using BYOC (https://s.defang.io/byoc)")
7477
return &client.PlaygroundProvider{GrpcClient: grpcClient}

src/pkg/clouds/do/appPlatform/setup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ func waitForActiveDeployment(ctx context.Context, apps godo.AppsService, appID s
234234
func NewClient(ctx context.Context) (*godo.Client, error) {
235235
accessToken := os.Getenv("DIGITALOCEAN_TOKEN")
236236
if accessToken == "" {
237-
return nil, errors.New("DIGITALOCEAN_TOKEN must be set")
237+
return nil, errors.New("DIGITALOCEAN_TOKEN must be set (https://docs.defang.io/docs/providers/digitalocean#getting-started)")
238238
}
239239
tokenSource := &oauth2.Token{AccessToken: accessToken}
240240
httpClient := oauth2.NewClient(ctx, oauth2.StaticTokenSource(tokenSource))

0 commit comments

Comments
 (0)