From 1dd8f28b5693f692e861a6de9f775eb2bbe64300 Mon Sep 17 00:00:00 2001 From: Milan Pavlik Date: Tue, 3 May 2022 12:15:12 +0000 Subject: [PATCH] [public-api] Convert to a cobra command and add config --- components/public-api-server/BUILD.yaml | 1 + components/public-api-server/go.mod | 8 +- components/public-api-server/go.sum | 14 + .../public-api-server/pkg/apiv1/workspace.go | 19 +- .../pkg/apiv1/workspace_test.go | 315 +++++++++++++++++- .../public-api-server/pkg/proxy/conn.go | 39 +++ .../pkg/server/integration_test.go | 15 +- .../public-api-server/pkg/server/server.go | 9 +- 8 files changed, 392 insertions(+), 28 deletions(-) create mode 100644 components/public-api-server/pkg/proxy/conn.go diff --git a/components/public-api-server/BUILD.yaml b/components/public-api-server/BUILD.yaml index bcc0b96030e133..5386f7a2697784 100644 --- a/components/public-api-server/BUILD.yaml +++ b/components/public-api-server/BUILD.yaml @@ -8,6 +8,7 @@ packages: deps: - components/common-go:lib - components/public-api/go:lib + - components/gitpod-protocol/go:lib env: - CGO_ENABLED=0 - GOOS=linux diff --git a/components/public-api-server/go.mod b/components/public-api-server/go.mod index 5f5965d82e22f7..70d4a8e639c6cd 100644 --- a/components/public-api-server/go.mod +++ b/components/public-api-server/go.mod @@ -4,7 +4,9 @@ go 1.17 require ( github.com/gitpod-io/gitpod/common-go v0.0.0-00010101000000-000000000000 + github.com/gitpod-io/gitpod/gitpod-protocol v0.0.0-00010101000000-000000000000 github.com/gitpod-io/gitpod/public-api v0.0.0-00010101000000-000000000000 + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 @@ -17,8 +19,9 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/heptiolabs/healthcheck v0.0.0-20211123025425-613501dd5deb // indirect @@ -30,6 +33,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 // indirect golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect golang.org/x/text v0.3.7 // indirect @@ -41,6 +45,8 @@ require ( replace github.com/gitpod-io/gitpod/common-go => ../common-go // leeway +replace github.com/gitpod-io/gitpod/gitpod-protocol => ../gitpod-protocol/go // leeway + replace github.com/gitpod-io/gitpod/public-api => ../public-api/go // leeway replace k8s.io/api => k8s.io/api v0.23.5 // leeway indirect from components/common-go:lib diff --git a/components/public-api-server/go.sum b/components/public-api-server/go.sum index 28db80ac2687fb..1310575d846ca6 100644 --- a/components/public-api-server/go.sum +++ b/components/public-api-server/go.sum @@ -92,6 +92,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -135,6 +137,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= @@ -214,6 +219,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 h1:marA1XQDC7N870zmSFIoHZpIUduK80USeY0Rkuflgp4= +github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra5qMlU4gTRwIo= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -230,6 +237,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -275,6 +283,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -304,6 +313,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -323,6 +333,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -358,7 +369,9 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -417,6 +430,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/components/public-api-server/pkg/apiv1/workspace.go b/components/public-api-server/pkg/apiv1/workspace.go index db1f11a2b35eef..f65280e270b7cf 100644 --- a/components/public-api-server/pkg/apiv1/workspace.go +++ b/components/public-api-server/pkg/apiv1/workspace.go @@ -6,28 +6,43 @@ package apiv1 import ( "context" + "github.com/gitpod-io/gitpod/public-api-server/pkg/proxy" v1 "github.com/gitpod-io/gitpod/public-api/v1" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) -func NewWorkspaceService() *WorkspaceService { +func NewWorkspaceService(serverConnPool proxy.ServerConnectionPool) *WorkspaceService { return &WorkspaceService{ + connectionPool: serverConnPool, UnimplementedWorkspacesServiceServer: &v1.UnimplementedWorkspacesServiceServer{}, } } type WorkspaceService struct { + connectionPool proxy.ServerConnectionPool + *v1.UnimplementedWorkspacesServiceServer } func (w *WorkspaceService) GetWorkspace(ctx context.Context, r *v1.GetWorkspaceRequest) (*v1.GetWorkspaceResponse, error) { - _, err := bearerTokenFromContext(ctx) + token, err := bearerTokenFromContext(ctx) if err != nil { return nil, err } + server, err := w.connectionPool.Get(ctx, token) + if err != nil { + return nil, status.Error(codes.Internal, "failed to establish connection to downstream services") + } + + // TODO(milan): Use resulting workspace and transform it to public API response + _, err = server.GetWorkspace(ctx, r.GetWorkspaceId()) + if err != nil { + return nil, status.Error(codes.NotFound, "failed to get workspace") + } + return &v1.GetWorkspaceResponse{ Result: &v1.Workspace{ WorkspaceId: r.GetWorkspaceId(), diff --git a/components/public-api-server/pkg/apiv1/workspace_test.go b/components/public-api-server/pkg/apiv1/workspace_test.go index 19dfa0d49225f1..a78e4a6628011c 100644 --- a/components/public-api-server/pkg/apiv1/workspace_test.go +++ b/components/public-api-server/pkg/apiv1/workspace_test.go @@ -6,43 +6,324 @@ package apiv1 import ( "context" + "fmt" "github.com/gitpod-io/gitpod/common-go/baseserver" + gitpod "github.com/gitpod-io/gitpod/gitpod-protocol" v1 "github.com/gitpod-io/gitpod/public-api/v1" "github.com/stretchr/testify/require" "google.golang.org/grpc" + "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "testing" ) func TestWorkspaceService_GetWorkspace(t *testing.T) { + const ( + bearerToken = "bearer-token-for-tests" + foundWorkspaceID = "easycz-seer-xl8o1zacpyw" + ) + srv := baseserver.NewForTests(t) - v1.RegisterWorkspacesServiceServer(srv.GRPC(), NewWorkspaceService()) + + connPool := &FakeServerConnPool{ + api: &FakeGitpodAPI{workspaces: map[string]*gitpod.WorkspaceInfo{ + foundWorkspaceID: { + LatestInstance: &gitpod.WorkspaceInstance{}, + Workspace: &gitpod.Workspace{}, + }, + }}, + } + v1.RegisterWorkspacesServiceServer(srv.GRPC(), NewWorkspaceService(connPool)) baseserver.StartServerForTests(t, srv) conn, err := grpc.Dial(srv.GRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) client := v1.NewWorkspacesServiceClient(conn) + ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", bearerToken) - ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "some-token") + scenarios := []struct { + name string - workspaceID := "some-workspace-id" - resp, err := client.GetWorkspace(ctx, &v1.GetWorkspaceRequest{ - WorkspaceId: workspaceID, - }) - require.NoError(t, err) - require.True(t, proto.Equal(&v1.GetWorkspaceResponse{ - Result: &v1.Workspace{ - WorkspaceId: workspaceID, - OwnerId: "mock_owner", - ProjectId: "mock_project_id", - Context: &v1.WorkspaceContext{ - ContextUrl: "https://github.com/gitpod-io/gitpod", - Details: nil, + WorkspaceID string + + ErrorCode codes.Code + Response *v1.GetWorkspaceResponse + }{ + { + name: "returns a workspace when workspace is found by ID", + WorkspaceID: foundWorkspaceID, + ErrorCode: codes.OK, + Response: &v1.GetWorkspaceResponse{ + Result: &v1.Workspace{ + WorkspaceId: foundWorkspaceID, + OwnerId: "mock_owner", + ProjectId: "mock_project_id", + Context: &v1.WorkspaceContext{ + ContextUrl: "https://github.com/gitpod-io/gitpod", + Details: nil, + }, + Description: "This is a mock response", + }, }, - Description: "This is a mock response", }, - }, resp)) + { + name: "not found when workspace is not found by ID", + WorkspaceID: "some-not-found-workspace-id", + ErrorCode: codes.NotFound, + Response: nil, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + resp, err := client.GetWorkspace(ctx, &v1.GetWorkspaceRequest{ + WorkspaceId: scenario.WorkspaceID, + }) + require.Equal(t, scenario.ErrorCode, status.Code(err), "status code must match") + require.True(t, proto.Equal(scenario.Response, resp)) + }) + + } + +} + +type FakeServerConnPool struct { + api gitpod.APIInterface +} + +func (f *FakeServerConnPool) Get(ctx context.Context, token string) (gitpod.APIInterface, error) { + return f.api, nil +} + +type FakeGitpodAPI struct { + workspaces map[string]*gitpod.WorkspaceInfo +} + +func (f *FakeGitpodAPI) GetWorkspace(ctx context.Context, id string) (res *gitpod.WorkspaceInfo, err error) { + w, ok := f.workspaces[id] + if !ok { + return nil, fmt.Errorf("workspace not found") + } + + return w, nil +} + +func (f *FakeGitpodAPI) AdminBlockUser(ctx context.Context, req *gitpod.AdminBlockUserRequest) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetLoggedInUser(ctx context.Context) (res *gitpod.User, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) UpdateLoggedInUser(ctx context.Context, user *gitpod.User) (res *gitpod.User, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetAuthProviders(ctx context.Context) (res []*gitpod.AuthProviderInfo, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetOwnAuthProviders(ctx context.Context) (res []*gitpod.AuthProviderEntry, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) UpdateOwnAuthProvider(ctx context.Context, params *gitpod.UpdateOwnAuthProviderParams) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) DeleteOwnAuthProvider(ctx context.Context, params *gitpod.DeleteOwnAuthProviderParams) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetConfiguration(ctx context.Context) (res *gitpod.Configuration, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetGitpodTokenScopes(ctx context.Context, tokenHash string) (res []string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetToken(ctx context.Context, query *gitpod.GetTokenSearchOptions) (res *gitpod.Token, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetPortAuthenticationToken(ctx context.Context, workspaceID string) (res *gitpod.Token, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) DeleteAccount(ctx context.Context) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetClientRegion(ctx context.Context) (res string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) HasPermission(ctx context.Context, permission *gitpod.PermissionName) (res bool, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetWorkspaces(ctx context.Context, options *gitpod.GetWorkspacesOptions) (res []*gitpod.WorkspaceInfo, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetWorkspaceOwner(ctx context.Context, workspaceID string) (res *gitpod.UserInfo, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetWorkspaceUsers(ctx context.Context, workspaceID string) (res []*gitpod.WorkspaceInstanceUser, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetFeaturedRepositories(ctx context.Context) (res []*gitpod.WhitelistedRepository, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) IsWorkspaceOwner(ctx context.Context, workspaceID string) (res bool, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) CreateWorkspace(ctx context.Context, options *gitpod.CreateWorkspaceOptions) (res *gitpod.WorkspaceCreationResult, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) StartWorkspace(ctx context.Context, id string, options *gitpod.StartWorkspaceOptions) (res *gitpod.StartWorkspaceResult, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) StopWorkspace(ctx context.Context, id string) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) DeleteWorkspace(ctx context.Context, id string) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) SetWorkspaceDescription(ctx context.Context, id string, desc string) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) ControlAdmission(ctx context.Context, id string, level *gitpod.AdmissionLevel) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) UpdateWorkspaceUserPin(ctx context.Context, id string, action *gitpod.PinAction) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) SendHeartBeat(ctx context.Context, options *gitpod.SendHeartBeatOptions) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) WatchWorkspaceImageBuildLogs(ctx context.Context, workspaceID string) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) IsPrebuildDone(ctx context.Context, pwsid string) (res bool, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) SetWorkspaceTimeout(ctx context.Context, workspaceID string, duration *gitpod.WorkspaceTimeoutDuration) (res *gitpod.SetWorkspaceTimeoutResult, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetWorkspaceTimeout(ctx context.Context, workspaceID string) (res *gitpod.GetWorkspaceTimeoutResult, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetOpenPorts(ctx context.Context, workspaceID string) (res []*gitpod.WorkspaceInstancePort, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) OpenPort(ctx context.Context, workspaceID string, port *gitpod.WorkspaceInstancePort) (res *gitpod.WorkspaceInstancePort, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) ClosePort(ctx context.Context, workspaceID string, port float32) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetUserStorageResource(ctx context.Context, options *gitpod.GetUserStorageResourceOptions) (res string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) UpdateUserStorageResource(ctx context.Context, options *gitpod.UpdateUserStorageResourceOptions) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetEnvVars(ctx context.Context) (res []*gitpod.UserEnvVarValue, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) SetEnvVar(ctx context.Context, variable *gitpod.UserEnvVarValue) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) DeleteEnvVar(ctx context.Context, variable *gitpod.UserEnvVarValue) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetContentBlobUploadURL(ctx context.Context, name string) (url string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetContentBlobDownloadURL(ctx context.Context, name string) (url string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetGitpodTokens(ctx context.Context) (res []*gitpod.APIToken, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GenerateNewGitpodToken(ctx context.Context, options *gitpod.GenerateNewGitpodTokenOptions) (res string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) DeleteGitpodToken(ctx context.Context, tokenHash string) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) SendFeedback(ctx context.Context, feedback string) (res string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) RegisterGithubApp(ctx context.Context, installationID string) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) TakeSnapshot(ctx context.Context, options *gitpod.TakeSnapshotOptions) (res string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) WaitForSnapshot(ctx context.Context, snapshotId string) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetSnapshots(ctx context.Context, workspaceID string) (res []*string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) StoreLayout(ctx context.Context, workspaceID string, layoutData string) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GetLayout(ctx context.Context, workspaceID string) (res string, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) GuessGitTokenScopes(ctx context.Context, params *gitpod.GuessGitTokenScopesParams) (res *gitpod.GuessedGitTokenScopes, err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) TrackEvent(ctx context.Context, event *gitpod.RemoteTrackMessage) (err error) { + panic("implement me") +} + +func (f *FakeGitpodAPI) InstanceUpdates(ctx context.Context, instanceID string) (<-chan *gitpod.WorkspaceInstance, error) { + panic("implement me") } diff --git a/components/public-api-server/pkg/proxy/conn.go b/components/public-api-server/pkg/proxy/conn.go new file mode 100644 index 00000000000000..d3af9c44903771 --- /dev/null +++ b/components/public-api-server/pkg/proxy/conn.go @@ -0,0 +1,39 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License-AGPL.txt in the project root for license information. + +package proxy + +import ( + "context" + "fmt" + gitpod "github.com/gitpod-io/gitpod/gitpod-protocol" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" + "net/url" +) + +type ServerConnectionPool interface { + // Get retrieves or creates a new connection for the specified token + // Connections must not be shared across tokens + Get(ctx context.Context, token string) (gitpod.APIInterface, error) +} + +// NoConnectionPool is a simple version of the ServerConnectionPool which always creates a new connection. +type NoConnectionPool struct { + ServerAPI *url.URL +} + +func (p *NoConnectionPool) Get(ctx context.Context, token string) (gitpod.APIInterface, error) { + logger := ctxlogrus.Extract(ctx) + + server, err := gitpod.ConnectToServer(p.ServerAPI.String(), gitpod.ConnectToServerOpts{ + Context: ctx, + Token: token, + Log: logger, + }) + if err != nil { + return nil, fmt.Errorf("failed to create new connection to server: %w", err) + } + + return server, nil +} diff --git a/components/public-api-server/pkg/server/integration_test.go b/components/public-api-server/pkg/server/integration_test.go index 50fd2ec6465680..db3f0dc2e3079e 100644 --- a/components/public-api-server/pkg/server/integration_test.go +++ b/components/public-api-server/pkg/server/integration_test.go @@ -14,6 +14,7 @@ import ( "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "net/url" "testing" ) @@ -21,7 +22,10 @@ func TestPublicAPIServer_v1_WorkspaceService(t *testing.T) { ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "some-token") srv := baseserver.NewForTests(t) - require.NoError(t, register(srv)) + gitpodAPI, err := url.Parse("wss://main.preview.gitpod-dev.com/api/v1") + require.NoError(t, err) + + require.NoError(t, register(srv, Config{GitpodAPI: gitpodAPI})) baseserver.StartServerForTests(t, srv) conn, err := grpc.Dial(srv.GRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) @@ -29,9 +33,6 @@ func TestPublicAPIServer_v1_WorkspaceService(t *testing.T) { workspaceClient := v1.NewWorkspacesServiceClient(conn) - _, err = workspaceClient.GetWorkspace(ctx, &v1.GetWorkspaceRequest{}) - require.NoError(t, err) - _, err = workspaceClient.ListWorkspaces(ctx, &v1.ListWorkspacesRequest{}) requireErrorStatusCode(t, codes.Unimplemented, err) @@ -66,7 +67,11 @@ func TestPublicAPIServer_v1_WorkspaceService(t *testing.T) { func TestPublicAPIServer_v1_PrebuildService(t *testing.T) { ctx := context.Background() srv := baseserver.NewForTests(t) - require.NoError(t, register(srv)) + + gitpodAPI, err := url.Parse("wss://main.preview.gitpod-dev.com/api/v1") + require.NoError(t, err) + + require.NoError(t, register(srv, Config{GitpodAPI: gitpodAPI})) baseserver.StartServerForTests(t, srv) diff --git a/components/public-api-server/pkg/server/server.go b/components/public-api-server/pkg/server/server.go index 9be5635b6d11cf..212649e6c84ee8 100644 --- a/components/public-api-server/pkg/server/server.go +++ b/components/public-api-server/pkg/server/server.go @@ -10,6 +10,7 @@ import ( "github.com/gitpod-io/gitpod/common-go/log" "github.com/gitpod-io/gitpod/public-api-server/middleware" "github.com/gitpod-io/gitpod/public-api-server/pkg/apiv1" + "github.com/gitpod-io/gitpod/public-api-server/pkg/proxy" v1 "github.com/gitpod-io/gitpod/public-api/v1" "github.com/sirupsen/logrus" "net/http" @@ -25,7 +26,7 @@ func Start(logger *logrus.Entry, cfg Config) error { return fmt.Errorf("failed to initialize public api server: %w", err) } - if registerErr := register(srv); registerErr != nil { + if registerErr := register(srv, cfg); registerErr != nil { return fmt.Errorf("failed to register services: %w", registerErr) } @@ -36,12 +37,14 @@ func Start(logger *logrus.Entry, cfg Config) error { return nil } -func register(srv *baseserver.Server) error { +func register(srv *baseserver.Server, cfg Config) error { logger := log.New() m := middleware.NewLoggingMiddleware(logger) srv.HTTPMux().Handle("/", m(http.HandlerFunc(HelloWorldHandler))) - v1.RegisterWorkspacesServiceServer(srv.GRPC(), apiv1.NewWorkspaceService()) + connPool := &proxy.NoConnectionPool{ServerAPI: cfg.GitpodAPI} + + v1.RegisterWorkspacesServiceServer(srv.GRPC(), apiv1.NewWorkspaceService(connPool)) v1.RegisterPrebuildsServiceServer(srv.GRPC(), v1.UnimplementedPrebuildsServiceServer{}) return nil