feat: add agent metadata (#6614)

This commit is contained in:
Ammar Bandukwala
2023-03-31 15:26:19 -05:00
committed by GitHub
parent c191692751
commit ca4fa81570
62 changed files with 3139 additions and 727 deletions
+95
View File
@@ -19,6 +19,7 @@ import (
"tailscale.com/tailcfg"
"cdr.dev/slog"
"github.com/coder/coder/coderd/tracing"
"github.com/coder/coder/tailnet"
"github.com/coder/retry"
)
@@ -75,6 +76,31 @@ var WorkspaceAgentLifecycleOrder = []WorkspaceAgentLifecycle{
WorkspaceAgentLifecycleOff,
}
type WorkspaceAgentMetadataResult struct {
CollectedAt time.Time `json:"collected_at" format:"date-time"`
// Age is the number of seconds since the metadata was collected.
// It is provided in addition to CollectedAt to protect against clock skew.
Age int64 `json:"age"`
Value string `json:"value"`
Error string `json:"error"`
}
// WorkspaceAgentMetadataDescription is a description of dynamic metadata the agent should report
// back to coderd. It is provided via the `metadata` list in the `coder_agent`
// block.
type WorkspaceAgentMetadataDescription struct {
DisplayName string `json:"display_name"`
Key string `json:"key"`
Script string `json:"script"`
Interval int64 `json:"interval"`
Timeout int64 `json:"timeout"`
}
type WorkspaceAgentMetadata struct {
Result WorkspaceAgentMetadataResult `json:"result"`
Description WorkspaceAgentMetadataDescription `json:"description"`
}
type WorkspaceAgent struct {
ID uuid.UUID `json:"id" format:"uuid"`
CreatedAt time.Time `json:"created_at" format:"date-time"`
@@ -258,6 +284,75 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
return agentConn, nil
}
// WatchWorkspaceAgentMetadata watches the metadata of a workspace agent.
// The returned channel will be closed when the context is canceled. Exactly
// one error will be sent on the error channel. The metadata channel is never closed.
func (c *Client) WatchWorkspaceAgentMetadata(ctx context.Context, id uuid.UUID) (<-chan []WorkspaceAgentMetadata, <-chan error) {
ctx, span := tracing.StartSpan(ctx)
defer span.End()
metadataChan := make(chan []WorkspaceAgentMetadata, 256)
watch := func() error {
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaceagents/%s/watch-metadata", id), nil)
if err != nil {
return err
}
if res.StatusCode != http.StatusOK {
return ReadBodyAsError(res)
}
nextEvent := ServerSentEventReader(ctx, res.Body)
defer res.Body.Close()
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
break
}
sse, err := nextEvent()
if err != nil {
return err
}
b, ok := sse.Data.([]byte)
if !ok {
return xerrors.Errorf("unexpected data type: %T", sse.Data)
}
switch sse.Type {
case ServerSentEventTypeData:
var met []WorkspaceAgentMetadata
err = json.Unmarshal(b, &met)
if err != nil {
return xerrors.Errorf("unmarshal metadata: %w", err)
}
metadataChan <- met
case ServerSentEventTypeError:
var r Response
err = json.Unmarshal(b, &r)
if err != nil {
return xerrors.Errorf("unmarshal error: %w", err)
}
return xerrors.Errorf("%+v", r)
default:
return xerrors.Errorf("unexpected event type: %s", sse.Type)
}
}
}
errorChan := make(chan error, 1)
go func() {
defer close(errorChan)
errorChan <- watch()
}()
return metadataChan, errorChan
}
// WorkspaceAgent returns an agent by ID.
func (c *Client) WorkspaceAgent(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error) {
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaceagents/%s", id), nil)