Skip to content
Merged
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
5 changes: 3 additions & 2 deletions src/cmd/cli/command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ var RootCmd = &cobra.Command{
if global.NonInteractive {
err = global.Client.CheckLoginAndToS(ctx)
} else {
err = login.InteractiveRequireLoginAndToS(ctx, global.Client, global.Cluster)
global.Client, err = login.InteractiveRequireLoginAndToS(ctx, global.Client, global.Cluster)
}

return err
Expand All @@ -446,7 +446,8 @@ var RootCmd = &cobra.Command{
}

ctx := cmd.Context()
err := login.InteractiveRequireLoginAndToS(ctx, global.Client, global.Cluster)
var err error
global.Client, err = login.InteractiveRequireLoginAndToS(ctx, global.Client, global.Cluster)
if err != nil {
return err
}
Expand Down
15 changes: 10 additions & 5 deletions src/cmd/cli/command/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,26 +531,31 @@ func makeComposeConfigCmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()

session, err := newCommandSessionWithOpts(cmd, commandSessionOpts{
sessionx, err := newCommandSessionWithOpts(cmd, commandSessionOpts{
CheckAccountInfo: false,
})
if err != nil {
term.Warn("unable to load stack:", err, "- some information may not be up-to-date")
sessionx = &session.Session{
Loader: configureLoader(cmd),
Provider: client.NewPlaygroundProvider(global.Client, stacks.DefaultBeta),
Stack: &stacks.Parameters{Name: stacks.DefaultBeta, Provider: client.ProviderDefang},
}
}

_, err = session.Provider.AccountInfo(ctx)
_, err = sessionx.Provider.AccountInfo(ctx)
if err != nil {
term.Warn("unable to connect to cloud provider:", err, "- some information may not be up-to-date")
}

project, loadErr := session.Loader.LoadProject(ctx)
project, loadErr := sessionx.Loader.LoadProject(ctx)
if loadErr != nil {
if global.NonInteractive {
return loadErr
}

term.Error("Cannot load project:", loadErr)
project, err := session.Loader.CreateProjectForDebug()
project, err := sessionx.Loader.CreateProjectForDebug()
if err != nil {
term.Warn("Failed to create project for debug:", err)
return loadErr
Expand All @@ -567,7 +572,7 @@ func makeComposeConfigCmd() *cobra.Command {
}, loadErr)
}

_, _, err = cli.ComposeUp(ctx, global.Client, session.Provider, session.Stack, cli.ComposeUpParams{
_, _, err = cli.ComposeUp(ctx, global.Client, sessionx.Provider, sessionx.Stack, cli.ComposeUpParams{
Project: project,
UploadMode: compose.UploadModeIgnore,
Mode: modes.ModeUnspecified,
Expand Down
36 changes: 36 additions & 0 deletions src/cmd/cli/command/compose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package command

import (
"bytes"
"context"
"os"
"testing"

"github.com/DefangLabs/defang/src/pkg/cli/client"
"github.com/DefangLabs/defang/src/pkg/term"
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
"github.com/bufbuild/connect-go"
)

func TestInitializeTailCmd(t *testing.T) {
Expand Down Expand Up @@ -43,3 +45,37 @@ func TestPrintPlaygroundPortalServiceURLs(t *testing.T) {
t.Errorf("got %q, want %q", got, want)
}
}

type unauthedMockFabricClient struct {
client.MockFabricClient
}

func (c unauthedMockFabricClient) GetDefaultStack(context.Context, *defangv1.GetDefaultStackRequest) (*defangv1.GetStackResponse, error) {
return nil, connect.NewError(connect.CodeUnauthenticated, nil)
}

func TestComposeConfig(t *testing.T) {
// Test fix for https://github.com/DefangLabs/defang/issues/1894
global.Client = unauthedMockFabricClient{}
t.Cleanup(func() {
global.Client = nil
})

t.Run("Unauth OK", func(t *testing.T) {
t.Chdir("testdata/without-stack")
cmd := makeComposeConfigCmd()
err := cmd.Execute()
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
})

t.Run("Unauth OK - with stack", func(t *testing.T) {
t.Chdir("testdata/with-project-and-stack")
cmd := makeComposeConfigCmd()
err := cmd.Execute()
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
})
}
2 changes: 1 addition & 1 deletion src/cmd/cli/command/globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Note: Ensure the flag name, environment variable name, and struct field name are
and follow the established naming conventions.
*/
type GlobalConfig struct {
Client *client.GrpcClient
Client client.FabricClient
Cluster string
ColorMode ColorMode
Debug bool
Expand Down
1 change: 0 additions & 1 deletion src/cmd/cli/command/testdata/no-stack/.defang

This file was deleted.

6 changes: 6 additions & 0 deletions src/pkg/cli/client/byoc/gcp/byoc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/base64"
"errors"
"io"
"os"
"testing"
"time"

Expand Down Expand Up @@ -258,6 +259,11 @@ func TestGetGcpProjectID(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for _, envVar := range pkg.GCPProjectEnvVars {
t.Setenv(envVar, "") // this ensures env var is restored after the test
os.Unsetenv(envVar) // after t.Setenv, to really unset it
}

// Set test environment variables
for k, v := range tt.envVars {
t.Setenv(k, v)
Expand Down
7 changes: 5 additions & 2 deletions src/pkg/cli/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,21 @@ type FabricClient interface {
Estimate(context.Context, *defangv1.EstimateRequest) (*defangv1.EstimateResponse, error)
GenerateCompose(context.Context, *defangv1.GenerateComposeRequest) (*defangv1.GenerateComposeResponse, error)
GenerateFiles(context.Context, *defangv1.GenerateFilesRequest) (*defangv1.GenerateFilesResponse, error)
GetFabricClient() defangv1connect.FabricControllerClient
GetDefaultStack(context.Context, *defangv1.GetDefaultStackRequest) (*defangv1.GetStackResponse, error)
GetDelegateSubdomainZone(context.Context, *defangv1.GetDelegateSubdomainZoneRequest) (*defangv1.DelegateSubdomainZoneResponse, error)
GetFabricClient() defangv1connect.FabricControllerClient
GetPlaygroundProjectDomain(context.Context) (*defangv1.GetPlaygroundProjectDomainResponse, error)
GetDefaultStack(context.Context, *defangv1.GetDefaultStackRequest) (*defangv1.GetStackResponse, error)
GetRequestedTenant() types.TenantNameOrID
GetTenantName() types.TenantLabel
GetVersions(context.Context) (*defangv1.Version, error)
ListDeployments(context.Context, *defangv1.ListDeploymentsRequest) (*defangv1.ListDeploymentsResponse, error)
ListStacks(context.Context, *defangv1.ListStacksRequest) (*defangv1.ListStacksResponse, error)
Preview(context.Context, *defangv1.PreviewRequest) (*defangv1.PreviewResponse, error)
Publish(context.Context, *defangv1.PublishRequest) error
PutDeployment(context.Context, *defangv1.PutDeploymentRequest) error
PutStack(context.Context, *defangv1.PutStackRequest) error
RevokeToken(context.Context) error
SetOptions(context.Context, *defangv1.SetOptionsRequest) error
Token(context.Context, *defangv1.TokenRequest) (*defangv1.TokenResponse, error)
Track(string, ...Property) error
VerifyDNSSetup(context.Context, *defangv1.VerifyDNSSetupRequest) error
Expand Down
17 changes: 9 additions & 8 deletions src/pkg/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ func NonInteractiveGitHubLogin(ctx context.Context, fabric client.FabricClient,
return err
}

func InteractiveRequireLoginAndToS(ctx context.Context, fabric *client.GrpcClient, addr string) error {
// InteractiveRequireLoginAndToS ensures the user is logged in and has agreed to the terms of service.
// If necessary, it will reconnect to the server as the right tenant, returning the updated Fabric client.
func InteractiveRequireLoginAndToS(ctx context.Context, fabric client.FabricClient, addr string) (client.FabricClient, error) {
var err error
if err = fabric.CheckLoginAndToS(ctx); err != nil {
// Login interactively now; only do this for authorization-related errors
Expand All @@ -123,19 +125,19 @@ func InteractiveRequireLoginAndToS(ctx context.Context, fabric *client.GrpcClien

defer func() { track.Cmd(nil, "Login", P("reason", err)) }()
if err = InteractiveLogin(ctx, addr); err != nil {
return err
return fabric, err
}

// Reconnect with the new token
if newFabric, err := cli.ConnectWithTenant(ctx, addr, fabric.GetRequestedTenant()); err != nil {
return err
return fabric, err
} else {
*fabric = *newFabric
fabric = newFabric
track.Tracker = fabric // update global tracker
}

if err = fabric.CheckLoginAndToS(ctx); err == nil { // recheck (new token = new user)
return nil // success
return fabric, nil // success
}
}

Expand All @@ -145,10 +147,9 @@ func InteractiveRequireLoginAndToS(ctx context.Context, fabric *client.GrpcClien

defer func() { track.Cmd(nil, "Terms", P("reason", err)) }()
if err = InteractiveAgreeToS(ctx, fabric); err != nil {
return err // fatal
return fabric, err // fatal
}
}
}

return err
return fabric, err
}
5 changes: 3 additions & 2 deletions src/pkg/setup/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var P = track.P
type SetupClient struct {
Surveyor *surveyor.DefaultSurveyor
ModelID string
Fabric *client.GrpcClient
Fabric client.FabricClient
Cluster string
}

Expand Down Expand Up @@ -225,7 +225,8 @@ func beforeGenerate(directory string) {
}

func (s *SetupClient) MigrateFromHeroku(ctx context.Context) (SetupResult, error) {
err := login.InteractiveRequireLoginAndToS(ctx, s.Fabric, s.Cluster)
var err error
s.Fabric, err = login.InteractiveRequireLoginAndToS(ctx, s.Fabric, s.Cluster)
if err != nil {
return SetupResult{}, err
}
Expand Down