-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add WebSocket handler for WebUI database sessions #49749
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First pass.
lib/service/servicecfg/config.go
Outdated
@@ -265,6 +266,10 @@ type Config struct { | |||
// AccessGraph represents AccessGraph server config | |||
AccessGraph AccessGraphConfig | |||
|
|||
// DatabaseREPLGetter is used to retrieve datatabase REPL given the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// DatabaseREPLGetter is used to retrieve datatabase REPL given the | |
// DatabaseREPLGetter is used to retrieve datatabase REPL given the |
lib/client/db/repl/repl.go
Outdated
// REPLGetter is an interface for retrieving REPL constructor functions given | ||
// the database protocol. | ||
type REPLGetter interface { | ||
// GetREPL returns a start function for the specified protocol. | ||
GetREPL(ctx context.Context, dbProtocol string) (REPLNewFunc, error) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some interface nits:
is REPLNewFunc
necessary? can GetREPL
return the REPLInstance
?
would it be easier if REPLGetter
is a func instead of an interface? Do we expect other functions for REPLGetter
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't initialize the REPL right away, so we can reuse this function to fill the SupportsInteractive
field on the API. The WebUI uses this field to show the connect button on the database connect dialog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. if the only goal is the check SupportsInteractive
at an earlier stage, could we have a dedicated call? something like:
type REPLGetter interface {
IsDatabaseProtocolSupported(dbProtocol string) bool
NewREPLInstance(context.Context, *NewREPLConfig) (REPLInstance, error)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the interface, can you take a look?
lib/web/ui/server.go
Outdated
@@ -355,10 +371,10 @@ func MakeDatabase(database types.Database, dbUsers, dbNames []string, requiresRe | |||
} | |||
|
|||
// MakeDatabases creates database objects. | |||
func MakeDatabases(databases []*types.DatabaseV3, dbUsers, dbNames []string) []Database { | |||
func MakeDatabases(databases []*types.DatabaseV3, dbUsers, dbNames []string, interactiveChecker DatabaseInteractiveChecker) []Database { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this function doesn't look right to me. Each database should have different dbUsers
and dbNames
.
Could you double-check if this function is still needed? My understanding is that the current WebUI will only use the UnifiedResourceAPI so the caller "databases" API is no longer necessary. Even if we want to keep it, the caller should run the loop and use access checker for every database.
If we can fix that, I would prefer MakeDatabase
to take in a bool for SupportsInteractive
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated it. Now, the MakeDatabase
only receives the values, and the MakeDatabases
performs the database users/names listing (using the Enumerate
functions). Note that I didn't update other places that directly call the MakeDatabase
to use the same flow (as this seems out of this PR scope). That will be done in a separate PR.
* feat(web): add websocket handler for database webui sessions * refactor: move common structs into a separate package * refactor(web): use ALPN local proxy to dial databases * feat(repl): add default registry * refactor(web): code review suggestions * refactor: update repl config parameters * refactor: move default getter implementation * feat(web): add supports_interactive field on dbs * refactor: code review suggestions * refactor: update database REPL interfaces * chore(web): remove debug print * feat: register postgres repl * refactor(web): update MakeDatabase to receive access checker and interactive * chore(web): remove unused function
* Add PostgreSQL REPL implementation (#49598) * feat(repl): add postgres * refactor(repl): change repl to use a single Run function * test(repl): reduce usage of require.Eventually blocks * refactor(repl): code review suggestions * refactor(repl): code review suggestions * test(repl): increase timeout values * fix(repl): commands formatting * refactor(repl): send close pgconn using a different context * fix(repl): add proper spacing between multi queries * test(repl): add fuzz test for processing commands * Add WebSocket handler for WebUI database sessions (#49749) * feat(web): add websocket handler for database webui sessions * refactor: move common structs into a separate package * refactor(web): use ALPN local proxy to dial databases * feat(repl): add default registry * refactor(web): code review suggestions * refactor: update repl config parameters * refactor: move default getter implementation * feat(web): add supports_interactive field on dbs * refactor: code review suggestions * refactor: update database REPL interfaces * chore(web): remove debug print * feat: register postgres repl * refactor(web): update MakeDatabase to receive access checker and interactive * chore(web): remove unused function * Database access through WebUI (#49979) * feat(web): add database terminal access * chore(web): make explict type cast * refactor(web): code review suggestions * chore(web): fix lint errors * refactor(web): lint errors * refactor: code review suggestions * refactor(web): filter wildcard options from connect dialog * chore(web): lint * refactor(web): code review suggestions
Part of #44956 (RFD 0181)
This PR adds a new WebSocket that the WebUI will use to start database sessions. Those sessions will rely on protocol-specific REPLs (we're initially adding PostgreSQL REPL), which will act as a database client for the WebUI terminal client. The handle is responsible for issuing session certificates and calling the REPL implementation, which will connect to the database proxy server using the given configuration. This flow is similar to what is done for Kube Exec from WebUI, where we establish a "localhost" connection to the proxy using the database client.
Note
This implementation differs slightly from my original PoC and the short RFD description. Instead of directly dialing the database servers (duplicating the flow implemented by the Proxy server), we're dialing the proxy server to perform its job, so the handler + REPL act like regular database clients (such as
psql
). The main reason for this change was to try and keep the closest flow astsh
, and it also proved easier to implement (as the proxy server already has test coverage). This implementation detail should keep the UX of databases WebUI Access as described on the RFD.The PostgreSQL REPL implementation is decoupled (at #49598), but this PR defines the function signature and who/how will be responsible for connecting to the database. So the changes here will directly affect the other PR.