Skip to content

Clone the tshd gRPC client to allow inspecting errors#39229

Merged
gzdunek merged 17 commits intomasterfrom
gzdunek/cloneable-client
Mar 18, 2024
Merged

Clone the tshd gRPC client to allow inspecting errors#39229
gzdunek merged 17 commits intomasterfrom
gzdunek/cloneable-client

Conversation

@gzdunek
Copy link
Copy Markdown
Contributor

@gzdunek gzdunek commented Mar 12, 2024

Part 1/2 of https://github.com/gravitational/teleport.e/issues/853

The task of this PR is to provide utility functions for wrapping each gRPC call so we can inspect their errors on the renderer side.
Why do we need all these clone* functions? Because when the error goes through the electron context bridge (is a bridge between preload and renderer) most of its properties is removed, so we can't inspect gRPC code or metadata.

The solution for this is converting the error to an object before its crosses the bridge.

Additionally, I switched the client from grpc-js to protobuf-ts. It has a more modern API that uses promises and abort signals. The conversion of createClient is done in the second PR.

Also, emitting our custom errors addresses #30753. The codes are no longer visible, but if we want to bring them back (in a nicer form) we can do it.

I wanted split the changes into something that is easier to review, this PR contains the essential parts: adds functions to convert the calls and abort signals, converts interceptors to the form required by protobuf-ts, replaces the check in isRetryable with the check on error metadata.

Note: this PR doesn't pass TS check, it requires the second PR.

@gzdunek gzdunek added teleport-connect Issues related to Teleport Connect. no-changelog Indicates that a PR does not require a changelog entry backport/branch/v15 labels Mar 12, 2024
@gzdunek gzdunek requested review from avatus and removed request for ibeckermayer March 12, 2024 12:05
It's needed because now we also log a passwordless login stream call.
Copy link
Copy Markdown
Contributor

@avatus avatus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to dive in more but so far looks good

Comment thread lib/teleterm/services/clientcache/clientcache.go
@gzdunek
Copy link
Copy Markdown
Contributor Author

gzdunek commented Mar 14, 2024

@ravicious helped me with creating a function to wrap the entire client, instead of each method manually.
That's a great improvement, it reduces the amount of boilerplate in createClient.ts even further.

I updated both PRs.

Copy link
Copy Markdown
Member

@ravicious ravicious left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job, the code in this PR looks very good. I submitted a bunch of minor suggestions.

Comment thread web/packages/teleterm/src/services/tshd/cloneableClient.test.ts Outdated
Comment thread web/packages/teleterm/src/services/tshd/cloneableClient.ts Outdated
* A unary RPC call. Can be passed over the context bridge.
* Errors are converted to `TshdRpcError` objects.
*/
export type CloneableUnaryCall<I extends object, O extends object> = Pick<
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do types for specific calls need to be exported?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to say that I need a CloneableUnaryCall for types of reportEvent, but actually I don't.
I can do this:

  reportUsageEvent: CloneableClient<ITerminalServiceClient>['reportUsageEvent'];

instead of this:

  reportUsageEvent: (
     input: ReportUsageEventRequest,
     options?: CloneableRpcOptions
   ) => CloneableUnaryCall<ReportUsageEventRequest, apiService.EmptyResponse>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fine for now until we replace createClient. In my opinion, when defining types it's better to avoid to referring to types of other "entities".

It's pretty much like Law of Demeter but for types. Such things are not even possible in languages designed with types in mind from the start. 😏

Comment thread web/packages/teleterm/src/services/tshd/cloneableClient.ts Outdated
Comment on lines +276 to +277
if (error.name === 'RpcError') {
const e = error as RpcError;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (error instanceof RpcError) should work here, shouldn't it? Since we're still on the preload side.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure that I tried this, but it didn't want to work 🤔
But I was wrong, it works perfectly fine, I don't know, maybe I was checking it on the renderer side 🤷‍♂️

Comment thread web/packages/teleterm/src/ui/utils/retryWithRelogin.ts Outdated
Comment thread web/packages/teleterm/src/services/tshd/interceptors.ts Outdated
gzdunek and others added 11 commits March 18, 2024 09:40
…e context bridge (#39230)

* Regenerate protos with the `protobuf-ts` client

* Enable `strictBindCallApply` so `.bind()` results have correct types (instead of `any`)

This is needed for wrapping calls in `createClient`.

* Use `protobuf-ts` client instead of `grpc-js` one

* Convert `createClient` to use `protobuf-ts` response style, clone each call

* Switch callsites to `cloneAbortSignal`

* Replace `error.message` checks with a proper check on the error status code

* Clone the entire client instead of each method separately

* Correct the error `cause` in `ResourceSearchError`

* Remove `params.sortBy` defaults, always set `startKey` to string

* Use a simpler type for `reportUsageEvent`
# Conflicts:
#	web/packages/teleterm/src/services/tshd/createClient.ts
@gzdunek gzdunek changed the title Add functions to wrap gRPC calls to allow inspecting errors Clone the tshd gRPC client to allow inspecting errors Mar 18, 2024
@gzdunek gzdunek enabled auto-merge March 18, 2024 16:19
@gzdunek gzdunek added this pull request to the merge queue Mar 18, 2024
Merged via the queue into master with commit 784d7ac Mar 18, 2024
@gzdunek gzdunek deleted the gzdunek/cloneable-client branch March 18, 2024 16:52
@public-teleport-github-review-bot
Copy link
Copy Markdown

@gzdunek See the table below for backport results.

Branch Result
branch/v15 Failed

gzdunek added a commit that referenced this pull request Mar 22, 2024
* Add functions to clone every gRPC method, so they can be passed over the context bridge

* Replace `@grpc/grpc-js` interceptors with `@protobuf-ts/runtime-rpc` ones

* Replace string check with check on metadata, add missing `AddMetadataToRetryableError`

* Add `pin` to sensitive properties

It's needed because now we also log a passwordless login stream call.

* Add `cloneClient` method to clone the entire gRPC client

* Switch tests to `TshdRetryableError`

* Improve comments

* Move `cloneClient` and `cloneAbortSignal` to the top of the file

* Simplify condition

* Lowercase logs

* Check if the error is `RpcError` using `instanceof`

* Log error object instead of `[object Object]`

* Do not export call specific types

* Wrap gRPC calls in `createClient` to allow passing the errors over the context bridge (#39230)

* Regenerate protos with the `protobuf-ts` client

* Enable `strictBindCallApply` so `.bind()` results have correct types (instead of `any`)

This is needed for wrapping calls in `createClient`.

* Use `protobuf-ts` client instead of `grpc-js` one

* Convert `createClient` to use `protobuf-ts` response style, clone each call

* Switch callsites to `cloneAbortSignal`

* Replace `error.message` checks with a proper check on the error status code

* Clone the entire client instead of each method separately

* Correct the error `cause` in `ResourceSearchError`

* Remove `params.sortBy` defaults, always set `startKey` to string

* Use a simpler type for `reportUsageEvent`

* Fix interceptor test

* Ignore TS error in `highbar.ts`

(cherry picked from commit 784d7ac)
github-merge-queue Bot pushed a commit that referenced this pull request Mar 25, 2024
…ss denied" (#39720)

* Clone the tshd gRPC client to allow inspecting errors (#39229)

* Add functions to clone every gRPC method, so they can be passed over the context bridge

* Replace `@grpc/grpc-js` interceptors with `@protobuf-ts/runtime-rpc` ones

* Replace string check with check on metadata, add missing `AddMetadataToRetryableError`

* Add `pin` to sensitive properties

It's needed because now we also log a passwordless login stream call.

* Add `cloneClient` method to clone the entire gRPC client

* Switch tests to `TshdRetryableError`

* Improve comments

* Move `cloneClient` and `cloneAbortSignal` to the top of the file

* Simplify condition

* Lowercase logs

* Check if the error is `RpcError` using `instanceof`

* Log error object instead of `[object Object]`

* Do not export call specific types

* Wrap gRPC calls in `createClient` to allow passing the errors over the context bridge (#39230)

* Regenerate protos with the `protobuf-ts` client

* Enable `strictBindCallApply` so `.bind()` results have correct types (instead of `any`)

This is needed for wrapping calls in `createClient`.

* Use `protobuf-ts` client instead of `grpc-js` one

* Convert `createClient` to use `protobuf-ts` response style, clone each call

* Switch callsites to `cloneAbortSignal`

* Replace `error.message` checks with a proper check on the error status code

* Clone the entire client instead of each method separately

* Correct the error `cause` in `ResourceSearchError`

* Remove `params.sortBy` defaults, always set `startKey` to string

* Use a simpler type for `reportUsageEvent`

* Fix interceptor test

* Ignore TS error in `highbar.ts`

(cherry picked from commit 784d7ac)

* Do not replace `AccessDenied` error messages with generic "access denied" (#39558)

* Do not replace `AccessDenied` error messages with generic "access denied"

* Replace `error.code` checks with `isTshdRpcError(error, code)`

* Add `ABORTED` and `UNAUTHENTICATED` codes

* Improve `isTshdRpcError` documentation

* Do not catch `retryWithRelogin` errors in `Setup.tsx`

(cherry picked from commit 48c05b6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog Indicates that a PR does not require a changelog entry size/md teleport-connect Issues related to Teleport Connect. ui

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants