Skip to content

Commit 42b15df

Browse files
committed
wip: keep compose as-is
1 parent 43c8ec6 commit 42b15df

File tree

17 files changed

+1220
-988
lines changed

17 files changed

+1220
-988
lines changed

src/cmd/cli/command/commands.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ import (
2727
"github.com/spf13/cobra"
2828
)
2929

30-
const DEFANG_PORTAL_HOST = "portal.defang.dev"
31-
const SERVICE_PORTAL_URL = "https://" + DEFANG_PORTAL_HOST + "/service"
32-
3330
const authNeeded = "auth-needed" // annotation to indicate that a command needs authorization
3431
var authNeededAnnotation = map[string]string{authNeeded: ""}
3532

@@ -886,11 +883,11 @@ var composeRestartCmd = &cobra.Command{
886883
Args: cobra.NoArgs, // TODO: takes optional list of service names
887884
Short: "Reads a Compose file and restarts its services",
888885
RunE: func(cmd *cobra.Command, args []string) error {
889-
etag, err := cli.ComposeRestart(cmd.Context(), client)
886+
err := cli.ComposeRestart(cmd.Context(), client)
890887
if err != nil {
891888
return err
892889
}
893-
term.Info("Restarted services with deployment ID", etag)
890+
term.Info("Restarted services")
894891
return nil
895892
},
896893
}
@@ -1020,11 +1017,11 @@ var restartCmd = &cobra.Command{
10201017
Args: cobra.MinimumNArgs(1),
10211018
Short: "Restart one or more services",
10221019
RunE: func(cmd *cobra.Command, args []string) error {
1023-
etag, err := cli.Restart(cmd.Context(), client, args...)
1020+
err := cli.Restart(cmd.Context(), client, args...)
10241021
if err != nil {
10251022
return err
10261023
}
1027-
term.Info("Restarted service", args, "with deployment ID", etag)
1024+
term.Info("Restarted service", args)
10281025
return nil
10291026
},
10301027
}

src/cmd/cli/command/deploymentinfo.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
package command
22

33
import (
4-
"fmt"
4+
"strings"
55

66
"github.com/DefangLabs/defang/src/pkg/cli"
77
cliClient "github.com/DefangLabs/defang/src/pkg/cli/client"
88
"github.com/DefangLabs/defang/src/pkg/term"
99
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
1010
)
1111

12+
const DEFANG_PORTAL_HOST = "portal.defang.dev"
13+
const SERVICE_PORTAL_URL = "https://" + DEFANG_PORTAL_HOST + "/service"
14+
1215
func printPlaygroundPortalServiceURLs(serviceInfos []*defangv1.ServiceInfo) {
1316
// We can only show services deployed to the prod1 defang SaaS environment.
1417
if provider == cliClient.ProviderDefang && cluster == cli.DefaultCluster {
1518
term.Info("Monitor your services' status in the defang portal")
1619
for _, serviceInfo := range serviceInfos {
17-
fmt.Println(" -", SERVICE_PORTAL_URL+"/"+serviceInfo.Service.Name)
20+
term.Println(" -", SERVICE_PORTAL_URL+"/"+serviceInfo.Service.Name)
1821
}
1922
}
2023
}
@@ -26,17 +29,17 @@ func printEndpoints(serviceInfos []*defangv1.ServiceInfo) {
2629
andEndpoints = "and will be available at:"
2730
}
2831
term.Info("Service", serviceInfo.Service.Name, "is in state", serviceInfo.Status, andEndpoints)
29-
for i, endpoint := range serviceInfo.Endpoints {
30-
if serviceInfo.Service.Ports[i].Mode == defangv1.Mode_INGRESS {
31-
endpoint = "https://" + endpoint
32+
for _, endpoint := range serviceInfo.Endpoints {
33+
if url, ok := strings.CutSuffix(endpoint, ":443"); ok {
34+
endpoint = "https://" + url
3235
}
33-
fmt.Println(" -", endpoint)
36+
term.Println(" -", endpoint)
3437
}
35-
if serviceInfo.Service.Domainname != "" {
38+
if serviceInfo.Domainname != "" {
3639
if serviceInfo.ZoneId != "" {
37-
fmt.Println(" -", "https://"+serviceInfo.Service.Domainname)
40+
term.Println(" -", "https://"+serviceInfo.Domainname)
3841
} else {
39-
fmt.Println(" -", "https://"+serviceInfo.Service.Domainname+" (after `defang cert generate` to get a TLS certificate)")
42+
term.Println(" -", "https://"+serviceInfo.Domainname+" (after `defang cert generate` to get a TLS certificate)")
4043
}
4144
}
4245
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package command
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/DefangLabs/defang/src/pkg/cli"
8+
cliClient "github.com/DefangLabs/defang/src/pkg/cli/client"
9+
"github.com/DefangLabs/defang/src/pkg/term"
10+
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
11+
)
12+
13+
func TestPrintPlaygroundPortalServiceURLs(t *testing.T) {
14+
defaultTerm := term.DefaultTerm
15+
t.Cleanup(func() {
16+
term.DefaultTerm = defaultTerm
17+
})
18+
19+
var stdout, stderr bytes.Buffer
20+
term.DefaultTerm = term.NewTerm(&stdout, &stderr)
21+
22+
provider = cliClient.ProviderDefang
23+
cluster = cli.DefaultCluster
24+
printPlaygroundPortalServiceURLs([]*defangv1.ServiceInfo{
25+
{
26+
Service: &defangv1.ServiceID{Name: "service1"},
27+
}})
28+
const want = ` * Monitor your services' status in the defang portal
29+
- https://portal.defang.dev/service/service1
30+
`
31+
if got := stdout.String(); got != want {
32+
t.Errorf("got %q, want %q", got, want)
33+
}
34+
}
35+
36+
func TestPrintEndpoints(t *testing.T) {
37+
defaultTerm := term.DefaultTerm
38+
t.Cleanup(func() {
39+
term.DefaultTerm = defaultTerm
40+
})
41+
42+
var stdout, stderr bytes.Buffer
43+
term.DefaultTerm = term.NewTerm(&stdout, &stderr)
44+
45+
printEndpoints([]*defangv1.ServiceInfo{
46+
{
47+
Service: &defangv1.ServiceID{Name: "service1"},
48+
Status: "UNKNOWN",
49+
Endpoints: []string{
50+
"example.com:443",
51+
"service1.internal",
52+
},
53+
}})
54+
const want = ` * Service service1 is in state UNKNOWN and will be available at:
55+
- https://example.com
56+
- service1.internal
57+
`
58+
if got := stdout.String(); got != want {
59+
t.Errorf("got %q, want %q", got, want)
60+
}
61+
}

src/pkg/cli/cert.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import (
1414
"github.com/DefangLabs/defang/src/pkg"
1515
cliClient "github.com/DefangLabs/defang/src/pkg/cli/client"
1616
"github.com/DefangLabs/defang/src/pkg/dns"
17+
"github.com/DefangLabs/defang/src/pkg/quota"
1718
"github.com/DefangLabs/defang/src/pkg/spinner"
1819
"github.com/DefangLabs/defang/src/pkg/term"
19-
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
2020
)
2121

2222
type HTTPClient interface {
@@ -27,29 +27,29 @@ var resolver dns.Resolver = dns.RootResolver{}
2727
var httpClient HTTPClient = http.DefaultClient
2828

2929
func GenerateLetsEncryptCert(ctx context.Context, client cliClient.Client) error {
30-
projectName, err := client.LoadProjectName(ctx)
30+
project, err := client.LoadProject(ctx)
3131
if err != nil {
3232
return err
3333
}
34-
term.Debugf("Generating TLS cert for project %q", projectName)
34+
term.Debugf("Generating TLS cert for project %q", project.Name)
3535

3636
services, err := client.GetServices(ctx)
3737
if err != nil {
3838
return err
3939
}
4040

4141
cnt := 0
42-
for _, service := range services.Services {
43-
if service.Service != nil && service.Service.Domainname != "" && service.ZoneId == "" {
42+
for _, serviceInfo := range services.Services {
43+
if service, ok := project.Services[serviceInfo.Service.Name]; ok && service.DomainName != "" && serviceInfo.ZoneId == "" {
4444
cnt++
45-
targets := []string{service.PublicFqdn}
46-
for i, endpoint := range service.Endpoints {
47-
if service.Service.Ports[i].Mode == defangv1.Mode_INGRESS {
45+
targets := []string{serviceInfo.PublicFqdn}
46+
for i, endpoint := range serviceInfo.Endpoints {
47+
if service.Ports[i].Mode == quota.Mode_INGRESS {
4848
targets = append(targets, endpoint)
4949
}
5050
}
51-
term.Debugf("Found service %v with domain %v and targets %v", service.Service.Name, service.Service.Domainname, targets)
52-
generateCert(ctx, service.Service.Domainname, targets)
51+
term.Debugf("Found service %v with domain %v and targets %v", service.Name, service.DomainName, targets)
52+
generateCert(ctx, service.DomainName, targets)
5353
}
5454
}
5555
if cnt == 0 {

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

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs"
2121
"github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs/cfn"
2222
"github.com/DefangLabs/defang/src/pkg/http"
23+
"github.com/DefangLabs/defang/src/pkg/quota"
2324
"github.com/DefangLabs/defang/src/pkg/term"
2425
"github.com/DefangLabs/defang/src/pkg/types"
2526
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
@@ -31,6 +32,8 @@ import (
3132
"github.com/aws/aws-sdk-go-v2/service/sts"
3233
"github.com/aws/smithy-go/ptr"
3334
"github.com/bufbuild/connect-go"
35+
"github.com/compose-spec/compose-go/v2/loader"
36+
compose "github.com/compose-spec/compose-go/v2/types"
3437
"google.golang.org/protobuf/proto"
3538
)
3639

@@ -116,12 +119,18 @@ func (b *ByocAws) Deploy(ctx context.Context, req *defangv1.DeployRequest) (*def
116119
return nil, err
117120
}
118121

122+
p, err := loader.LoadWithContext(ctx, compose.ConfigDetails{ConfigFiles: []compose.ConfigFile{{Content: []byte(req.Compose)}}})
123+
if err != nil {
124+
return nil, err
125+
}
126+
119127
etag := pkg.RandomID()
120-
if len(req.Services) > b.Quota.Services {
128+
if len(p.Services) > b.Quota.Services {
121129
return nil, errors.New("maximum number of services reached")
122130
}
131+
123132
serviceInfos := []*defangv1.ServiceInfo{}
124-
for _, service := range req.Services {
133+
for _, service := range p.Services {
125134
serviceInfo, err := b.update(ctx, service)
126135
if err != nil {
127136
return nil, err
@@ -185,7 +194,7 @@ func (b *ByocAws) Deploy(ctx context.Context, req *defangv1.DeployRequest) (*def
185194

186195
for _, si := range serviceInfos {
187196
if si.UseAcmeCert {
188-
term.Infof("To activate TLS certificate for %v, run 'defang cert gen'", si.Service.Domainname)
197+
term.Infof("To activate TLS certificate for %v, run 'defang cert gen'", si.Service.Name)
189198
}
190199
}
191200

@@ -504,35 +513,38 @@ func (b *ByocAws) Tail(ctx context.Context, req *defangv1.TailRequest) (client.S
504513
}
505514

506515
// This function was copied from Fabric controller and slightly modified to work with BYOC
507-
func (b *ByocAws) update(ctx context.Context, service *defangv1.Service) (*defangv1.ServiceInfo, error) {
508-
if err := b.Quota.Validate(service); err != nil {
516+
func (b *ByocAws) update(ctx context.Context, service compose.ServiceConfig) (*defangv1.ServiceInfo, error) {
517+
if err := b.Quota.Validate(&service); err != nil {
509518
return nil, err
510519
}
511520

512-
// Check to make sure all required secrets are present in the secrets store
513-
missing, err := b.checkForMissingSecrets(ctx, service.Secrets)
521+
// Check to make sure all required configs are present in the configs store
522+
var configs []string
523+
for config, value := range service.Environment {
524+
if value == nil {
525+
configs = append(configs, config)
526+
}
527+
}
528+
err := b.checkForMissingSecrets(ctx, configs)
514529
if err != nil {
515530
return nil, err
516531
}
517-
if missing != nil {
518-
return nil, fmt.Errorf("missing config %q", missing) // retryable CodeFailedPrecondition
519-
}
520532

521533
ensure(b.PulumiProject != "", "pulumiProject not set")
522534
si := &defangv1.ServiceInfo{
523-
Service: service,
535+
Service: &defangv1.ServiceID{Name: service.Name},
524536
Project: b.PulumiProject, // was: tenant
525537
Etag: pkg.RandomID(), // TODO: could be hash for dedup/idempotency
526538
}
527539

528540
hasHost := false
529541
hasIngress := false
530542
fqn := service.Name
531-
if service.StaticFiles == nil {
543+
if sf := service.Extensions["x-defang-static-files"]; sf == nil {
532544
for _, port := range service.Ports {
533-
hasIngress = hasIngress || port.Mode == defangv1.Mode_INGRESS
534-
hasHost = hasHost || port.Mode == defangv1.Mode_HOST
535-
si.Endpoints = append(si.Endpoints, b.getEndpoint(fqn, port))
545+
hasIngress = hasIngress || port.Mode == quota.Mode_INGRESS
546+
hasHost = hasHost || port.Mode == quota.Mode_HOST
547+
si.Endpoints = append(si.Endpoints, b.getEndpoint(fqn, &port))
536548
}
537549
} else {
538550
si.PublicFqdn = b.getPublicFqdn(fqn)
@@ -546,14 +558,15 @@ func (b *ByocAws) update(ctx context.Context, service *defangv1.Service) (*defan
546558
si.PrivateFqdn = b.getPrivateFqdn(fqn)
547559
}
548560

549-
if service.Domainname != "" {
550-
if !hasIngress && service.StaticFiles == nil {
561+
if service.DomainName != "" {
562+
if !hasIngress && service.Extensions["x-defang-static-files"] == nil {
551563
return nil, errors.New("domainname requires at least one ingress port") // retryable CodeFailedPrecondition
552564
}
553-
// Do a DNS lookup for Domainname and confirm it's indeed a CNAME to the service's public FQDN
554-
cname, _ := net.LookupCNAME(service.Domainname)
565+
// Do a DNS lookup for DomainName and confirm it's indeed a CNAME to the service's public FQDN
566+
cname, _ := net.LookupCNAME(service.DomainName)
555567
if strings.TrimSuffix(cname, ".") != si.PublicFqdn {
556-
zoneId, err := b.findZone(ctx, service.Domainname, service.DnsRole)
568+
dnsRole, _ := service.Extensions["x-defang-dns-role"].(string)
569+
zoneId, err := b.findZone(ctx, service.DomainName, dnsRole)
557570
if err != nil {
558571
return nil, err
559572
}
@@ -569,29 +582,29 @@ func (b *ByocAws) update(ctx context.Context, service *defangv1.Service) (*defan
569582

570583
si.NatIps = b.publicNatIps // TODO: even internal services use NAT now
571584
si.Status = "UPDATE_QUEUED"
572-
if si.Service.Build != nil {
585+
if service.Build != nil {
573586
si.Status = "BUILD_QUEUED" // in SaaS, this gets overwritten by the ECS events for "kaniko"
574587
}
575588
return si, nil
576589
}
577590

578591
// This function was copied from Fabric controller and slightly modified to work with BYOC
579-
func (b *ByocAws) checkForMissingSecrets(ctx context.Context, secrets []*defangv1.Secret) (*defangv1.Secret, error) {
592+
func (b *ByocAws) checkForMissingSecrets(ctx context.Context, secrets []string) error {
580593
if len(secrets) == 0 {
581-
return nil, nil // no secrets to check
594+
return nil // no secrets to check
582595
}
583596
prefix := b.getSecretID("")
584597
sorted, err := b.driver.ListSecretsByPrefix(ctx, prefix)
585598
if err != nil {
586-
return nil, err
599+
return err
587600
}
588601
for _, secret := range secrets {
589-
fqn := b.getSecretID(secret.Source)
602+
fqn := b.getSecretID(secret)
590603
if !searchSecret(sorted, fqn) {
591-
return secret, nil // secret not found
604+
return fmt.Errorf("missing config %q", secret)
592605
}
593606
}
594-
return nil, nil // all secrets found
607+
return nil // all secrets found
595608
}
596609

597610
// This function was copied from Fabric controller
@@ -605,8 +618,8 @@ func searchSecret(sorted []qualifiedName, fqn qualifiedName) bool {
605618
type qualifiedName = string // legacy
606619

607620
// This function was copied from Fabric controller and slightly modified to work with BYOC
608-
func (b *ByocAws) getEndpoint(fqn qualifiedName, port *defangv1.Port) string {
609-
if port.Mode == defangv1.Mode_HOST {
621+
func (b *ByocAws) getEndpoint(fqn qualifiedName, port *compose.ServicePortConfig) string {
622+
if port.Mode == quota.Mode_HOST {
610623
privateFqdn := b.getPrivateFqdn(fqn)
611624
return fmt.Sprintf("%s:%d", privateFqdn, port.Target)
612625
}
@@ -675,8 +688,8 @@ func (b *ByocAws) DeleteConfig(ctx context.Context, secrets *defangv1.Secrets) e
675688
return nil
676689
}
677690

678-
func (b *ByocAws) Restart(ctx context.Context, names ...string) (types.ETag, error) {
679-
return "", client.ErrNotImplemented("not yet implemented for BYOC; please use the AWS ECS dashboard") // FIXME: implement this for BYOC
691+
func (b *ByocAws) Restart(ctx context.Context, names ...string) error {
692+
return client.ErrNotImplemented("not yet implemented for BYOC; please use the AWS ECS dashboard") // FIXME: implement this for BYOC
680693
}
681694

682695
func (b *ByocAws) BootstrapList(ctx context.Context) ([]string, error) {

0 commit comments

Comments
 (0)