mirror of
https://github.com/coder/coder.git
synced 2026-06-03 04:58:23 +00:00
7cc2b22568
relates to #21335 Adds UpdateAppStatus on the agentsocket, wired up to forward to Coderd over the dRPC connection the agent maintains. Disclosure: I used AI to generate significant portions of this PR, but hand-reviewed and tweaked the code. I consider it approximately indistinguishable from what I would have done by hand.
156 lines
4.5 KiB
Go
156 lines
4.5 KiB
Go
package agentsocket
|
|
|
|
import (
|
|
"context"
|
|
|
|
"golang.org/x/xerrors"
|
|
"storj.io/drpc"
|
|
"storj.io/drpc/drpcconn"
|
|
|
|
"github.com/coder/coder/v2/agent/agentsocket/proto"
|
|
agentproto "github.com/coder/coder/v2/agent/proto"
|
|
"github.com/coder/coder/v2/agent/unit"
|
|
)
|
|
|
|
// Option represents a configuration option for NewClient.
|
|
type Option func(*options)
|
|
|
|
type options struct {
|
|
path string
|
|
}
|
|
|
|
// WithPath sets the socket path. If not provided or empty, the client will
|
|
// auto-discover the default socket path.
|
|
func WithPath(path string) Option {
|
|
return func(opts *options) {
|
|
if path == "" {
|
|
return
|
|
}
|
|
opts.path = path
|
|
}
|
|
}
|
|
|
|
// Client provides a client for communicating with the workspace agentsocket API.
|
|
type Client struct {
|
|
client proto.DRPCAgentSocketClient
|
|
conn drpc.Conn
|
|
}
|
|
|
|
// NewClient creates a new socket client and opens a connection to the socket.
|
|
// If path is not provided via WithPath or is empty, it will auto-discover the
|
|
// default socket path.
|
|
func NewClient(ctx context.Context, opts ...Option) (*Client, error) {
|
|
options := &options{}
|
|
for _, opt := range opts {
|
|
opt(options)
|
|
}
|
|
|
|
conn, err := dialSocket(ctx, options.path)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("connect to socket: %w", err)
|
|
}
|
|
|
|
drpcConn := drpcconn.New(conn)
|
|
client := proto.NewDRPCAgentSocketClient(drpcConn)
|
|
|
|
return &Client{
|
|
client: client,
|
|
conn: drpcConn,
|
|
}, nil
|
|
}
|
|
|
|
// Close closes the socket connection.
|
|
func (c *Client) Close() error {
|
|
return c.conn.Close()
|
|
}
|
|
|
|
// Ping sends a ping request to the agent.
|
|
func (c *Client) Ping(ctx context.Context) error {
|
|
_, err := c.client.Ping(ctx, &proto.PingRequest{})
|
|
return err
|
|
}
|
|
|
|
// SyncStart starts a unit in the dependency graph.
|
|
func (c *Client) SyncStart(ctx context.Context, unitName unit.ID) error {
|
|
_, err := c.client.SyncStart(ctx, &proto.SyncStartRequest{
|
|
Unit: string(unitName),
|
|
})
|
|
return err
|
|
}
|
|
|
|
// SyncWant declares a dependency between units.
|
|
func (c *Client) SyncWant(ctx context.Context, unitName, dependsOn unit.ID) error {
|
|
_, err := c.client.SyncWant(ctx, &proto.SyncWantRequest{
|
|
Unit: string(unitName),
|
|
DependsOn: string(dependsOn),
|
|
})
|
|
return err
|
|
}
|
|
|
|
// SyncComplete marks a unit as complete in the dependency graph.
|
|
func (c *Client) SyncComplete(ctx context.Context, unitName unit.ID) error {
|
|
_, err := c.client.SyncComplete(ctx, &proto.SyncCompleteRequest{
|
|
Unit: string(unitName),
|
|
})
|
|
return err
|
|
}
|
|
|
|
// SyncReady requests whether a unit is ready to be started. That is, all dependencies are satisfied.
|
|
func (c *Client) SyncReady(ctx context.Context, unitName unit.ID) (bool, error) {
|
|
resp, err := c.client.SyncReady(ctx, &proto.SyncReadyRequest{
|
|
Unit: string(unitName),
|
|
})
|
|
if err != nil {
|
|
return false, xerrors.Errorf("sync ready: %w", err)
|
|
}
|
|
return resp.Ready, nil
|
|
}
|
|
|
|
// SyncStatus gets the status of a unit and its dependencies.
|
|
func (c *Client) SyncStatus(ctx context.Context, unitName unit.ID) (SyncStatusResponse, error) {
|
|
resp, err := c.client.SyncStatus(ctx, &proto.SyncStatusRequest{
|
|
Unit: string(unitName),
|
|
})
|
|
if err != nil {
|
|
return SyncStatusResponse{}, err
|
|
}
|
|
|
|
var dependencies []DependencyInfo
|
|
for _, dep := range resp.Dependencies {
|
|
dependencies = append(dependencies, DependencyInfo{
|
|
DependsOn: unit.ID(dep.DependsOn),
|
|
RequiredStatus: unit.Status(dep.RequiredStatus),
|
|
CurrentStatus: unit.Status(dep.CurrentStatus),
|
|
IsSatisfied: dep.IsSatisfied,
|
|
})
|
|
}
|
|
|
|
return SyncStatusResponse{
|
|
UnitName: unitName,
|
|
Status: unit.Status(resp.Status),
|
|
IsReady: resp.IsReady,
|
|
Dependencies: dependencies,
|
|
}, nil
|
|
}
|
|
|
|
// UpdateAppStatus forwards an app status update to coderd via the agent.
|
|
func (c *Client) UpdateAppStatus(ctx context.Context, req *agentproto.UpdateAppStatusRequest) (*agentproto.UpdateAppStatusResponse, error) {
|
|
return c.client.UpdateAppStatus(ctx, req)
|
|
}
|
|
|
|
// SyncStatusResponse contains the status information for a unit.
|
|
type SyncStatusResponse struct {
|
|
UnitName unit.ID `table:"unit,default_sort" json:"unit_name"`
|
|
Status unit.Status `table:"status" json:"status"`
|
|
IsReady bool `table:"ready" json:"is_ready"`
|
|
Dependencies []DependencyInfo `table:"dependencies" json:"dependencies"`
|
|
}
|
|
|
|
// DependencyInfo contains information about a unit dependency.
|
|
type DependencyInfo struct {
|
|
DependsOn unit.ID `table:"depends on,default_sort" json:"depends_on"`
|
|
RequiredStatus unit.Status `table:"required status" json:"required_status"`
|
|
CurrentStatus unit.Status `table:"current status" json:"current_status"`
|
|
IsSatisfied bool `table:"satisfied" json:"is_satisfied"`
|
|
}
|