Skip to content

Add new WorkspaceUpdates tailnet RPC #14716

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

Closed
spikecurtis opened this issue Sep 18, 2024 · 2 comments · Fixed by #14847
Closed

Add new WorkspaceUpdates tailnet RPC #14716

spikecurtis opened this issue Sep 18, 2024 · 2 comments · Fixed by #14847
Assignees
Labels
networking Area: networking

Comments

@spikecurtis
Copy link
Contributor

spikecurtis commented Sep 18, 2024

Today, tailnet clients connect to exactly one workspace Agent known at start of day. In order to support a desktop native VPN to Coder workspaces, we need a new tailnet RPC that delivers a stream of updates about workspaces being created, started, stopped and deleted, so that the native RPC can automatically connect to available agents.

The RPC will be part of the Tailnet API, and adds the following messages and RPC calls:

message WorkspaceUpdatesRequest {}

message WorkspaceUpdate {
	repeated Workspace upserted_workspaces = 1;
	repeated Agent upserted_agents = 2;
	repeated Workspace deleted_workspaces = 3;
	repeated Agent deleted_agents = 4;
}

message Workspace {
	bytes id = 1; // UUID
	string name = 2;

	enum Status {
		UNKNOWN = 0;
		PENDING = 1;
		STARTING = 2;
		RUNNING = 3;
		STOPPING = 4;
		STOPPED = 5;
		FAILED = 6;
		CANCELING = 7;
		CANCELED = 8;
		DELETING = 9;
		DELETED = 10;
	}
	Status status = 3;
}

message Agent {
	bytes id = 1; // UUID
	string name = 2;
	bytes workspace_id = 3; // UUID
}

service Tailnet {
	rpc WorkspaceUpdates(WorkspaceUpdatesRequest) returns (stream WorkspaceUpdate);
}

Because the RPC is not tied to a specific agent, it will not be available on the existing api/v2/workspaceagents/<agent>/coordinate HTTP endpoint, and needs to be added to a new api/v2/users/me/tailnet endpoint.

@ethanndickson
Copy link
Member

ethanndickson commented Sep 24, 2024

Since this workspace status isn't an existing type, I assume it's to be set using the provisioner job status and the build transition? I'm proposing:

WHEN job_status = 'running' AND transition = 'start' THEN 'starting'
WHEN job_status = 'succeeded' AND transition = 'start' THEN 'running'
WHEN job_status = 'running' AND transition = 'stop' THEN 'stopping'
WHEN job_status = 'succeeded' AND transition = 'stop' THEN 'stopped'
WHEN job_status = 'succeeded' AND transition = 'delete' THEN 'deleted'
WHEN job_status = 'running' AND transition = 'delete' THEN 'deleting'
WHEN job_status = 'pending' THEN 'pending'
WHEN job_status = 'failed' THEN 'failed'
WHEN job_status = 'canceling' THEN 'canceling'
WHEN job_status = 'canceled' THEN 'canceled'
ELSE 'unknown'

@spikecurtis
Copy link
Contributor Author

There is a derived type and conversion algorithm:

func convertWorkspaceStatus(jobStatus codersdk.ProvisionerJobStatus, transition codersdk.WorkspaceTransition) codersdk.WorkspaceStatus {

@matifali matifali removed the feature label Oct 14, 2024
ethanndickson added a commit that referenced this issue Nov 1, 2024
We currently send empty payloads to pubsub channels of the form `workspace:<workspace_id>` to notify listeners of updates to workspaces (such as for refreshing the workspace dashboard).

To support #14716, we'll instead send `WorkspaceEvent` payloads to pubsub channels of the form `workspace_owner:<owner_id>`. This enables a listener to receive events for all workspaces owned by a user.
This PR replaces the usage of the old channels without modifying any existing behaviors.

```
type WorkspaceEvent struct {
	Kind        WorkspaceEventKind `json:"kind"`
	WorkspaceID uuid.UUID          `json:"workspace_id" format:"uuid"`
	// AgentID is only set for WorkspaceEventKindAgent* events
	// (excluding AgentTimeout)
	AgentID *uuid.UUID `json:"agent_id,omitempty" format:"uuid"`
}
```

We've defined `WorkspaceEventKind`s based on how the old channel was used, but it's not yet necessary to inspect the types of any of the events, as the existing listeners are designed to fire off any of them.

```
WorkspaceEventKindStateChange     WorkspaceEventKind = "state_change"
WorkspaceEventKindStatsUpdate     WorkspaceEventKind = "stats_update"
WorkspaceEventKindMetadataUpdate  WorkspaceEventKind = "mtd_update"
WorkspaceEventKindAppHealthUpdate WorkspaceEventKind = "app_health"

WorkspaceEventKindAgentLifecycleUpdate  WorkspaceEventKind = "agt_lifecycle_update"
WorkspaceEventKindAgentLogsUpdate       WorkspaceEventKind = "agt_logs_update"
WorkspaceEventKindAgentConnectionUpdate WorkspaceEventKind = "agt_connection_update"
WorkspaceEventKindAgentLogsOverflow     WorkspaceEventKind = "agt_logs_overflow"
WorkspaceEventKindAgentTimeout          WorkspaceEventKind = "agt_timeout"
```
ethanndickson added a commit that referenced this issue Nov 1, 2024
Second PR for #14716.

Adds a query that, given a user ID, returns all the workspaces they own, that can also be `ActionRead` by the requesting user.

```
type GetWorkspacesAndAgentsByOwnerIDRow struct {
	WorkspaceID      uuid.UUID            `db:"workspace_id" json:"workspace_id"`
	WorkspaceName    string               `db:"workspace_name" json:"workspace_name"`
	JobStatus        ProvisionerJobStatus `db:"job_status" json:"job_status"`
	Transition       WorkspaceTransition  `db:"transition" json:"transition"`
	Agents           []AgentIDNamePair    `db:"agents" json:"agents"`
}
```
 `JobStatus` and `Transition` are set using the latest build/job of the workspace. Deleted workspaces are not included.
ethanndickson added a commit that referenced this issue Nov 1, 2024
Closes #14716
Closes #14717

Adds a new user-scoped tailnet API endpoint (`api/v2/tailnet`) with a new RPC stream for receiving updates on workspaces owned by a specific user, as defined in #14716. 

When a stream is started, the `WorkspaceUpdatesProvider` will begin listening on the user-scoped pubsub events implemented in #14964. When a relevant event type is seen (such as a workspace state transition), the provider will query the DB for all the workspaces (and agents) owned by the user. This gets compared against the result of the previous query to produce a set of workspace updates. 

Workspace updates can be requested for any user ID, however only workspaces the authorised user is permitted to `ActionRead` will have their updates streamed.
Opening a tunnel to an agent requires that the user can perform `ActionSSH` against the workspace containing it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
networking Area: networking
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants