Skip to content

Commit

Permalink
Merge pull request #514 from DefangLabs/lio-connect-context
Browse files Browse the repository at this point in the history
move WhoAmI out of Connect
  • Loading branch information
lionello authored Jun 28, 2024
2 parents e7ff07b + 2ec16e4 commit 2dec1a4
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 39 deletions.
44 changes: 31 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,39 @@
Defang is a radically simpler way for developers to create, deploy, and manage cloud applications.

This repo includes:
* Public releases of the Defang CLI; [click here](https://github.com/DefangLabs/defang/releases/latest/) for the latest version
* Samples in Golang, Python, and Node.js that show how to accomplish various tasks and deploy them to the DOP using a Docker Compose file using the Defang CLI.
* Samples that show how to deploy an app using the [Defang Pulumi Provider](https://github.com/DefangLabs/pulumi-defang).

- Public releases of the Defang CLI; [click here](https://github.com/DefangLabs/defang/releases/latest/) for the latest version
- Samples in Golang, Python, and Node.js that show how to accomplish various tasks and deploy them to the DOP using a Docker Compose file using the Defang CLI.
- Samples that show how to deploy an app using the [Defang Pulumi Provider](https://github.com/DefangLabs/pulumi-defang).

## Getting started
* Read our [Getting Started](https://docs.defang.io/docs/getting-started) page
* Follow the installation instructions from the [Installing](https://docs.defang.io/docs/getting-started/installing) page
* Take a look at our [Samples folder](https://github.com/DefangLabs/defang/tree/main/samples) for example projects in various programming languages.
* Try the AI integration by running `defang generate`
* Start your new service with `defang compose up`

- Read our [Getting Started](https://docs.defang.io/docs/getting-started) page
- Follow the installation instructions from the [Installing](https://docs.defang.io/docs/getting-started/installing) page
- Take a look at our [Samples folder](https://github.com/DefangLabs/defang/tree/main/samples) for example projects in various programming languages.
- Try the AI integration by running `defang generate`
- Start your new service with `defang compose up`

## Installing

Install the Defang CLI from one of the following sources:

* Using the [Homebrew](https://brew.sh) package manager [DefangLabs/defang tap](https://github.com/DefangLabs/homebrew-defang):
```
brew install DefangLabs/defang/defang
```
- Using a shell script:

* Using a shell script:
```
. <(curl -Ls https://s.defang.io/install)
```
- Using [Go](https://go.dev):

* Using [Go](https://go.dev):
```
go install github.com/DefangLabs/defang/src/cmd/cli@latest
```
- Using the [Nix package manager](https://nixos.org):

* Using the [Nix package manager](https://nixos.org):
- with Nix-Env:
```
nix-env -if https://github.com/DefangLabs/defang/archive/main.tar.gz
Expand All @@ -40,10 +46,23 @@ Install the Defang CLI from one of the following sources:
```
nix profile install github:DefangLabs/defang#defang-bin --refresh
```
* Using [winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/):
```
winget install defang
```
* Using a PowerShell script:
```
iwr https://s.defang.io/defang_win_amd64.zip -OutFile defang.zip
Expand-Archive defang.zip . -Force
```
* Download the [latest binary](https://github.com/DefangLabs/defang/releases/latest/) of the Defang CLI. For this beta, MacOS users will have to explicitly allow running of downloaded programs in the OS security settings.
## Support
* File any issues [right here on GitHub](https://github.com/DefangLabs/defang/issues)
- File any issues [right here on GitHub](https://github.com/DefangLabs/defang/issues)
## Command completion
Expand Down Expand Up @@ -96,4 +115,3 @@ The Defang CLI recognizes the following environment variables:
- `NO_COLOR` - If set to any value, disables color output; by default, color output is enabled depending on the terminal
- `TZ` - The timezone to use for log timestamps: an IANA TZ name like `UTC` or `Europe/Amsterdam`; defaults to `Local`
- `XDG_STATE_HOME` - The directory to use for storing state; defaults to `~/.local/state`
7 changes: 3 additions & 4 deletions src/cmd/cli/command/track.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@ func Track(name string, props ...P) {
if disableAnalytics {
return
}
var client cliClient.FabricClient = client
if client == nil {
client, _ = cli.Connect(cluster, nil)
}
trackWG.Add(1)
go func(client cliClient.FabricClient) {
if client == nil {
client = cli.Connect(cluster, nil)
}
defer trackWG.Done()
_ = client.Track(name, props...)
}(client)
Expand Down
4 changes: 2 additions & 2 deletions src/pkg/cli/client/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type GrpcClient struct {
anonID string
client defangv1connect.FabricControllerClient

tenantID types.TenantID
TenantID types.TenantID
Loader ProjectLoader
}

Expand All @@ -33,7 +33,7 @@ func NewGrpcClient(host, accessToken string, tenantID types.TenantID, loader Pro
// Debug(" - Connecting to", baseUrl)
fabricClient := defangv1connect.NewFabricControllerClient(http.DefaultClient, baseUrl, connect.WithGRPC(), connect.WithInterceptors(auth.NewAuthInterceptor(accessToken), Retrier{}))

return GrpcClient{client: fabricClient, anonID: GetAnonID(), tenantID: tenantID, Loader: loader}
return GrpcClient{client: fabricClient, anonID: GetAnonID(), TenantID: tenantID, Loader: loader}
}

func getMsg[T any](resp *connect.Response[T], err error) (*T, error) {
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/cli/client/playground.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (g *PlaygroundClient) Restart(ctx context.Context, names ...string) (types.
}

func (g PlaygroundClient) ServiceDNS(name string) string {
return string(g.tenantID) + "-" + name
return string(g.TenantID) + "-" + name
}

func (g PlaygroundClient) LoadProjectName(ctx context.Context) (string, error) {
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/cli/client/playground_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

func TestServiceDNS(t *testing.T) {
p := PlaygroundClient{GrpcClient: GrpcClient{tenantID: "proj1"}}
p := PlaygroundClient{GrpcClient: GrpcClient{TenantID: "proj1"}}

const expected = "proj1-service1"
if got := p.ServiceDNS("service1"); got != expected {
Expand Down
22 changes: 12 additions & 10 deletions src/pkg/cli/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func getExistingTokenAndTenant(cluster string) (string, types.TenantID) {
return accessToken, tenantId
}

func Connect(cluster string, loader client.ProjectLoader) (client.GrpcClient, types.TenantID) {
func Connect(cluster string, loader client.ProjectLoader) client.GrpcClient {
accessToken, tenantId := getExistingTokenAndTenant(cluster)

tenant, host := SplitTenantHost(cluster)
Expand All @@ -63,20 +63,22 @@ func Connect(cluster string, loader client.ProjectLoader) (client.GrpcClient, ty
}
term.Debug("Using tenant", tenantId, "for cluster", host)

defangClient := client.NewGrpcClient(host, accessToken, tenantId, loader)
resp, err := defangClient.WhoAmI(context.TODO()) // TODO: Should we pass in the command context?
return client.NewGrpcClient(host, accessToken, tenantId, loader)
}

func NewClient(ctx context.Context, cluster string, provider client.Provider, loader client.ProjectLoader) client.Client {
grpcClient := Connect(cluster, loader)

// Determine the current tenant ID
resp, err := grpcClient.WhoAmI(ctx)
if err != nil {
term.Debug("Unable to validate tenant ID with server:", err)
}
if resp != nil && tenantId != types.TenantID(resp.Tenant) {
term.Warnf("Overriding locally cached TenantID %v with server provided value %v", tenantId, resp.Tenant)
tenantId := grpcClient.TenantID
if resp != nil && string(tenantId) != resp.Tenant {
term.Warnf("Overriding locally cached TenantID %q with server provided value %q", tenantId, resp.Tenant)
tenantId = types.TenantID(resp.Tenant)
}
return defangClient, tenantId
}

func NewClient(ctx context.Context, cluster string, provider client.Provider, loader client.ProjectLoader) client.Client {
grpcClient, tenantId := Connect(cluster, loader)

switch provider {
case client.ProviderAWS:
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/cli/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func InitFromSamples(ctx context.Context, dir string, names []string) error {
term.Debug(resp.Header)
tarball, err := gzip.NewReader(resp.Body)
if err != nil {
return err
return fmt.Errorf("failed to read tarball: %w", err)
}
defer tarball.Close()
tarReader := tar.NewReader(tarball)
Expand Down
56 changes: 49 additions & 7 deletions src/pkg/cli/new_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,59 @@
package cli

import (
"archive/tar"
"compress/gzip"
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"

ourHttp "github.com/DefangLabs/defang/src/pkg/http"
)

func TestInitFromSamples(t *testing.T) {
err := InitFromSamples(context.Background(), t.TempDir(), []string{"nonexisting"})
if err == nil {
t.Fatal("Expected test to fail")
}
if !errors.Is(err, ErrSampleNotFound) {
t.Errorf("Expected error to be %v, got %v", ErrSampleNotFound, err)
type mockRoundTripper struct{}

func (d mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
res := httptest.NewRecorder()
if req.URL.String() != "https://github.com/DefangLabs/samples/archive/refs/heads/main.tar.gz" {
res.Code = 404
} else {
gz := gzip.NewWriter(res.Body)
tar.NewWriter(gz).Close()
gz.Close()
}
return res.Result(), nil
}

func TestInitFromSamples(t *testing.T) {
t.Run("mock", func(t *testing.T) {
oldClient := ourHttp.DefaultClient
t.Cleanup(func() { ourHttp.DefaultClient = oldClient })
ourHttp.DefaultClient = &http.Client{Transport: mockRoundTripper{}}

err := InitFromSamples(context.Background(), t.TempDir(), []string{"nonexisting"})
if err == nil {
t.Fatal("Expected test to fail")
}
if !errors.Is(err, ErrSampleNotFound) {
t.Errorf("Expected error to be %v, got %v", ErrSampleNotFound, err)
}

})

t.Run("wan", func(t *testing.T) {
if testing.Short() {
t.Skip("skipped; add -short to enable")
}

err := InitFromSamples(context.Background(), t.TempDir(), []string{"nonexisting"})
if err == nil {
t.Fatal("Expected test to fail")
}
if !errors.Is(err, ErrSampleNotFound) {
t.Errorf("Expected error to be %v, got %v", ErrSampleNotFound, err)
}

})
}

0 comments on commit 2dec1a4

Please sign in to comment.