Skip to content

Commit

Permalink
wip: keep compose as-is
Browse files Browse the repository at this point in the history
  • Loading branch information
lionello committed Jul 4, 2024
1 parent 43c8ec6 commit 42b15df
Show file tree
Hide file tree
Showing 17 changed files with 1,220 additions and 988 deletions.
11 changes: 4 additions & 7 deletions src/cmd/cli/command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ import (
"github.com/spf13/cobra"
)

const DEFANG_PORTAL_HOST = "portal.defang.dev"
const SERVICE_PORTAL_URL = "https://" + DEFANG_PORTAL_HOST + "/service"

const authNeeded = "auth-needed" // annotation to indicate that a command needs authorization
var authNeededAnnotation = map[string]string{authNeeded: ""}

Expand Down Expand Up @@ -886,11 +883,11 @@ var composeRestartCmd = &cobra.Command{
Args: cobra.NoArgs, // TODO: takes optional list of service names
Short: "Reads a Compose file and restarts its services",
RunE: func(cmd *cobra.Command, args []string) error {
etag, err := cli.ComposeRestart(cmd.Context(), client)
err := cli.ComposeRestart(cmd.Context(), client)
if err != nil {
return err
}
term.Info("Restarted services with deployment ID", etag)
term.Info("Restarted services")
return nil
},
}
Expand Down Expand Up @@ -1020,11 +1017,11 @@ var restartCmd = &cobra.Command{
Args: cobra.MinimumNArgs(1),
Short: "Restart one or more services",
RunE: func(cmd *cobra.Command, args []string) error {
etag, err := cli.Restart(cmd.Context(), client, args...)
err := cli.Restart(cmd.Context(), client, args...)
if err != nil {
return err
}
term.Info("Restarted service", args, "with deployment ID", etag)
term.Info("Restarted service", args)
return nil
},
}
Expand Down
21 changes: 12 additions & 9 deletions src/cmd/cli/command/deploymentinfo.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package command

import (
"fmt"
"strings"

"github.com/DefangLabs/defang/src/pkg/cli"
cliClient "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"
)

const DEFANG_PORTAL_HOST = "portal.defang.dev"
const SERVICE_PORTAL_URL = "https://" + DEFANG_PORTAL_HOST + "/service"

func printPlaygroundPortalServiceURLs(serviceInfos []*defangv1.ServiceInfo) {
// We can only show services deployed to the prod1 defang SaaS environment.
if provider == cliClient.ProviderDefang && cluster == cli.DefaultCluster {
term.Info("Monitor your services' status in the defang portal")
for _, serviceInfo := range serviceInfos {
fmt.Println(" -", SERVICE_PORTAL_URL+"/"+serviceInfo.Service.Name)
term.Println(" -", SERVICE_PORTAL_URL+"/"+serviceInfo.Service.Name)
}
}
}
Expand All @@ -26,17 +29,17 @@ func printEndpoints(serviceInfos []*defangv1.ServiceInfo) {
andEndpoints = "and will be available at:"
}
term.Info("Service", serviceInfo.Service.Name, "is in state", serviceInfo.Status, andEndpoints)
for i, endpoint := range serviceInfo.Endpoints {
if serviceInfo.Service.Ports[i].Mode == defangv1.Mode_INGRESS {
endpoint = "https://" + endpoint
for _, endpoint := range serviceInfo.Endpoints {
if url, ok := strings.CutSuffix(endpoint, ":443"); ok {
endpoint = "https://" + url
}
fmt.Println(" -", endpoint)
term.Println(" -", endpoint)
}
if serviceInfo.Service.Domainname != "" {
if serviceInfo.Domainname != "" {
if serviceInfo.ZoneId != "" {
fmt.Println(" -", "https://"+serviceInfo.Service.Domainname)
term.Println(" -", "https://"+serviceInfo.Domainname)
} else {
fmt.Println(" -", "https://"+serviceInfo.Service.Domainname+" (after `defang cert generate` to get a TLS certificate)")
term.Println(" -", "https://"+serviceInfo.Domainname+" (after `defang cert generate` to get a TLS certificate)")
}
}
}
Expand Down
61 changes: 61 additions & 0 deletions src/cmd/cli/command/deploymentinfo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package command

import (
"bytes"
"testing"

"github.com/DefangLabs/defang/src/pkg/cli"
cliClient "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"
)

func TestPrintPlaygroundPortalServiceURLs(t *testing.T) {
defaultTerm := term.DefaultTerm
t.Cleanup(func() {
term.DefaultTerm = defaultTerm
})

var stdout, stderr bytes.Buffer
term.DefaultTerm = term.NewTerm(&stdout, &stderr)

provider = cliClient.ProviderDefang
cluster = cli.DefaultCluster
printPlaygroundPortalServiceURLs([]*defangv1.ServiceInfo{
{
Service: &defangv1.ServiceID{Name: "service1"},
}})
const want = ` * Monitor your services' status in the defang portal
- https://portal.defang.dev/service/service1
`
if got := stdout.String(); got != want {
t.Errorf("got %q, want %q", got, want)
}
}

func TestPrintEndpoints(t *testing.T) {
defaultTerm := term.DefaultTerm
t.Cleanup(func() {
term.DefaultTerm = defaultTerm
})

var stdout, stderr bytes.Buffer
term.DefaultTerm = term.NewTerm(&stdout, &stderr)

printEndpoints([]*defangv1.ServiceInfo{
{
Service: &defangv1.ServiceID{Name: "service1"},
Status: "UNKNOWN",
Endpoints: []string{
"example.com:443",
"service1.internal",
},
}})
const want = ` * Service service1 is in state UNKNOWN and will be available at:
- https://example.com
- service1.internal
`
if got := stdout.String(); got != want {
t.Errorf("got %q, want %q", got, want)
}
}
20 changes: 10 additions & 10 deletions src/pkg/cli/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (
"github.com/DefangLabs/defang/src/pkg"
cliClient "github.com/DefangLabs/defang/src/pkg/cli/client"
"github.com/DefangLabs/defang/src/pkg/dns"
"github.com/DefangLabs/defang/src/pkg/quota"
"github.com/DefangLabs/defang/src/pkg/spinner"
"github.com/DefangLabs/defang/src/pkg/term"
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
)

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

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

services, err := client.GetServices(ctx)
if err != nil {
return err
}

cnt := 0
for _, service := range services.Services {
if service.Service != nil && service.Service.Domainname != "" && service.ZoneId == "" {
for _, serviceInfo := range services.Services {
if service, ok := project.Services[serviceInfo.Service.Name]; ok && service.DomainName != "" && serviceInfo.ZoneId == "" {
cnt++
targets := []string{service.PublicFqdn}
for i, endpoint := range service.Endpoints {
if service.Service.Ports[i].Mode == defangv1.Mode_INGRESS {
targets := []string{serviceInfo.PublicFqdn}
for i, endpoint := range serviceInfo.Endpoints {
if service.Ports[i].Mode == quota.Mode_INGRESS {
targets = append(targets, endpoint)
}
}
term.Debugf("Found service %v with domain %v and targets %v", service.Service.Name, service.Service.Domainname, targets)
generateCert(ctx, service.Service.Domainname, targets)
term.Debugf("Found service %v with domain %v and targets %v", service.Name, service.DomainName, targets)
generateCert(ctx, service.DomainName, targets)
}
}
if cnt == 0 {
Expand Down
75 changes: 44 additions & 31 deletions src/pkg/cli/client/byoc/aws/byoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs"
"github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs/cfn"
"github.com/DefangLabs/defang/src/pkg/http"
"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"
Expand All @@ -31,6 +32,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/aws/smithy-go/ptr"
"github.com/bufbuild/connect-go"
"github.com/compose-spec/compose-go/v2/loader"
compose "github.com/compose-spec/compose-go/v2/types"
"google.golang.org/protobuf/proto"
)

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

p, err := loader.LoadWithContext(ctx, compose.ConfigDetails{ConfigFiles: []compose.ConfigFile{{Content: []byte(req.Compose)}}})
if err != nil {
return nil, err
}

etag := pkg.RandomID()
if len(req.Services) > b.Quota.Services {
if len(p.Services) > b.Quota.Services {
return nil, errors.New("maximum number of services reached")
}

serviceInfos := []*defangv1.ServiceInfo{}
for _, service := range req.Services {
for _, service := range p.Services {
serviceInfo, err := b.update(ctx, service)
if err != nil {
return nil, err
Expand Down Expand Up @@ -185,7 +194,7 @@ func (b *ByocAws) Deploy(ctx context.Context, req *defangv1.DeployRequest) (*def

for _, si := range serviceInfos {
if si.UseAcmeCert {
term.Infof("To activate TLS certificate for %v, run 'defang cert gen'", si.Service.Domainname)
term.Infof("To activate TLS certificate for %v, run 'defang cert gen'", si.Service.Name)
}
}

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

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

// Check to make sure all required secrets are present in the secrets store
missing, err := b.checkForMissingSecrets(ctx, service.Secrets)
// Check to make sure all required configs are present in the configs store
var configs []string
for config, value := range service.Environment {
if value == nil {
configs = append(configs, config)
}
}
err := b.checkForMissingSecrets(ctx, configs)
if err != nil {
return nil, err
}
if missing != nil {
return nil, fmt.Errorf("missing config %q", missing) // retryable CodeFailedPrecondition
}

ensure(b.PulumiProject != "", "pulumiProject not set")
si := &defangv1.ServiceInfo{
Service: service,
Service: &defangv1.ServiceID{Name: service.Name},
Project: b.PulumiProject, // was: tenant
Etag: pkg.RandomID(), // TODO: could be hash for dedup/idempotency
}

hasHost := false
hasIngress := false
fqn := service.Name
if service.StaticFiles == nil {
if sf := service.Extensions["x-defang-static-files"]; sf == nil {
for _, port := range service.Ports {
hasIngress = hasIngress || port.Mode == defangv1.Mode_INGRESS
hasHost = hasHost || port.Mode == defangv1.Mode_HOST
si.Endpoints = append(si.Endpoints, b.getEndpoint(fqn, port))
hasIngress = hasIngress || port.Mode == quota.Mode_INGRESS
hasHost = hasHost || port.Mode == quota.Mode_HOST
si.Endpoints = append(si.Endpoints, b.getEndpoint(fqn, &port))
}
} else {
si.PublicFqdn = b.getPublicFqdn(fqn)
Expand All @@ -546,14 +558,15 @@ func (b *ByocAws) update(ctx context.Context, service *defangv1.Service) (*defan
si.PrivateFqdn = b.getPrivateFqdn(fqn)
}

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

si.NatIps = b.publicNatIps // TODO: even internal services use NAT now
si.Status = "UPDATE_QUEUED"
if si.Service.Build != nil {
if service.Build != nil {
si.Status = "BUILD_QUEUED" // in SaaS, this gets overwritten by the ECS events for "kaniko"
}
return si, nil
}

// This function was copied from Fabric controller and slightly modified to work with BYOC
func (b *ByocAws) checkForMissingSecrets(ctx context.Context, secrets []*defangv1.Secret) (*defangv1.Secret, error) {
func (b *ByocAws) checkForMissingSecrets(ctx context.Context, secrets []string) error {
if len(secrets) == 0 {
return nil, nil // no secrets to check
return nil // no secrets to check
}
prefix := b.getSecretID("")
sorted, err := b.driver.ListSecretsByPrefix(ctx, prefix)
if err != nil {
return nil, err
return err
}
for _, secret := range secrets {
fqn := b.getSecretID(secret.Source)
fqn := b.getSecretID(secret)
if !searchSecret(sorted, fqn) {
return secret, nil // secret not found
return fmt.Errorf("missing config %q", secret)
}
}
return nil, nil // all secrets found
return nil // all secrets found
}

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

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

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

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

0 comments on commit 42b15df

Please sign in to comment.