Skip to content

Commit

Permalink
Merge pull request #471 from DefangLabs/edw-consistent-proj-name
Browse files Browse the repository at this point in the history
Load project name follow docker compose standard
  • Loading branch information
lionello authored Jun 18, 2024
2 parents f46d500 + 581c9cb commit 5cbc777
Show file tree
Hide file tree
Showing 45 changed files with 297 additions and 186 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:
working-directory: src

- name: Add dummy secret
run: echo blah | go run ./cmd/cli secret set -n dummy --debug
run: echo blah | go run ./cmd/cli secret set -n dummy -f tests/testproj/compose.yaml --debug
working-directory: src

- name: Run sanity tests
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ node_modules/

*.log
*.txt
.gitignore
*-lock.json
2 changes: 1 addition & 1 deletion pkgs/defang/cli.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ buildGoModule {
pname = "defang-cli";
version = "git";
src = ../../src;
vendorHash = "sha256-MHShmL0AChU9uHIpLbhqZLlMkxSHJGgHo8/NCn6ZAUc=";
vendorHash = "sha256-DtBVKLAgnwr5LXA0b1EyaVeNV6yLTMxkmjtjHHlbr9A=";

subPackages = [ "cmd/cli" ];

Expand Down
13 changes: 10 additions & 3 deletions src/cmd/cli/command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ var RootCmd = &cobra.Command{

composeFilePath, _ := cmd.Flags().GetString("file")
loader := cli.ComposeLoader{ComposeFilePath: composeFilePath}
client = cli.NewClient(cluster, provider, loader)
client = cli.NewClient(cmd.Context(), cluster, provider, loader)

if v, err := client.GetVersions(cmd.Context()); err == nil {
version := cmd.Root().Version // HACK to avoid circular dependency with RootCmd
Expand Down Expand Up @@ -346,8 +346,8 @@ var RootCmd = &cobra.Command{
}

// FIXME: the new login might have changed the tenant, so we should reload the project
client = cli.NewClient(cluster, provider, loader) // reconnect with the new token
if err = client.CheckLoginAndToS(cmd.Context()); err == nil { // recheck (new token = new user)
client = cli.NewClient(cmd.Context(), cluster, provider, loader) // reconnect with the new token
if err = client.CheckLoginAndToS(cmd.Context()); err == nil { // recheck (new token = new user)
return nil // success
}
}
Expand Down Expand Up @@ -676,6 +676,13 @@ var configSetCmd = &cobra.Command{
Aliases: []string{"set", "add", "put"},
Short: "Adds or updates a sensitive config value",
RunE: func(cmd *cobra.Command, args []string) error {

// Make sure we have a project to set config for before asking for a value
_, err := client.LoadProjectName(cmd.Context())
if err != nil {
return err
}

parts := strings.SplitN(args[0], "=", 2)
name := parts[0]

Expand Down
2 changes: 1 addition & 1 deletion src/pkg/cli/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

func BootstrapCommand(ctx context.Context, client client.Client, command string) error {
projectName, err := client.LoadProjectName()
projectName, err := client.LoadProjectName(ctx)
if err != nil {
return err
}
Expand Down
3 changes: 1 addition & 2 deletions src/pkg/cli/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ var resolver dns.Resolver = dns.RootResolver{}
var httpClient HTTPClient = http.DefaultClient

func GenerateLetsEncryptCert(ctx context.Context, client cliClient.Client) error {
projectName, err := client.LoadProjectName()
projectName, err := client.LoadProjectName(ctx)
if err != nil {
return err
}

term.Debug("Generating TLS cert for project", projectName)

services, err := client.GetServices(ctx)
Expand Down
10 changes: 5 additions & 5 deletions src/pkg/cli/client/byoc/aws/byoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
)

type ByocAws struct {
byoc.ByocBaseClient
*byoc.ByocBaseClient

cdTasks map[string]ecs.TaskArn
driver *cfn.AwsEcs
Expand All @@ -44,12 +44,12 @@ type ByocAws struct {

var _ client.Client = (*ByocAws)(nil)

func NewByoc(grpcClient client.GrpcClient, tenantId types.TenantID) *ByocAws {
func NewByoc(ctx context.Context, grpcClient client.GrpcClient, tenantId types.TenantID) *ByocAws {
b := &ByocAws{
ByocBaseClient: *byoc.NewByocBaseClient(grpcClient, tenantId),
cdTasks: make(map[string]ecs.TaskArn),
driver: cfn.New(byoc.CdTaskPrefix, aws.Region("")), // default region
cdTasks: make(map[string]ecs.TaskArn),
driver: cfn.New(byoc.CdTaskPrefix, aws.Region("")), // default region
}
b.ByocBaseClient = byoc.NewByocBaseClient(ctx, grpcClient, tenantId, b)
return b
}

Expand Down
21 changes: 7 additions & 14 deletions src/pkg/cli/client/byoc/aws/byoc_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aws

import (
"context"
"testing"

"github.com/DefangLabs/defang/src/pkg/cli/client"
Expand All @@ -22,8 +23,8 @@ func TestDomainMultipleProjectSupport(t *testing.T) {
PublicFqdn string
PrivateFqdn string
}{
{"", "tenant1", "web", port80, "web--80.example.com", "web.example.com", "web.tenant1.internal"},
{"", "tenant1", "web", hostModePort, "web.tenant1.internal:80", "web.example.com", "web.tenant1.internal"},
{"tenant1", "tenant1", "web", port80, "web--80.example.com", "web.example.com", "web.tenant1.internal"},
{"tenant1", "tenant1", "web", hostModePort, "web.tenant1.internal:80", "web.example.com", "web.tenant1.internal"},
{"project1", "tenant1", "web", port80, "web--80.project1.example.com", "web.project1.example.com", "web.project1.internal"},
{"Project1", "tenant1", "web", port80, "web--80.project1.example.com", "web.project1.example.com", "web.project1.internal"},
{"project1", "tenant1", "web", hostModePort, "web.project1.internal:80", "web.project1.example.com", "web.project1.internal"},
Expand All @@ -38,8 +39,8 @@ func TestDomainMultipleProjectSupport(t *testing.T) {
for _, tt := range tests {
t.Run(tt.ProjectName+","+string(tt.TenantID), func(t *testing.T) {
grpcClient := &client.GrpcClient{Loader: FakeLoader{ProjectName: tt.ProjectName}}
b := NewByoc(*grpcClient, tt.TenantID)
if _, err := b.LoadProject(); err != nil {
b := NewByoc(context.Background(), *grpcClient, tt.TenantID)
if _, err := b.LoadProject(context.Background()); err != nil {
t.Fatalf("LoadProject() failed: %v", err)
}
b.ProjectDomain = b.getProjectDomain("example.com")
Expand All @@ -66,14 +67,6 @@ type FakeLoader struct {
ProjectName string
}

func (f FakeLoader) LoadWithDefaultProjectName(defaultName string) (*compose.Project, error) {
name := defaultName
if f.ProjectName != "" {
name = f.ProjectName
}
return &compose.Project{Name: name}, nil
}

func (f FakeLoader) LoadWithProjectName(projectName string) (*compose.Project, error) {
return &compose.Project{Name: projectName}, nil
func (f FakeLoader) LoadCompose(ctx context.Context) (*compose.Project, error) {
return &compose.Project{Name: f.ProjectName}, nil
}
89 changes: 64 additions & 25 deletions src/pkg/cli/client/byoc/baseclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ package byoc

import (
"context"
"errors"
"fmt"
"os"
"slices"
"strings"
"sync"

"github.com/DefangLabs/defang/src/pkg"
"github.com/DefangLabs/defang/src/pkg/cli/client"
"github.com/DefangLabs/defang/src/pkg/quota"
"github.com/DefangLabs/defang/src/pkg/term"
"github.com/DefangLabs/defang/src/pkg/types"
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
"github.com/compose-spec/compose-go/v2/consts"
compose "github.com/compose-spec/compose-go/v2/types"
)

Expand All @@ -32,6 +38,10 @@ func DnsSafe(fqdn string) string {
return strings.ToLower(fqdn)
}

type BootstrapLister interface {
BootstrapList(context.Context) ([]string, error)
}

type ByocBaseClient struct {
client.GrpcClient

Expand All @@ -45,13 +55,16 @@ type ByocBaseClient struct {
SetupDone bool
ShouldDelegateSubdomain bool
TenantID string

loadProjOnce func() (*compose.Project, error)
bootstrapLister BootstrapLister
}

func NewByocBaseClient(grpcClient client.GrpcClient, tenantID types.TenantID) *ByocBaseClient {
return &ByocBaseClient{
func NewByocBaseClient(ctx context.Context, grpcClient client.GrpcClient, tenantID types.TenantID, bl BootstrapLister) *ByocBaseClient {
b := &ByocBaseClient{
GrpcClient: grpcClient,
TenantID: string(tenantID),
PulumiProject: os.Getenv("COMPOSE_PROJECT_NAME"),
PulumiProject: "", // To be overwritten by LoadProject
PulumiStack: "beta", // TODO: make customizable
Quota: quota.Quotas{
// These serve mostly to pevent fat-finger errors in the CLI or Compose files
Expand All @@ -62,43 +75,69 @@ func NewByocBaseClient(grpcClient client.GrpcClient, tenantID types.TenantID) *B
Services: 40,
ShmSizeMiB: 30720,
},
bootstrapLister: bl,
}
b.loadProjOnce = sync.OnceValues(func() (*compose.Project, error) {
proj, err := b.GrpcClient.Loader.LoadCompose(ctx)
if err != nil {
return nil, err
}
b.PrivateDomain = DnsSafeLabel(proj.Name) + ".internal"
b.PulumiProject = proj.Name
return proj, nil
})
return b
}

func (b *ByocBaseClient) GetVersions(context.Context) (*defangv1.Version, error) {
cdVersion := CdImage[strings.LastIndex(CdImage, ":")+1:]
return &defangv1.Version{Fabric: cdVersion}, nil
}

func (b *ByocBaseClient) LoadProject() (*compose.Project, error) {
if b.PrivateDomain != "" {
panic("LoadProject should only be called once")
}
var proj *compose.Project
var err error
func (b *ByocBaseClient) LoadProject(ctx context.Context) (*compose.Project, error) {
return b.loadProjOnce()
}

func (b *ByocBaseClient) LoadProjectName(ctx context.Context) (string, error) {

if b.PulumiProject != "" {
proj, err = b.GrpcClient.Loader.LoadWithProjectName(b.PulumiProject)
} else {
proj, err = b.GrpcClient.Loader.LoadWithDefaultProjectName(b.TenantID)
proj, err := b.loadProjOnce()
if err == nil {
b.PulumiProject = proj.Name
return proj.Name, nil
}
if !errors.Is(err, types.ErrComposeFileNotFound) {
return "", err
}

// Get the list of projects from remote
projectNames, err := b.bootstrapLister.BootstrapList(ctx)
if err != nil {
return nil, err
return "", err
}
for i, name := range projectNames {
projectNames[i] = strings.Split(name, "/")[0] // Remove the stack name
}
b.PrivateDomain = DnsSafeLabel(proj.Name) + ".internal"
b.PulumiProject = proj.Name
return proj, nil
}

func (b *ByocBaseClient) LoadProjectName() (string, error) {
if b.PulumiProject != "" {
return b.PulumiProject, nil
if len(projectNames) == 0 {
return "", errors.New("no projects found")
}
p, err := b.LoadProject()
if err != nil {
return b.TenantID, err
if len(projectNames) == 1 {
term.Debug("Using default project: ", projectNames[0])
b.PulumiProject = projectNames[0]
return projectNames[0], nil
}
return p.Name, nil

// When there are multiple projects, take a hint from COMPOSE_PROJECT_NAME environment variable if set
if projectName, ok := os.LookupEnv(consts.ComposeProjectName); ok {
if !slices.Contains(projectNames, projectName) {
return "", fmt.Errorf("project %q specified by COMPOSE_PROJECT_NAME not found", projectName)
}
term.Debug("Using project from COMPOSE_PROJECT_NAME environment variable:", projectNames[0])
b.PulumiProject = projectName
return projectName, nil
}

return "", errors.New("multiple projects found; please go to the correct project directory where the compose file is or set COMPOSE_PROJECT_NAME")
}

func (b *ByocBaseClient) ServiceDNS(name string) string {
Expand Down
10 changes: 5 additions & 5 deletions src/pkg/cli/client/byoc/do/byoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,23 @@ const (
)

type ByocDo struct {
byoc.ByocBaseClient
*byoc.ByocBaseClient

appIds map[string]string
driver *appPlatform.DoApp
}

func NewByoc(grpcClient client.GrpcClient, tenantId types.TenantID) *ByocDo {
func NewByoc(ctx context.Context, grpcClient client.GrpcClient, tenantId types.TenantID) *ByocDo {
regionString := os.Getenv("REGION")

if regionString == "" {
regionString = "sfo3"
}

b := &ByocDo{
ByocBaseClient: *byoc.NewByocBaseClient(grpcClient, tenantId),
driver: appPlatform.New(byoc.CdTaskPrefix, do.Region(regionString)),
driver: appPlatform.New(byoc.CdTaskPrefix, do.Region(regionString)),
}
b.ByocBaseClient = byoc.NewByocBaseClient(ctx, grpcClient, tenantId, b)

return b
}
Expand Down Expand Up @@ -132,7 +132,7 @@ func (b *ByocDo) BootstrapCommand(ctx context.Context, command string) (string,
}

func (b *ByocDo) BootstrapList(ctx context.Context) ([]string, error) {
return nil, nil
return nil, client.ErrNotImplemented("not implemented for ByocDo")
}

func (b *ByocDo) CreateUploadURL(ctx context.Context, req *defangv1.UploadURLRequest) (*defangv1.UploadURLResponse, error) {
Expand Down
7 changes: 3 additions & 4 deletions src/pkg/cli/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ type ServerStream[Res any] interface {
}

type ProjectLoader interface {
LoadWithDefaultProjectName(string) (*compose.Project, error)
LoadWithProjectName(string) (*compose.Project, error)
LoadCompose(context.Context) (*compose.Project, error)
}

type FabricClient interface {
Expand Down Expand Up @@ -57,8 +56,8 @@ type Client interface {
TearDown(context.Context) error
WhoAmI(context.Context) (*defangv1.WhoAmIResponse, error)

LoadProject() (*compose.Project, error)
LoadProjectName() (string, error) // TODO: should probably be a private method
LoadProject(context.Context) (*compose.Project, error)
LoadProjectName(context.Context) (string, error)
}

type Property struct {
Expand Down
4 changes: 2 additions & 2 deletions src/pkg/cli/client/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ func (m MockClient) ServiceDNS(service string) string {
return service
}

func (m MockClient) LoadProject() (*compose.Project, error) {
func (m MockClient) LoadProject(ctx context.Context) (*compose.Project, error) {
return m.Project, nil
}

func (m MockClient) LoadProjectName() (string, error) {
func (m MockClient) LoadProjectName(ctx context.Context) (string, error) {
return m.Project.Name, nil
}

Expand Down
Loading

0 comments on commit 5cbc777

Please sign in to comment.