mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore!: remove deprecated agent v1 routes (#13486)
This commit is contained in:
Generated
-624
@@ -5696,62 +5696,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/app-health": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Submit workspace agent application health",
|
||||
"operationId": "submit-workspace-agent-application-health",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Application health request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostAppHealthsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/coordinate": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"description": "It accepts a WebSocket connection to an agent that listens to\nincoming connections and publishes node updates.",
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Coordinate workspace agent via Tailnet",
|
||||
"operationId": "coordinate-workspace-agent-via-tailnet",
|
||||
"responses": {
|
||||
"101": {
|
||||
"description": "Switching Protocols"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/external-auth": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -5949,190 +5893,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/manifest": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Get authorized workspace agent manifest",
|
||||
"operationId": "get-authorized-workspace-agent-manifest",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.Manifest"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/metadata": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Submit workspace agent metadata",
|
||||
"operationId": "submit-workspace-agent-metadata",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Workspace agent metadata request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.PostMetadataRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/metadata/{key}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Removed: Submit workspace agent metadata",
|
||||
"operationId": "removed-submit-workspace-agent-metadata",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Workspace agent metadata request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostMetadataRequestDeprecated"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "string",
|
||||
"description": "metadata key",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/report-lifecycle": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Submit workspace agent lifecycle state",
|
||||
"operationId": "submit-workspace-agent-lifecycle-state",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Workspace agent lifecycle request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostLifecycleRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/report-stats": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Submit workspace agent stats",
|
||||
"operationId": "submit-workspace-agent-stats",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Stats request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.Stats"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.StatsResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/rpc": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -6155,84 +5915,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/startup": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Submit workspace agent startup",
|
||||
"operationId": "submit-workspace-agent-startup",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Startup request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostStartupRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/startup-logs": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Removed: Patch workspace agent logs",
|
||||
"operationId": "removed-patch-workspace-agent-logs",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "logs",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PatchLogs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -7882,65 +7564,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetric": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.AgentMetricLabel"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"counter",
|
||||
"gauge"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/agentsdk.AgentMetricType"
|
||||
}
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetricLabel": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetricType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"counter",
|
||||
"gauge"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"AgentMetricTypeCounter",
|
||||
"AgentMetricTypeGauge"
|
||||
]
|
||||
},
|
||||
"agentsdk.AuthenticateResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8025,95 +7648,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Manifest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"agent_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"apps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceApp"
|
||||
}
|
||||
},
|
||||
"derp_force_websockets": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"derpmap": {
|
||||
"$ref": "#/definitions/tailcfg.DERPMap"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string"
|
||||
},
|
||||
"disable_direct_connections": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"environment_variables": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"git_auth_configs": {
|
||||
"description": "GitAuthConfigs stores the number of Git configurations\nthe Coder deployment has. If this number is \u003e0, we\nset up special configuration in the workspace.",
|
||||
"type": "integer"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentMetadataDescription"
|
||||
}
|
||||
},
|
||||
"motd_file": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner_name": {
|
||||
"description": "OwnerName and WorkspaceID are used by an open-source user to identify the workspace.\nWe do not provide insurance that this will not be removed in the future,\nbut if it's easy to persist lets keep it around.",
|
||||
"type": "string"
|
||||
},
|
||||
"scripts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentScript"
|
||||
}
|
||||
},
|
||||
"vscode_port_proxy_uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"workspace_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"workspace_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Metadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {
|
||||
"description": "Age is the number of seconds since the metadata was collected.\nIt is provided in addition to CollectedAt to protect against clock skew.",
|
||||
"type": "integer"
|
||||
},
|
||||
"collected_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PatchLogs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8128,29 +7662,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostAppHealthsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"healths": {
|
||||
"description": "Healths is a map of the workspace app name and the health of the app.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppHealth"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostLifecycleRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"changed_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentLifecycle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostLogSourceRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8166,121 +7677,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostMetadataRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostMetadataRequestDeprecated": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {
|
||||
"description": "Age is the number of seconds since the metadata was collected.\nIt is provided in addition to CollectedAt to protect against clock skew.",
|
||||
"type": "integer"
|
||||
},
|
||||
"collected_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostStartupRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expanded_directory": {
|
||||
"type": "string"
|
||||
},
|
||||
"subsystems": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.AgentSubsystem"
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Stats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"connection_count": {
|
||||
"description": "ConnectionCount is the number of connections received by an agent.",
|
||||
"type": "integer"
|
||||
},
|
||||
"connection_median_latency_ms": {
|
||||
"description": "ConnectionMedianLatencyMS is the median latency of all connections in milliseconds.",
|
||||
"type": "number"
|
||||
},
|
||||
"connections_by_proto": {
|
||||
"description": "ConnectionsByProto is a count of connections by protocol.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"metrics": {
|
||||
"description": "Metrics collected by the agent",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.AgentMetric"
|
||||
}
|
||||
},
|
||||
"rx_bytes": {
|
||||
"description": "RxBytes is the number of received bytes.",
|
||||
"type": "integer"
|
||||
},
|
||||
"rx_packets": {
|
||||
"description": "RxPackets is the number of received packets.",
|
||||
"type": "integer"
|
||||
},
|
||||
"session_count_jetbrains": {
|
||||
"description": "SessionCountJetBrains is the number of connections received by an agent\nthat are from our JetBrains extension.",
|
||||
"type": "integer"
|
||||
},
|
||||
"session_count_reconnecting_pty": {
|
||||
"description": "SessionCountReconnectingPTY is the number of connections received by an agent\nthat are from the reconnecting web terminal.",
|
||||
"type": "integer"
|
||||
},
|
||||
"session_count_ssh": {
|
||||
"description": "SessionCountSSH is the number of connections received by an agent\nthat are normal, non-tagged SSH sessions.",
|
||||
"type": "integer"
|
||||
},
|
||||
"session_count_vscode": {
|
||||
"description": "SessionCountVSCode is the number of connections received by an agent\nthat are from our VS Code extension.",
|
||||
"type": "integer"
|
||||
},
|
||||
"tx_bytes": {
|
||||
"description": "TxBytes is the number of transmitted bytes.",
|
||||
"type": "integer"
|
||||
},
|
||||
"tx_packets": {
|
||||
"description": "TxPackets is the number of transmitted bytes.",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.StatsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"report_interval": {
|
||||
"description": "ReportInterval is the duration after which the agent should send stats\nagain.",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coderd.SCIMUser": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -13166,26 +12562,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentMetadataDescription": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"interval": {
|
||||
"type": "integer"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"script": {
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentPortShare": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Generated
-566
@@ -5024,54 +5024,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/app-health": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Submit workspace agent application health",
|
||||
"operationId": "submit-workspace-agent-application-health",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Application health request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostAppHealthsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/coordinate": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"description": "It accepts a WebSocket connection to an agent that listens to\nincoming connections and publishes node updates.",
|
||||
"tags": ["Agents"],
|
||||
"summary": "Coordinate workspace agent via Tailnet",
|
||||
"operationId": "coordinate-workspace-agent-via-tailnet",
|
||||
"responses": {
|
||||
"101": {
|
||||
"description": "Switching Protocols"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/external-auth": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -5245,168 +5197,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/manifest": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Get authorized workspace agent manifest",
|
||||
"operationId": "get-authorized-workspace-agent-manifest",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.Manifest"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/metadata": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Submit workspace agent metadata",
|
||||
"operationId": "submit-workspace-agent-metadata",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Workspace agent metadata request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.PostMetadataRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/metadata/{key}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Removed: Submit workspace agent metadata",
|
||||
"operationId": "removed-submit-workspace-agent-metadata",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Workspace agent metadata request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostMetadataRequestDeprecated"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "string",
|
||||
"description": "metadata key",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/report-lifecycle": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Submit workspace agent lifecycle state",
|
||||
"operationId": "submit-workspace-agent-lifecycle-state",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Workspace agent lifecycle request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostLifecycleRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/report-stats": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Submit workspace agent stats",
|
||||
"operationId": "submit-workspace-agent-stats",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Stats request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.Stats"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.StatsResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/rpc": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -5427,72 +5217,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/startup": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Submit workspace agent startup",
|
||||
"operationId": "submit-workspace-agent-startup",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Startup request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostStartupRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/startup-logs": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Removed: Patch workspace agent logs",
|
||||
"operationId": "removed-patch-workspace-agent-logs",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "logs",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PatchLogs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -6969,49 +6693,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetric": {
|
||||
"type": "object",
|
||||
"required": ["name", "type", "value"],
|
||||
"properties": {
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.AgentMetricLabel"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": ["counter", "gauge"],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/agentsdk.AgentMetricType"
|
||||
}
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetricLabel": {
|
||||
"type": "object",
|
||||
"required": ["name", "value"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetricType": {
|
||||
"type": "string",
|
||||
"enum": ["counter", "gauge"],
|
||||
"x-enum-varnames": ["AgentMetricTypeCounter", "AgentMetricTypeGauge"]
|
||||
},
|
||||
"agentsdk.AuthenticateResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7091,95 +6772,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Manifest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"agent_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"apps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceApp"
|
||||
}
|
||||
},
|
||||
"derp_force_websockets": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"derpmap": {
|
||||
"$ref": "#/definitions/tailcfg.DERPMap"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string"
|
||||
},
|
||||
"disable_direct_connections": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"environment_variables": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"git_auth_configs": {
|
||||
"description": "GitAuthConfigs stores the number of Git configurations\nthe Coder deployment has. If this number is \u003e0, we\nset up special configuration in the workspace.",
|
||||
"type": "integer"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentMetadataDescription"
|
||||
}
|
||||
},
|
||||
"motd_file": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner_name": {
|
||||
"description": "OwnerName and WorkspaceID are used by an open-source user to identify the workspace.\nWe do not provide insurance that this will not be removed in the future,\nbut if it's easy to persist lets keep it around.",
|
||||
"type": "string"
|
||||
},
|
||||
"scripts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentScript"
|
||||
}
|
||||
},
|
||||
"vscode_port_proxy_uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"workspace_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"workspace_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Metadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {
|
||||
"description": "Age is the number of seconds since the metadata was collected.\nIt is provided in addition to CollectedAt to protect against clock skew.",
|
||||
"type": "integer"
|
||||
},
|
||||
"collected_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PatchLogs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7194,29 +6786,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostAppHealthsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"healths": {
|
||||
"description": "Healths is a map of the workspace app name and the health of the app.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppHealth"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostLifecycleRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"changed_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentLifecycle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostLogSourceRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7232,121 +6801,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostMetadataRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostMetadataRequestDeprecated": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {
|
||||
"description": "Age is the number of seconds since the metadata was collected.\nIt is provided in addition to CollectedAt to protect against clock skew.",
|
||||
"type": "integer"
|
||||
},
|
||||
"collected_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostStartupRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expanded_directory": {
|
||||
"type": "string"
|
||||
},
|
||||
"subsystems": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.AgentSubsystem"
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Stats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"connection_count": {
|
||||
"description": "ConnectionCount is the number of connections received by an agent.",
|
||||
"type": "integer"
|
||||
},
|
||||
"connection_median_latency_ms": {
|
||||
"description": "ConnectionMedianLatencyMS is the median latency of all connections in milliseconds.",
|
||||
"type": "number"
|
||||
},
|
||||
"connections_by_proto": {
|
||||
"description": "ConnectionsByProto is a count of connections by protocol.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"metrics": {
|
||||
"description": "Metrics collected by the agent",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.AgentMetric"
|
||||
}
|
||||
},
|
||||
"rx_bytes": {
|
||||
"description": "RxBytes is the number of received bytes.",
|
||||
"type": "integer"
|
||||
},
|
||||
"rx_packets": {
|
||||
"description": "RxPackets is the number of received packets.",
|
||||
"type": "integer"
|
||||
},
|
||||
"session_count_jetbrains": {
|
||||
"description": "SessionCountJetBrains is the number of connections received by an agent\nthat are from our JetBrains extension.",
|
||||
"type": "integer"
|
||||
},
|
||||
"session_count_reconnecting_pty": {
|
||||
"description": "SessionCountReconnectingPTY is the number of connections received by an agent\nthat are from the reconnecting web terminal.",
|
||||
"type": "integer"
|
||||
},
|
||||
"session_count_ssh": {
|
||||
"description": "SessionCountSSH is the number of connections received by an agent\nthat are normal, non-tagged SSH sessions.",
|
||||
"type": "integer"
|
||||
},
|
||||
"session_count_vscode": {
|
||||
"description": "SessionCountVSCode is the number of connections received by an agent\nthat are from our VS Code extension.",
|
||||
"type": "integer"
|
||||
},
|
||||
"tx_bytes": {
|
||||
"description": "TxBytes is the number of transmitted bytes.",
|
||||
"type": "integer"
|
||||
},
|
||||
"tx_packets": {
|
||||
"description": "TxPackets is the number of transmitted bytes.",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.StatsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"report_interval": {
|
||||
"description": "ReportInterval is the duration after which the agent should send stats\nagain.",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coderd.SCIMUser": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -11976,26 +11430,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentMetadataDescription": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"interval": {
|
||||
"type": "integer"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"script": {
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentPortShare": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1002,23 +1002,11 @@ func New(options *Options) *API {
|
||||
Optional: false,
|
||||
}))
|
||||
r.Get("/rpc", api.workspaceAgentRPC)
|
||||
r.Get("/manifest", api.workspaceAgentManifest)
|
||||
// This route is deprecated and will be removed in a future release.
|
||||
// New agents will use /me/manifest instead.
|
||||
r.Get("/metadata", api.workspaceAgentManifest)
|
||||
r.Post("/startup", api.postWorkspaceAgentStartup)
|
||||
r.Patch("/startup-logs", api.patchWorkspaceAgentLogsDeprecated)
|
||||
r.Patch("/logs", api.patchWorkspaceAgentLogs)
|
||||
r.Post("/app-health", api.postWorkspaceAppHealth)
|
||||
// Deprecated: Required to support legacy agents
|
||||
r.Get("/gitauth", api.workspaceAgentsGitAuth)
|
||||
r.Get("/external-auth", api.workspaceAgentsExternalAuth)
|
||||
r.Get("/gitsshkey", api.agentGitSSHKey)
|
||||
r.Get("/coordinate", api.workspaceAgentCoordinate)
|
||||
r.Post("/report-stats", api.workspaceAgentReportStats)
|
||||
r.Post("/report-lifecycle", api.workspaceAgentReportLifecycle)
|
||||
r.Post("/metadata", api.workspaceAgentPostMetadata)
|
||||
r.Post("/metadata/{key}", api.workspaceAgentPostMetadataDeprecated)
|
||||
r.Post("/log-source", api.workspaceAgentPostLogSource)
|
||||
})
|
||||
r.Route("/{workspaceagent}", func(r chi.Router) {
|
||||
|
||||
@@ -3,13 +3,9 @@ package coderd
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
)
|
||||
|
||||
// @Summary Removed: Get parameters by template version
|
||||
@@ -34,19 +30,6 @@ func templateVersionSchemaDeprecated(rw http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(r.Context(), rw, http.StatusOK, []struct{}{})
|
||||
}
|
||||
|
||||
// @Summary Removed: Patch workspace agent logs
|
||||
// @ID removed-patch-workspace-agent-logs
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PatchLogs true "logs"
|
||||
// @Success 200 {object} codersdk.Response
|
||||
// @Router /workspaceagents/me/startup-logs [patch]
|
||||
func (api *API) patchWorkspaceAgentLogsDeprecated(rw http.ResponseWriter, r *http.Request) {
|
||||
api.patchWorkspaceAgentLogs(rw, r)
|
||||
}
|
||||
|
||||
// @Summary Removed: Get logs by workspace agent
|
||||
// @ID removed-get-logs-by-workspace-agent
|
||||
// @Security CoderSessionToken
|
||||
@@ -77,45 +60,6 @@ func (api *API) workspaceAgentsGitAuth(rw http.ResponseWriter, r *http.Request)
|
||||
api.workspaceAgentsExternalAuth(rw, r)
|
||||
}
|
||||
|
||||
// @Summary Removed: Submit workspace agent metadata
|
||||
// @ID removed-submit-workspace-agent-metadata
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PostMetadataRequestDeprecated true "Workspace agent metadata request"
|
||||
// @Param key path string true "metadata key" format(string)
|
||||
// @Success 204 "Success"
|
||||
// @Router /workspaceagents/me/metadata/{key} [post]
|
||||
// @x-apidocgen {"skip": true}
|
||||
func (api *API) workspaceAgentPostMetadataDeprecated(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var req agentsdk.PostMetadataRequestDeprecated
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
|
||||
key := chi.URLParam(r, "key")
|
||||
|
||||
err := api.workspaceAgentUpdateMetadata(ctx, workspaceAgent, agentsdk.PostMetadataRequest{
|
||||
Metadata: []agentsdk.Metadata{
|
||||
{
|
||||
Key: key,
|
||||
WorkspaceAgentMetadataResult: req,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
api.Logger.Error(ctx, "failed to handle metadata request", slog.Error(err))
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// @Summary Removed: Get workspace resources for workspace build
|
||||
// @ID removed-get-workspace-resources-for-workspace-build
|
||||
// @Security CoderSessionToken
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
agentproto "github.com/coder/coder/v2/agent/proto"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
@@ -87,25 +88,37 @@ func TestCollectInsights(t *testing.T) {
|
||||
)
|
||||
|
||||
// Start an agent so that we can generate stats.
|
||||
var agentClients []*agentsdk.Client
|
||||
var agentClients []agentproto.DRPCAgentClient
|
||||
for i, agent := range []database.WorkspaceAgent{agent1, agent2} {
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(agent.AuthToken.String())
|
||||
agentClient.SDK.SetLogger(logger.Leveled(slog.LevelDebug).Named(fmt.Sprintf("agent%d", i+1)))
|
||||
agentClients = append(agentClients, agentClient)
|
||||
conn, err := agentClient.ConnectRPC(context.Background())
|
||||
require.NoError(t, err)
|
||||
agentAPI := agentproto.NewDRPCAgentClient(conn)
|
||||
agentClients = append(agentClients, agentAPI)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for a := range agentClients {
|
||||
err := agentClients[a].DRPCConn().Close()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Fake app stats
|
||||
_, err = agentClients[0].PostStats(context.Background(), &agentsdk.Stats{
|
||||
// ConnectionCount must be positive as database query ignores stats with no active connections at the time frame
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
ConnectionCount: 1,
|
||||
ConnectionMedianLatencyMS: 15,
|
||||
// Session counts must be positive, but the exact value is ignored.
|
||||
// Database query approximates it to 60s of usage.
|
||||
SessionCountSSH: 99,
|
||||
SessionCountJetBrains: 47,
|
||||
SessionCountVSCode: 34,
|
||||
_, err = agentClients[0].UpdateStats(context.Background(), &agentproto.UpdateStatsRequest{
|
||||
Stats: &agentproto.Stats{
|
||||
// ConnectionCount must be positive as database query ignores stats with no active connections at the time frame
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
ConnectionCount: 1,
|
||||
ConnectionMedianLatencyMs: 15,
|
||||
// Session counts must be positive, but the exact value is ignored.
|
||||
// Database query approximates it to 60s of usage.
|
||||
SessionCountSsh: 99,
|
||||
SessionCountJetbrains: 47,
|
||||
SessionCountVscode: 34,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "unable to post fake stats")
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
|
||||
agentproto "github.com/coder/coder/v2/agent/proto"
|
||||
"github.com/coder/coder/v2/coderd/agentmetrics"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
@@ -415,36 +416,45 @@ func TestAgentStats(t *testing.T) {
|
||||
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
agent1 := prepareWorkspaceAndAgent(t, client, user, 1)
|
||||
agent2 := prepareWorkspaceAndAgent(t, client, user, 2)
|
||||
agent3 := prepareWorkspaceAndAgent(t, client, user, 3)
|
||||
agent1 := prepareWorkspaceAndAgent(ctx, t, client, user, 1)
|
||||
agent2 := prepareWorkspaceAndAgent(ctx, t, client, user, 2)
|
||||
agent3 := prepareWorkspaceAndAgent(ctx, t, client, user, 3)
|
||||
defer agent1.DRPCConn().Close()
|
||||
defer agent2.DRPCConn().Close()
|
||||
defer agent3.DRPCConn().Close()
|
||||
|
||||
registry := prometheus.NewRegistry()
|
||||
|
||||
// given
|
||||
var i int64
|
||||
for i = 0; i < 3; i++ {
|
||||
_, err = agent1.PostStats(ctx, &agentsdk.Stats{
|
||||
TxBytes: 1 + i, RxBytes: 2 + i,
|
||||
SessionCountVSCode: 3 + i, SessionCountJetBrains: 4 + i, SessionCountReconnectingPTY: 5 + i, SessionCountSSH: 6 + i,
|
||||
ConnectionCount: 7 + i, ConnectionMedianLatencyMS: 8000,
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
_, err = agent1.UpdateStats(ctx, &agentproto.UpdateStatsRequest{
|
||||
Stats: &agentproto.Stats{
|
||||
TxBytes: 1 + i, RxBytes: 2 + i,
|
||||
SessionCountVscode: 3 + i, SessionCountJetbrains: 4 + i, SessionCountReconnectingPty: 5 + i, SessionCountSsh: 6 + i,
|
||||
ConnectionCount: 7 + i, ConnectionMedianLatencyMs: 8000,
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = agent2.PostStats(ctx, &agentsdk.Stats{
|
||||
TxBytes: 2 + i, RxBytes: 4 + i,
|
||||
SessionCountVSCode: 6 + i, SessionCountJetBrains: 8 + i, SessionCountReconnectingPTY: 10 + i, SessionCountSSH: 12 + i,
|
||||
ConnectionCount: 8 + i, ConnectionMedianLatencyMS: 10000,
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
_, err = agent2.UpdateStats(ctx, &agentproto.UpdateStatsRequest{
|
||||
Stats: &agentproto.Stats{
|
||||
TxBytes: 2 + i, RxBytes: 4 + i,
|
||||
SessionCountVscode: 6 + i, SessionCountJetbrains: 8 + i, SessionCountReconnectingPty: 10 + i, SessionCountSsh: 12 + i,
|
||||
ConnectionCount: 8 + i, ConnectionMedianLatencyMs: 10000,
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = agent3.PostStats(ctx, &agentsdk.Stats{
|
||||
TxBytes: 3 + i, RxBytes: 6 + i,
|
||||
SessionCountVSCode: 12 + i, SessionCountJetBrains: 14 + i, SessionCountReconnectingPTY: 16 + i, SessionCountSSH: 18 + i,
|
||||
ConnectionCount: 9 + i, ConnectionMedianLatencyMS: 12000,
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
_, err = agent3.UpdateStats(ctx, &agentproto.UpdateStatsRequest{
|
||||
Stats: &agentproto.Stats{
|
||||
TxBytes: 3 + i, RxBytes: 6 + i,
|
||||
SessionCountVscode: 12 + i, SessionCountJetbrains: 14 + i, SessionCountReconnectingPty: 16 + i, SessionCountSsh: 18 + i,
|
||||
ConnectionCount: 9 + i, ConnectionMedianLatencyMs: 12000,
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -596,7 +606,7 @@ func TestExperimentsMetric(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func prepareWorkspaceAndAgent(t *testing.T, client *codersdk.Client, user codersdk.CreateFirstUserResponse, workspaceNum int) *agentsdk.Client {
|
||||
func prepareWorkspaceAndAgent(ctx context.Context, t *testing.T, client *codersdk.Client, user codersdk.CreateFirstUserResponse, workspaceNum int) agentproto.DRPCAgentClient {
|
||||
authToken := uuid.NewString()
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
@@ -611,9 +621,12 @@ func prepareWorkspaceAndAgent(t *testing.T, client *codersdk.Client, user coders
|
||||
})
|
||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(authToken)
|
||||
return agentClient
|
||||
ac := agentsdk.New(client.URL)
|
||||
ac.SetSessionToken(authToken)
|
||||
conn, err := ac.ConnectRPC(ctx)
|
||||
require.NoError(t, err)
|
||||
agentAPI := agentproto.NewDRPCAgentClient(conn)
|
||||
return agentAPI
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -18,14 +18,12 @@ import (
|
||||
"github.com/sqlc-dev/pqtype"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/mod/semver"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/xerrors"
|
||||
"nhooyr.io/websocket"
|
||||
"tailscale.com/tailcfg"
|
||||
|
||||
"cdr.dev/slog"
|
||||
agentproto "github.com/coder/coder/v2/agent/proto"
|
||||
"github.com/coder/coder/v2/coderd/agentapi"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||
@@ -136,144 +134,8 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(ctx, rw, http.StatusOK, apiAgent)
|
||||
}
|
||||
|
||||
// @Summary Get authorized workspace agent manifest
|
||||
// @ID get-authorized-workspace-agent-manifest
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Success 200 {object} agentsdk.Manifest
|
||||
// @Router /workspaceagents/me/manifest [get]
|
||||
func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
|
||||
// As this API becomes deprecated, use the new protobuf API and convert the
|
||||
// types back to the SDK types.
|
||||
manifestAPI := &agentapi.ManifestAPI{
|
||||
AccessURL: api.AccessURL,
|
||||
AppHostname: api.AppHostname,
|
||||
ExternalAuthConfigs: api.ExternalAuthConfigs,
|
||||
DisableDirectConnections: api.DeploymentValues.DERP.Config.BlockDirect.Value(),
|
||||
DerpForceWebSockets: api.DeploymentValues.DERP.Config.ForceWebSockets.Value(),
|
||||
|
||||
AgentFn: func(_ context.Context) (database.WorkspaceAgent, error) { return workspaceAgent, nil },
|
||||
WorkspaceIDFn: func(ctx context.Context, wa *database.WorkspaceAgent) (uuid.UUID, error) {
|
||||
// Sadly this results in a double query, but it's only temporary for
|
||||
// now.
|
||||
ws, err := api.Database.GetWorkspaceByAgentID(ctx, wa.ID)
|
||||
if err != nil {
|
||||
return uuid.Nil, err
|
||||
}
|
||||
return ws.Workspace.ID, nil
|
||||
},
|
||||
Database: api.Database,
|
||||
DerpMapFn: api.DERPMap,
|
||||
}
|
||||
manifest, err := manifestAPI.GetManifest(ctx, &agentproto.GetManifestRequest{})
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching workspace agent manifest.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
sdkManifest, err := agentsdk.ManifestFromProto(manifest)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error converting manifest.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, sdkManifest)
|
||||
}
|
||||
|
||||
const AgentAPIVersionREST = "1.0"
|
||||
|
||||
// @Summary Submit workspace agent startup
|
||||
// @ID submit-workspace-agent-startup
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PostStartupRequest true "Startup request"
|
||||
// @Success 200
|
||||
// @Router /workspaceagents/me/startup [post]
|
||||
// @x-apidocgen {"skip": true}
|
||||
func (api *API) postWorkspaceAgentStartup(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
apiAgent, err := db2sdk.WorkspaceAgent(
|
||||
api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, nil, api.AgentInactiveDisconnectTimeout,
|
||||
api.DeploymentValues.AgentFallbackTroubleshootingURL.String(),
|
||||
)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error reading workspace agent.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req agentsdk.PostStartupRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
api.Logger.Debug(
|
||||
ctx,
|
||||
"post workspace agent version",
|
||||
slog.F("agent_id", apiAgent.ID),
|
||||
slog.F("agent_version", req.Version),
|
||||
slog.F("remote_addr", r.RemoteAddr),
|
||||
)
|
||||
|
||||
if !semver.IsValid(req.Version) {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Invalid workspace agent version provided.",
|
||||
Detail: fmt.Sprintf("invalid semver version: %q", req.Version),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate subsystems.
|
||||
seen := make(map[codersdk.AgentSubsystem]bool)
|
||||
for _, s := range req.Subsystems {
|
||||
if !s.Valid() {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Invalid workspace agent subsystem provided.",
|
||||
Detail: fmt.Sprintf("invalid subsystem: %q", s),
|
||||
})
|
||||
return
|
||||
}
|
||||
if seen[s] {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Invalid workspace agent subsystem provided.",
|
||||
Detail: fmt.Sprintf("duplicate subsystem: %q", s),
|
||||
})
|
||||
return
|
||||
}
|
||||
seen[s] = true
|
||||
}
|
||||
|
||||
if err := api.Database.UpdateWorkspaceAgentStartupByID(ctx, database.UpdateWorkspaceAgentStartupByIDParams{
|
||||
ID: apiAgent.ID,
|
||||
Version: req.Version,
|
||||
ExpandedDirectory: req.ExpandedDirectory,
|
||||
Subsystems: convertWorkspaceAgentSubsystems(req.Subsystems),
|
||||
APIVersion: AgentAPIVersionREST,
|
||||
}); err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Error setting agent version",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
// @Summary Patch workspace agent logs
|
||||
// @ID patch-workspace-agent-logs
|
||||
// @Security CoderSessionToken
|
||||
@@ -938,79 +800,6 @@ func (api *API) derpMapUpdates(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// @Summary Coordinate workspace agent via Tailnet
|
||||
// @Description It accepts a WebSocket connection to an agent that listens to
|
||||
// @Description incoming connections and publishes node updates.
|
||||
// @ID coordinate-workspace-agent-via-tailnet
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Agents
|
||||
// @Success 101
|
||||
// @Router /workspaceagents/me/coordinate [get]
|
||||
func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
api.WebsocketWaitMutex.Lock()
|
||||
api.WebsocketWaitGroup.Add(1)
|
||||
api.WebsocketWaitMutex.Unlock()
|
||||
defer api.WebsocketWaitGroup.Done()
|
||||
// The middleware only accept agents for resources on the latest build.
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
build := httpmw.LatestBuild(r)
|
||||
|
||||
workspace, err := api.Database.GetWorkspaceByID(ctx, build.WorkspaceID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Internal error fetching workspace.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
owner, err := api.Database.GetUserByID(ctx, workspace.OwnerID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Internal error fetching user.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := websocket.Accept(rw, r, nil)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Failed to accept websocket.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx, wsNetConn := codersdk.WebsocketNetConn(ctx, conn, websocket.MessageBinary)
|
||||
defer wsNetConn.Close()
|
||||
|
||||
closeCtx, closeCtxCancel := context.WithCancel(ctx)
|
||||
defer closeCtxCancel()
|
||||
monitor := api.startAgentWebsocketMonitor(closeCtx, workspaceAgent, build, conn)
|
||||
defer monitor.close()
|
||||
|
||||
api.Logger.Debug(ctx, "accepting agent",
|
||||
slog.F("owner", owner.Username),
|
||||
slog.F("workspace", workspace.Name),
|
||||
slog.F("name", workspaceAgent.Name),
|
||||
)
|
||||
api.Logger.Debug(ctx, "accepting agent details", slog.F("agent", workspaceAgent))
|
||||
|
||||
defer conn.Close(websocket.StatusNormalClosure, "")
|
||||
|
||||
err = (*api.TailnetCoordinator.Load()).ServeAgent(wsNetConn, workspaceAgent.ID,
|
||||
fmt.Sprintf("%s-%s-%s", owner.Username, workspace.Name, workspaceAgent.Name),
|
||||
)
|
||||
if err != nil {
|
||||
api.Logger.Warn(ctx, "tailnet coordinator agent error", slog.Error(err))
|
||||
_ = conn.Close(websocket.StatusInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// workspaceAgentClientCoordinate accepts a WebSocket that reads node network updates.
|
||||
// After accept a PubSub starts listening for new connection node updates
|
||||
// which are written to the WebSocket.
|
||||
@@ -1171,214 +960,6 @@ func convertScripts(dbScripts []database.WorkspaceAgentScript) []codersdk.Worksp
|
||||
return scripts
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent stats
|
||||
// @ID submit-workspace-agent-stats
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.Stats true "Stats request"
|
||||
// @Success 200 {object} agentsdk.StatsResponse
|
||||
// @Router /workspaceagents/me/report-stats [post]
|
||||
// @Deprecated Uses agent API v2 endpoint instead.
|
||||
func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
row, err := api.Database.GetWorkspaceByAgentID(ctx, workspaceAgent.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Failed to get workspace.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
workspace := row.Workspace
|
||||
|
||||
var req agentsdk.Stats
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
// An empty stat means it's just looking for the report interval.
|
||||
if req.ConnectionsByProto == nil {
|
||||
httpapi.Write(ctx, rw, http.StatusOK, agentsdk.StatsResponse{
|
||||
ReportInterval: api.AgentStatsRefreshInterval,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
api.Logger.Debug(ctx, "read stats report",
|
||||
slog.F("interval", api.AgentStatsRefreshInterval),
|
||||
slog.F("workspace_agent_id", workspaceAgent.ID),
|
||||
slog.F("workspace_id", workspace.ID),
|
||||
slog.F("payload", req),
|
||||
)
|
||||
|
||||
protoStats := &agentproto.Stats{
|
||||
ConnectionsByProto: req.ConnectionsByProto,
|
||||
ConnectionCount: req.ConnectionCount,
|
||||
ConnectionMedianLatencyMs: req.ConnectionMedianLatencyMS,
|
||||
RxPackets: req.RxPackets,
|
||||
RxBytes: req.RxBytes,
|
||||
TxPackets: req.TxPackets,
|
||||
TxBytes: req.TxBytes,
|
||||
SessionCountVscode: req.SessionCountVSCode,
|
||||
SessionCountJetbrains: req.SessionCountJetBrains,
|
||||
SessionCountReconnectingPty: req.SessionCountReconnectingPTY,
|
||||
SessionCountSsh: req.SessionCountSSH,
|
||||
Metrics: make([]*agentproto.Stats_Metric, len(req.Metrics)),
|
||||
}
|
||||
for i, metric := range req.Metrics {
|
||||
metricType := agentproto.Stats_Metric_TYPE_UNSPECIFIED
|
||||
switch metric.Type {
|
||||
case agentsdk.AgentMetricTypeCounter:
|
||||
metricType = agentproto.Stats_Metric_COUNTER
|
||||
case agentsdk.AgentMetricTypeGauge:
|
||||
metricType = agentproto.Stats_Metric_GAUGE
|
||||
}
|
||||
|
||||
protoStats.Metrics[i] = &agentproto.Stats_Metric{
|
||||
Name: metric.Name,
|
||||
Type: metricType,
|
||||
Value: metric.Value,
|
||||
Labels: make([]*agentproto.Stats_Metric_Label, len(metric.Labels)),
|
||||
}
|
||||
for j, label := range metric.Labels {
|
||||
protoStats.Metrics[i].Labels[j] = &agentproto.Stats_Metric_Label{
|
||||
Name: label.Name,
|
||||
Value: label.Value,
|
||||
}
|
||||
}
|
||||
}
|
||||
err = api.statsReporter.ReportAgentStats(
|
||||
ctx,
|
||||
dbtime.Now(),
|
||||
workspace,
|
||||
workspaceAgent,
|
||||
row.TemplateName,
|
||||
protoStats,
|
||||
)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, agentsdk.StatsResponse{
|
||||
ReportInterval: api.AgentStatsRefreshInterval,
|
||||
})
|
||||
}
|
||||
|
||||
func ellipse(v string, n int) string {
|
||||
if len(v) > n {
|
||||
return v[:n] + "..."
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent metadata
|
||||
// @ID submit-workspace-agent-metadata
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Tags Agents
|
||||
// @Param request body []agentsdk.PostMetadataRequest true "Workspace agent metadata request"
|
||||
// @Success 204 "Success"
|
||||
// @Router /workspaceagents/me/metadata [post]
|
||||
// @x-apidocgen {"skip": true}
|
||||
func (api *API) workspaceAgentPostMetadata(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var req agentsdk.PostMetadataRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
|
||||
// Split into function to allow call by deprecated handler.
|
||||
err := api.workspaceAgentUpdateMetadata(ctx, workspaceAgent, req)
|
||||
if err != nil {
|
||||
api.Logger.Error(ctx, "failed to handle metadata request", slog.Error(err))
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (api *API) workspaceAgentUpdateMetadata(ctx context.Context, workspaceAgent database.WorkspaceAgent, req agentsdk.PostMetadataRequest) error {
|
||||
const (
|
||||
// maxValueLen is set to 2048 to stay under the 8000 byte Postgres
|
||||
// NOTIFY limit. Since both value and error can be set, the real
|
||||
// payload limit is 2 * 2048 * 4/3 <base64 expansion> = 5461 bytes + a few hundred bytes for JSON
|
||||
// syntax, key names, and metadata.
|
||||
maxValueLen = 2048
|
||||
maxErrorLen = maxValueLen
|
||||
)
|
||||
|
||||
collectedAt := time.Now()
|
||||
|
||||
datum := database.UpdateWorkspaceAgentMetadataParams{
|
||||
WorkspaceAgentID: workspaceAgent.ID,
|
||||
Key: make([]string, 0, len(req.Metadata)),
|
||||
Value: make([]string, 0, len(req.Metadata)),
|
||||
Error: make([]string, 0, len(req.Metadata)),
|
||||
CollectedAt: make([]time.Time, 0, len(req.Metadata)),
|
||||
}
|
||||
|
||||
for _, md := range req.Metadata {
|
||||
metadataError := md.Error
|
||||
|
||||
// We overwrite the error if the provided payload is too long.
|
||||
if len(md.Value) > maxValueLen {
|
||||
metadataError = fmt.Sprintf("value of %d bytes exceeded %d bytes", len(md.Value), maxValueLen)
|
||||
md.Value = md.Value[:maxValueLen]
|
||||
}
|
||||
|
||||
if len(md.Error) > maxErrorLen {
|
||||
metadataError = fmt.Sprintf("error of %d bytes exceeded %d bytes", len(md.Error), maxErrorLen)
|
||||
md.Error = md.Error[:maxErrorLen]
|
||||
}
|
||||
|
||||
// We don't want a misconfigured agent to fill the database.
|
||||
datum.Key = append(datum.Key, md.Key)
|
||||
datum.Value = append(datum.Value, md.Value)
|
||||
datum.Error = append(datum.Error, metadataError)
|
||||
// We ignore the CollectedAt from the agent to avoid bugs caused by
|
||||
// clock skew.
|
||||
datum.CollectedAt = append(datum.CollectedAt, collectedAt)
|
||||
|
||||
api.Logger.Debug(
|
||||
ctx, "accepted metadata report",
|
||||
slog.F("workspace_agent_id", workspaceAgent.ID),
|
||||
slog.F("collected_at", collectedAt),
|
||||
slog.F("original_collected_at", md.CollectedAt),
|
||||
slog.F("key", md.Key),
|
||||
slog.F("value", ellipse(md.Value, 16)),
|
||||
)
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(agentapi.WorkspaceAgentMetadataChannelPayload{
|
||||
CollectedAt: collectedAt,
|
||||
Keys: datum.Key,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = api.Database.UpdateWorkspaceAgentMetadata(ctx, datum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = api.Pubsub.Publish(agentapi.WatchWorkspaceAgentMetadataChannel(workspaceAgent.ID), payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// @Summary Watch for workspace agent metadata updates
|
||||
// @ID watch-for-workspace-agent-metadata-updates
|
||||
// @Security CoderSessionToken
|
||||
@@ -1612,211 +1193,6 @@ func convertWorkspaceAgentMetadata(db []database.WorkspaceAgentMetadatum) []code
|
||||
return result
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent lifecycle state
|
||||
// @ID submit-workspace-agent-lifecycle-state
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PostLifecycleRequest true "Workspace agent lifecycle request"
|
||||
// @Success 204 "Success"
|
||||
// @Router /workspaceagents/me/report-lifecycle [post]
|
||||
// @x-apidocgen {"skip": true}
|
||||
func (api *API) workspaceAgentReportLifecycle(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
row, err := api.Database.GetWorkspaceByAgentID(ctx, workspaceAgent.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Failed to get workspace.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
workspace := row.Workspace
|
||||
|
||||
var req agentsdk.PostLifecycleRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
logger := api.Logger.With(
|
||||
slog.F("workspace_agent_id", workspaceAgent.ID),
|
||||
slog.F("workspace_id", workspace.ID),
|
||||
slog.F("payload", req),
|
||||
)
|
||||
logger.Debug(ctx, "workspace agent state report")
|
||||
|
||||
lifecycleState := req.State
|
||||
dbLifecycleState := database.WorkspaceAgentLifecycleState(lifecycleState)
|
||||
if !dbLifecycleState.Valid() {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Invalid lifecycle state.",
|
||||
Detail: fmt.Sprintf("Invalid lifecycle state %q, must be be one of %q.", lifecycleState, database.AllWorkspaceAgentLifecycleStateValues()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.ChangedAt.IsZero() {
|
||||
// Backwards compatibility with older agents.
|
||||
req.ChangedAt = dbtime.Now()
|
||||
}
|
||||
changedAt := sql.NullTime{Time: req.ChangedAt, Valid: true}
|
||||
|
||||
startedAt := workspaceAgent.StartedAt
|
||||
readyAt := workspaceAgent.ReadyAt
|
||||
switch lifecycleState {
|
||||
case codersdk.WorkspaceAgentLifecycleStarting:
|
||||
startedAt = changedAt
|
||||
readyAt.Valid = false // This agent is re-starting, so it's not ready yet.
|
||||
case codersdk.WorkspaceAgentLifecycleReady, codersdk.WorkspaceAgentLifecycleStartError:
|
||||
readyAt = changedAt
|
||||
}
|
||||
|
||||
err = api.Database.UpdateWorkspaceAgentLifecycleStateByID(ctx, database.UpdateWorkspaceAgentLifecycleStateByIDParams{
|
||||
ID: workspaceAgent.ID,
|
||||
LifecycleState: dbLifecycleState,
|
||||
StartedAt: startedAt,
|
||||
ReadyAt: readyAt,
|
||||
})
|
||||
if err != nil {
|
||||
if !xerrors.Is(err, context.Canceled) {
|
||||
// not an error if we are canceled
|
||||
logger.Error(ctx, "failed to update lifecycle state", slog.Error(err))
|
||||
}
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.publishWorkspaceUpdate(ctx, workspace.ID)
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent application health
|
||||
// @ID submit-workspace-agent-application-health
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PostAppHealthsRequest true "Application health request"
|
||||
// @Success 200
|
||||
// @Router /workspaceagents/me/app-health [post]
|
||||
func (api *API) postWorkspaceAppHealth(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
var req agentsdk.PostAppHealthsRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Healths == nil || len(req.Healths) == 0 {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Health field is empty",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
apps, err := api.Database.GetWorkspaceAppsByAgentID(ctx, workspaceAgent.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Error getting agent apps",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var newApps []database.WorkspaceApp
|
||||
for id, newHealth := range req.Healths {
|
||||
old := func() *database.WorkspaceApp {
|
||||
for _, app := range apps {
|
||||
if app.ID == id {
|
||||
return &app
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
if old == nil {
|
||||
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
|
||||
Message: "Error setting workspace app health",
|
||||
Detail: xerrors.Errorf("workspace app name %s not found", id).Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if old.HealthcheckUrl == "" {
|
||||
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
|
||||
Message: "Error setting workspace app health",
|
||||
Detail: xerrors.Errorf("health checking is disabled for workspace app %s", id).Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
switch newHealth {
|
||||
case codersdk.WorkspaceAppHealthInitializing:
|
||||
case codersdk.WorkspaceAppHealthHealthy:
|
||||
case codersdk.WorkspaceAppHealthUnhealthy:
|
||||
default:
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Error setting workspace app health",
|
||||
Detail: xerrors.Errorf("workspace app health %s is not a valid value", newHealth).Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// don't save if the value hasn't changed
|
||||
if old.Health == database.WorkspaceAppHealth(newHealth) {
|
||||
continue
|
||||
}
|
||||
old.Health = database.WorkspaceAppHealth(newHealth)
|
||||
|
||||
newApps = append(newApps, *old)
|
||||
}
|
||||
|
||||
for _, app := range newApps {
|
||||
err = api.Database.UpdateWorkspaceAppHealthByID(ctx, database.UpdateWorkspaceAppHealthByIDParams{
|
||||
ID: app.ID,
|
||||
Health: app.Health,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Error setting workspace app health",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resource, err := api.Database.GetWorkspaceResourceByID(ctx, workspaceAgent.ResourceID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching workspace resource.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
job, err := api.Database.GetWorkspaceBuildByJobID(ctx, resource.JobID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching workspace build.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
workspace, err := api.Database.GetWorkspaceByID(ctx, job.WorkspaceID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching workspace.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
api.publishWorkspaceUpdate(ctx, workspace.ID)
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
// workspaceAgentsExternalAuth returns an access token for a given URL
|
||||
// or finds a provider by ID.
|
||||
//
|
||||
@@ -2117,24 +1493,3 @@ func convertWorkspaceAgentLog(logEntry database.WorkspaceAgentLog) codersdk.Work
|
||||
SourceID: logEntry.LogSourceID,
|
||||
}
|
||||
}
|
||||
|
||||
func convertWorkspaceAgentSubsystems(ss []codersdk.AgentSubsystem) []database.WorkspaceAgentSubsystem {
|
||||
out := make([]database.WorkspaceAgentSubsystem, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
switch s {
|
||||
case codersdk.AgentSubsystemEnvbox:
|
||||
out = append(out, database.WorkspaceAgentSubsystemEnvbox)
|
||||
case codersdk.AgentSubsystemEnvbuilder:
|
||||
out = append(out, database.WorkspaceAgentSubsystemEnvbuilder)
|
||||
case codersdk.AgentSubsystemExectrace:
|
||||
out = append(out, database.WorkspaceAgentSubsystemExectrace)
|
||||
default:
|
||||
// Invalid, drop it.
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
return out[i] < out[j]
|
||||
})
|
||||
return out
|
||||
}
|
||||
|
||||
+27
-116
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"tailscale.com/tailcfg"
|
||||
|
||||
"cdr.dev/slog"
|
||||
@@ -34,7 +35,6 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||
"github.com/coder/coder/v2/coderd/externalauth"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
||||
@@ -963,110 +963,12 @@ func TestWorkspaceAgentPostLogSource(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestWorkspaceAgentReportStats tests the legacy (agent API v1) report stats endpoint.
|
||||
func TestWorkspaceAgentReportStats(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, db := coderdtest.NewWithDatabase(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
r := dbfake.WorkspaceBuild(t, db, database.Workspace{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
}).WithAgent().Do()
|
||||
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(r.AgentToken)
|
||||
|
||||
_, err := agentClient.PostStats(context.Background(), &agentsdk.Stats{
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
ConnectionCount: 1,
|
||||
RxPackets: 1,
|
||||
RxBytes: 1,
|
||||
TxPackets: 1,
|
||||
TxBytes: 1,
|
||||
SessionCountVSCode: 1,
|
||||
SessionCountJetBrains: 0,
|
||||
SessionCountReconnectingPTY: 0,
|
||||
SessionCountSSH: 0,
|
||||
ConnectionMedianLatencyMS: 10,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
newWorkspace, err := client.Workspace(context.Background(), r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t,
|
||||
newWorkspace.LastUsedAt.After(r.Workspace.LastUsedAt),
|
||||
"%s is not after %s", newWorkspace.LastUsedAt, r.Workspace.LastUsedAt,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("FailDeleted", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner, db := coderdtest.NewWithDatabase(t, nil)
|
||||
ownerUser := coderdtest.CreateFirstUser(t, owner)
|
||||
client, admin := coderdtest.CreateAnotherUser(t, owner, ownerUser.OrganizationID, rbac.RoleTemplateAdmin(), rbac.RoleUserAdmin())
|
||||
r := dbfake.WorkspaceBuild(t, db, database.Workspace{
|
||||
OrganizationID: admin.OrganizationIDs[0],
|
||||
OwnerID: admin.ID,
|
||||
}).WithAgent().Do()
|
||||
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(r.AgentToken)
|
||||
|
||||
_, err := agentClient.PostStats(context.Background(), &agentsdk.Stats{
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
ConnectionCount: 1,
|
||||
RxPackets: 1,
|
||||
RxBytes: 1,
|
||||
TxPackets: 1,
|
||||
TxBytes: 1,
|
||||
SessionCountVSCode: 0,
|
||||
SessionCountJetBrains: 0,
|
||||
SessionCountReconnectingPTY: 0,
|
||||
SessionCountSSH: 0,
|
||||
ConnectionMedianLatencyMS: 10,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
newWorkspace, err := client.Workspace(context.Background(), r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// nolint:gocritic // using db directly over creating a delete job
|
||||
err = db.UpdateWorkspaceDeletedByID(dbauthz.As(context.Background(),
|
||||
coderdtest.AuthzUserSubject(admin, ownerUser.OrganizationID)),
|
||||
database.UpdateWorkspaceDeletedByIDParams{
|
||||
ID: newWorkspace.ID,
|
||||
Deleted: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = agentClient.PostStats(context.Background(), &agentsdk.Stats{
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
ConnectionCount: 1,
|
||||
RxPackets: 1,
|
||||
RxBytes: 1,
|
||||
TxPackets: 1,
|
||||
TxBytes: 1,
|
||||
SessionCountVSCode: 1,
|
||||
SessionCountJetBrains: 0,
|
||||
SessionCountReconnectingPTY: 0,
|
||||
SessionCountSSH: 0,
|
||||
ConnectionMedianLatencyMS: 10,
|
||||
})
|
||||
require.ErrorContains(t, err, "agent is invalid")
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceAgent_LifecycleState(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Set", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
client, db := coderdtest.NewWithDatabase(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
@@ -1082,8 +984,15 @@ func TestWorkspaceAgent_LifecycleState(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(r.AgentToken)
|
||||
ac := agentsdk.New(client.URL)
|
||||
ac.SetSessionToken(r.AgentToken)
|
||||
conn, err := ac.ConnectRPC(ctx)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
cErr := conn.Close()
|
||||
require.NoError(t, cErr)
|
||||
}()
|
||||
agentAPI := agentproto.NewDRPCAgentClient(conn)
|
||||
|
||||
tests := []struct {
|
||||
state codersdk.WorkspaceAgentLifecycle
|
||||
@@ -1105,16 +1014,17 @@ func TestWorkspaceAgent_LifecycleState(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(string(tt.state), func(t *testing.T) {
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
err := agentClient.PostLifecycle(ctx, agentsdk.PostLifecycleRequest{
|
||||
State: tt.state,
|
||||
ChangedAt: time.Now(),
|
||||
})
|
||||
state, err := agentsdk.ProtoFromLifecycleState(tt.state)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
_, err = agentAPI.UpdateLifecycle(ctx, &agentproto.UpdateLifecycleRequest{
|
||||
Lifecycle: &agentproto.Lifecycle{
|
||||
State: state,
|
||||
ChangedAt: timestamppb.Now(),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "post lifecycle state %q", tt.state)
|
||||
|
||||
workspace, err = client.Workspace(ctx, workspace.ID)
|
||||
@@ -1197,11 +1107,11 @@ func TestWorkspaceAgent_Metadata(t *testing.T) {
|
||||
require.EqualValues(t, 3, manifest.Metadata[0].Timeout)
|
||||
|
||||
post := func(ctx context.Context, key string, mr codersdk.WorkspaceAgentMetadataResult) {
|
||||
err := agentClient.PostMetadata(ctx, agentsdk.PostMetadataRequest{
|
||||
Metadata: []agentsdk.Metadata{
|
||||
_, err := aAPI.BatchUpdateMetadata(ctx, &agentproto.BatchUpdateMetadataRequest{
|
||||
Metadata: []*agentproto.Metadata{
|
||||
{
|
||||
Key: key,
|
||||
WorkspaceAgentMetadataResult: mr,
|
||||
Key: key,
|
||||
Result: agentsdk.ProtoFromMetadataResult(mr),
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -1452,17 +1362,18 @@ func TestWorkspaceAgent_Metadata_CatchMemoryLeak(t *testing.T) {
|
||||
manifest := requireGetManifest(ctx, t, aAPI)
|
||||
|
||||
post := func(ctx context.Context, key, value string) error {
|
||||
return agentClient.PostMetadata(ctx, agentsdk.PostMetadataRequest{
|
||||
Metadata: []agentsdk.Metadata{
|
||||
_, err := aAPI.BatchUpdateMetadata(ctx, &agentproto.BatchUpdateMetadataRequest{
|
||||
Metadata: []*agentproto.Metadata{
|
||||
{
|
||||
Key: key,
|
||||
WorkspaceAgentMetadataResult: codersdk.WorkspaceAgentMetadataResult{
|
||||
Result: agentsdk.ProtoFromMetadataResult(codersdk.WorkspaceAgentMetadataResult{
|
||||
CollectedAt: time.Now(),
|
||||
Value: value,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
workspace, err = client.Workspace(ctx, workspace.ID)
|
||||
|
||||
@@ -164,31 +164,6 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) startAgentWebsocketMonitor(ctx context.Context,
|
||||
workspaceAgent database.WorkspaceAgent, workspaceBuild database.WorkspaceBuild,
|
||||
conn *websocket.Conn,
|
||||
) *agentConnectionMonitor {
|
||||
monitor := &agentConnectionMonitor{
|
||||
apiCtx: api.ctx,
|
||||
workspaceAgent: workspaceAgent,
|
||||
workspaceBuild: workspaceBuild,
|
||||
conn: conn,
|
||||
pingPeriod: api.AgentConnectionUpdateFrequency,
|
||||
db: api.Database,
|
||||
replicaID: api.ID,
|
||||
updater: api,
|
||||
disconnectTimeout: api.AgentInactiveDisconnectTimeout,
|
||||
logger: api.Logger.With(
|
||||
slog.F("workspace_id", workspaceBuild.WorkspaceID),
|
||||
slog.F("agent_id", workspaceAgent.ID),
|
||||
),
|
||||
}
|
||||
monitor.init()
|
||||
monitor.start(ctx)
|
||||
|
||||
return monitor
|
||||
}
|
||||
|
||||
type yamuxPingerCloser struct {
|
||||
mux *yamux.Session
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package coderd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
agentproto "github.com/coder/coder/v2/agent/proto"
|
||||
@@ -14,6 +16,52 @@ import (
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
// Ported to RPC API from coderd/workspaceagents_test.go
|
||||
func TestWorkspaceAgentReportStats(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, db := coderdtest.NewWithDatabase(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
r := dbfake.WorkspaceBuild(t, db, database.Workspace{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
}).WithAgent().Do()
|
||||
|
||||
ac := agentsdk.New(client.URL)
|
||||
ac.SetSessionToken(r.AgentToken)
|
||||
conn, err := ac.ConnectRPC(context.Background())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
agentAPI := agentproto.NewDRPCAgentClient(conn)
|
||||
|
||||
_, err = agentAPI.UpdateStats(context.Background(), &agentproto.UpdateStatsRequest{
|
||||
Stats: &agentproto.Stats{
|
||||
ConnectionsByProto: map[string]int64{"TCP": 1},
|
||||
ConnectionCount: 1,
|
||||
RxPackets: 1,
|
||||
RxBytes: 1,
|
||||
TxPackets: 1,
|
||||
TxBytes: 1,
|
||||
SessionCountVscode: 1,
|
||||
SessionCountJetbrains: 0,
|
||||
SessionCountReconnectingPty: 0,
|
||||
SessionCountSsh: 0,
|
||||
ConnectionMedianLatencyMs: 10,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
newWorkspace, err := client.Workspace(context.Background(), r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t,
|
||||
newWorkspace.LastUsedAt.After(r.Workspace.LastUsedAt),
|
||||
"%s is not after %s", newWorkspace.LastUsedAt, r.Workspace.LastUsedAt,
|
||||
)
|
||||
}
|
||||
|
||||
func TestAgentAPI_LargeManifest(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
@@ -84,23 +84,6 @@ type PostMetadataRequest struct {
|
||||
// performance.
|
||||
type PostMetadataRequestDeprecated = codersdk.WorkspaceAgentMetadataResult
|
||||
|
||||
// PostMetadata posts agent metadata to the Coder server.
|
||||
//
|
||||
// Deprecated: use BatchUpdateMetadata on the agent dRPC API instead
|
||||
func (c *Client) PostMetadata(ctx context.Context, req PostMetadataRequest) error {
|
||||
res, err := c.SDK.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/metadata", req)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("execute request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusNoContent {
|
||||
return codersdk.ReadBodyAsError(res)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Manifest struct {
|
||||
AgentID uuid.UUID `json:"agent_id"`
|
||||
AgentName string `json:"agent_name"`
|
||||
@@ -457,49 +440,11 @@ type StatsResponse struct {
|
||||
ReportInterval time.Duration `json:"report_interval"`
|
||||
}
|
||||
|
||||
// PostStats sends agent stats to the coder server
|
||||
//
|
||||
// Deprecated: uses agent API v1 endpoint
|
||||
func (c *Client) PostStats(ctx context.Context, stats *Stats) (StatsResponse, error) {
|
||||
res, err := c.SDK.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/report-stats", stats)
|
||||
if err != nil {
|
||||
return StatsResponse{}, xerrors.Errorf("send request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return StatsResponse{}, codersdk.ReadBodyAsError(res)
|
||||
}
|
||||
|
||||
var interval StatsResponse
|
||||
err = json.NewDecoder(res.Body).Decode(&interval)
|
||||
if err != nil {
|
||||
return StatsResponse{}, xerrors.Errorf("decode stats response: %w", err)
|
||||
}
|
||||
|
||||
return interval, nil
|
||||
}
|
||||
|
||||
type PostLifecycleRequest struct {
|
||||
State codersdk.WorkspaceAgentLifecycle `json:"state"`
|
||||
ChangedAt time.Time `json:"changed_at"`
|
||||
}
|
||||
|
||||
// PostLifecycle posts the agent's lifecycle to the Coder server.
|
||||
//
|
||||
// Deprecated: Use UpdateLifecycle on the dRPC API instead
|
||||
func (c *Client) PostLifecycle(ctx context.Context, req PostLifecycleRequest) error {
|
||||
res, err := c.SDK.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/report-lifecycle", req)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("agent state post request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusNoContent {
|
||||
return codersdk.ReadBodyAsError(res)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PostStartupRequest struct {
|
||||
Version string `json:"version"`
|
||||
ExpandedDirectory string `json:"expanded_directory"`
|
||||
|
||||
@@ -371,3 +371,11 @@ func LifecycleStateFromProto(s proto.Lifecycle_State) (codersdk.WorkspaceAgentLi
|
||||
}
|
||||
return codersdk.WorkspaceAgentLifecycle(strings.ToLower(caps)), nil
|
||||
}
|
||||
|
||||
func ProtoFromLifecycleState(s codersdk.WorkspaceAgentLifecycle) (proto.Lifecycle_State, error) {
|
||||
caps, ok := proto.Lifecycle_State_value[strings.ToUpper(string(s))]
|
||||
if !ok {
|
||||
return 0, xerrors.Errorf("unknown lifecycle state: %s", s)
|
||||
}
|
||||
return proto.Lifecycle_State(caps), nil
|
||||
}
|
||||
|
||||
Generated
-338
@@ -160,67 +160,6 @@ curl -X POST http://coder-server:8080/api/v2/workspaceagents/google-instance-ide
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Submit workspace agent application health
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/app-health \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`POST /workspaceagents/me/app-health`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"healths": {
|
||||
"property1": "disabled",
|
||||
"property2": "disabled"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
| ------ | ---- | -------------------------------------------------------------------------- | -------- | -------------------------- |
|
||||
| `body` | body | [agentsdk.PostAppHealthsRequest](schemas.md#agentsdkpostapphealthsrequest) | true | Application health request |
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ------ |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Coordinate workspace agent via Tailnet
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/coordinate \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`GET /workspaceagents/me/coordinate`
|
||||
|
||||
It accepts a WebSocket connection to an agent that listens to
|
||||
incoming connections and publishes node updates.
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------------------------ | ------------------- | ------ |
|
||||
| 101 | [Switching Protocols](https://tools.ietf.org/html/rfc7231#section-6.2.2) | Switching Protocols | |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get workspace agent external auth
|
||||
|
||||
### Code samples
|
||||
@@ -453,283 +392,6 @@ curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/logs \
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get authorized workspace agent manifest
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/manifest \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`GET /workspaceagents/me/manifest`
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "string",
|
||||
"agent_name": "string",
|
||||
"apps": [
|
||||
{
|
||||
"command": "string",
|
||||
"display_name": "string",
|
||||
"external": true,
|
||||
"health": "disabled",
|
||||
"healthcheck": {
|
||||
"interval": 0,
|
||||
"threshold": 0,
|
||||
"url": "string"
|
||||
},
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
}
|
||||
],
|
||||
"derp_force_websockets": true,
|
||||
"derpmap": {
|
||||
"homeParams": {
|
||||
"regionScore": {
|
||||
"property1": 0,
|
||||
"property2": 0
|
||||
}
|
||||
},
|
||||
"omitDefaultRegions": true,
|
||||
"regions": {
|
||||
"property1": {
|
||||
"avoid": true,
|
||||
"embeddedRelay": true,
|
||||
"nodes": [
|
||||
{
|
||||
"canPort80": true,
|
||||
"certName": "string",
|
||||
"derpport": 0,
|
||||
"forceHTTP": true,
|
||||
"hostName": "string",
|
||||
"insecureForTests": true,
|
||||
"ipv4": "string",
|
||||
"ipv6": "string",
|
||||
"name": "string",
|
||||
"regionID": 0,
|
||||
"stunonly": true,
|
||||
"stunport": 0,
|
||||
"stuntestIP": "string"
|
||||
}
|
||||
],
|
||||
"regionCode": "string",
|
||||
"regionID": 0,
|
||||
"regionName": "string"
|
||||
},
|
||||
"property2": {
|
||||
"avoid": true,
|
||||
"embeddedRelay": true,
|
||||
"nodes": [
|
||||
{
|
||||
"canPort80": true,
|
||||
"certName": "string",
|
||||
"derpport": 0,
|
||||
"forceHTTP": true,
|
||||
"hostName": "string",
|
||||
"insecureForTests": true,
|
||||
"ipv4": "string",
|
||||
"ipv6": "string",
|
||||
"name": "string",
|
||||
"regionID": 0,
|
||||
"stunonly": true,
|
||||
"stunport": 0,
|
||||
"stuntestIP": "string"
|
||||
}
|
||||
],
|
||||
"regionCode": "string",
|
||||
"regionID": 0,
|
||||
"regionName": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"directory": "string",
|
||||
"disable_direct_connections": true,
|
||||
"environment_variables": {
|
||||
"property1": "string",
|
||||
"property2": "string"
|
||||
},
|
||||
"git_auth_configs": 0,
|
||||
"metadata": [
|
||||
{
|
||||
"display_name": "string",
|
||||
"interval": 0,
|
||||
"key": "string",
|
||||
"script": "string",
|
||||
"timeout": 0
|
||||
}
|
||||
],
|
||||
"motd_file": "string",
|
||||
"owner_name": "string",
|
||||
"scripts": [
|
||||
{
|
||||
"cron": "string",
|
||||
"log_path": "string",
|
||||
"log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a",
|
||||
"run_on_start": true,
|
||||
"run_on_stop": true,
|
||||
"script": "string",
|
||||
"start_blocks_login": true,
|
||||
"timeout": 0
|
||||
}
|
||||
],
|
||||
"vscode_port_proxy_uri": "string",
|
||||
"workspace_id": "string",
|
||||
"workspace_name": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [agentsdk.Manifest](schemas.md#agentsdkmanifest) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Submit workspace agent stats
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/report-stats \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`POST /workspaceagents/me/report-stats`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"connection_count": 0,
|
||||
"connection_median_latency_ms": 0,
|
||||
"connections_by_proto": {
|
||||
"property1": 0,
|
||||
"property2": 0
|
||||
},
|
||||
"metrics": [
|
||||
{
|
||||
"labels": [
|
||||
{
|
||||
"name": "string",
|
||||
"value": "string"
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"type": "counter",
|
||||
"value": 0
|
||||
}
|
||||
],
|
||||
"rx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"session_count_jetbrains": 0,
|
||||
"session_count_reconnecting_pty": 0,
|
||||
"session_count_ssh": 0,
|
||||
"session_count_vscode": 0,
|
||||
"tx_bytes": 0,
|
||||
"tx_packets": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
| ------ | ---- | ------------------------------------------ | -------- | ------------- |
|
||||
| `body` | body | [agentsdk.Stats](schemas.md#agentsdkstats) | true | Stats request |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"report_interval": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------- |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [agentsdk.StatsResponse](schemas.md#agentsdkstatsresponse) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Removed: Patch workspace agent logs
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/startup-logs \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`PATCH /workspaceagents/me/startup-logs`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"log_source_id": "string",
|
||||
"logs": [
|
||||
{
|
||||
"created_at": "string",
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
| ------ | ---- | -------------------------------------------------- | -------- | ----------- |
|
||||
| `body` | body | [agentsdk.PatchLogs](schemas.md#agentsdkpatchlogs) | true | logs |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "string",
|
||||
"message": "string",
|
||||
"validations": [
|
||||
{
|
||||
"detail": "string",
|
||||
"field": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get workspace agent by ID
|
||||
|
||||
### Code samples
|
||||
|
||||
Generated
-411
@@ -16,69 +16,6 @@
|
||||
| `document` | string | true | | |
|
||||
| `signature` | string | true | | |
|
||||
|
||||
## agentsdk.AgentMetric
|
||||
|
||||
```json
|
||||
{
|
||||
"labels": [
|
||||
{
|
||||
"name": "string",
|
||||
"value": "string"
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"type": "counter",
|
||||
"value": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------- | --------------------------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `labels` | array of [agentsdk.AgentMetricLabel](#agentsdkagentmetriclabel) | false | | |
|
||||
| `name` | string | true | | |
|
||||
| `type` | [agentsdk.AgentMetricType](#agentsdkagentmetrictype) | true | | |
|
||||
| `value` | number | true | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Property | Value |
|
||||
| -------- | --------- |
|
||||
| `type` | `counter` |
|
||||
| `type` | `gauge` |
|
||||
|
||||
## agentsdk.AgentMetricLabel
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string",
|
||||
"value": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------- | ------ | -------- | ------------ | ----------- |
|
||||
| `name` | string | true | | |
|
||||
| `value` | string | true | | |
|
||||
|
||||
## agentsdk.AgentMetricType
|
||||
|
||||
```json
|
||||
"counter"
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value |
|
||||
| --------- |
|
||||
| `counter` |
|
||||
| `gauge` |
|
||||
|
||||
## agentsdk.AuthenticateResponse
|
||||
|
||||
```json
|
||||
@@ -181,172 +118,6 @@
|
||||
| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | |
|
||||
| `output` | string | false | | |
|
||||
|
||||
## agentsdk.Manifest
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "string",
|
||||
"agent_name": "string",
|
||||
"apps": [
|
||||
{
|
||||
"command": "string",
|
||||
"display_name": "string",
|
||||
"external": true,
|
||||
"health": "disabled",
|
||||
"healthcheck": {
|
||||
"interval": 0,
|
||||
"threshold": 0,
|
||||
"url": "string"
|
||||
},
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
}
|
||||
],
|
||||
"derp_force_websockets": true,
|
||||
"derpmap": {
|
||||
"homeParams": {
|
||||
"regionScore": {
|
||||
"property1": 0,
|
||||
"property2": 0
|
||||
}
|
||||
},
|
||||
"omitDefaultRegions": true,
|
||||
"regions": {
|
||||
"property1": {
|
||||
"avoid": true,
|
||||
"embeddedRelay": true,
|
||||
"nodes": [
|
||||
{
|
||||
"canPort80": true,
|
||||
"certName": "string",
|
||||
"derpport": 0,
|
||||
"forceHTTP": true,
|
||||
"hostName": "string",
|
||||
"insecureForTests": true,
|
||||
"ipv4": "string",
|
||||
"ipv6": "string",
|
||||
"name": "string",
|
||||
"regionID": 0,
|
||||
"stunonly": true,
|
||||
"stunport": 0,
|
||||
"stuntestIP": "string"
|
||||
}
|
||||
],
|
||||
"regionCode": "string",
|
||||
"regionID": 0,
|
||||
"regionName": "string"
|
||||
},
|
||||
"property2": {
|
||||
"avoid": true,
|
||||
"embeddedRelay": true,
|
||||
"nodes": [
|
||||
{
|
||||
"canPort80": true,
|
||||
"certName": "string",
|
||||
"derpport": 0,
|
||||
"forceHTTP": true,
|
||||
"hostName": "string",
|
||||
"insecureForTests": true,
|
||||
"ipv4": "string",
|
||||
"ipv6": "string",
|
||||
"name": "string",
|
||||
"regionID": 0,
|
||||
"stunonly": true,
|
||||
"stunport": 0,
|
||||
"stuntestIP": "string"
|
||||
}
|
||||
],
|
||||
"regionCode": "string",
|
||||
"regionID": 0,
|
||||
"regionName": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"directory": "string",
|
||||
"disable_direct_connections": true,
|
||||
"environment_variables": {
|
||||
"property1": "string",
|
||||
"property2": "string"
|
||||
},
|
||||
"git_auth_configs": 0,
|
||||
"metadata": [
|
||||
{
|
||||
"display_name": "string",
|
||||
"interval": 0,
|
||||
"key": "string",
|
||||
"script": "string",
|
||||
"timeout": 0
|
||||
}
|
||||
],
|
||||
"motd_file": "string",
|
||||
"owner_name": "string",
|
||||
"scripts": [
|
||||
{
|
||||
"cron": "string",
|
||||
"log_path": "string",
|
||||
"log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a",
|
||||
"run_on_start": true,
|
||||
"run_on_stop": true,
|
||||
"script": "string",
|
||||
"start_blocks_login": true,
|
||||
"timeout": 0
|
||||
}
|
||||
],
|
||||
"vscode_port_proxy_uri": "string",
|
||||
"workspace_id": "string",
|
||||
"workspace_name": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `agent_id` | string | false | | |
|
||||
| `agent_name` | string | false | | |
|
||||
| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | |
|
||||
| `derp_force_websockets` | boolean | false | | |
|
||||
| `derpmap` | [tailcfg.DERPMap](#tailcfgderpmap) | false | | |
|
||||
| `directory` | string | false | | |
|
||||
| `disable_direct_connections` | boolean | false | | |
|
||||
| `environment_variables` | object | false | | |
|
||||
| » `[any property]` | string | false | | |
|
||||
| `git_auth_configs` | integer | false | | Git auth configs stores the number of Git configurations the Coder deployment has. If this number is >0, we set up special configuration in the workspace. |
|
||||
| `metadata` | array of [codersdk.WorkspaceAgentMetadataDescription](#codersdkworkspaceagentmetadatadescription) | false | | |
|
||||
| `motd_file` | string | false | | |
|
||||
| `owner_name` | string | false | | Owner name and WorkspaceID are used by an open-source user to identify the workspace. We do not provide insurance that this will not be removed in the future, but if it's easy to persist lets keep it around. |
|
||||
| `scripts` | array of [codersdk.WorkspaceAgentScript](#codersdkworkspaceagentscript) | false | | |
|
||||
| `vscode_port_proxy_uri` | string | false | | |
|
||||
| `workspace_id` | string | false | | |
|
||||
| `workspace_name` | string | false | | |
|
||||
|
||||
## agentsdk.Metadata
|
||||
|
||||
```json
|
||||
{
|
||||
"age": 0,
|
||||
"collected_at": "2019-08-24T14:15:22Z",
|
||||
"error": "string",
|
||||
"key": "string",
|
||||
"value": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `age` | integer | false | | Age is the number of seconds since the metadata was collected. It is provided in addition to CollectedAt to protect against clock skew. |
|
||||
| `collected_at` | string | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `key` | string | false | | |
|
||||
| `value` | string | false | | |
|
||||
|
||||
## agentsdk.PatchLogs
|
||||
|
||||
```json
|
||||
@@ -369,40 +140,6 @@
|
||||
| `log_source_id` | string | false | | |
|
||||
| `logs` | array of [agentsdk.Log](#agentsdklog) | false | | |
|
||||
|
||||
## agentsdk.PostAppHealthsRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"healths": {
|
||||
"property1": "disabled",
|
||||
"property2": "disabled"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------------ | ---------------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------- |
|
||||
| `healths` | object | false | | Healths is a map of the workspace app name and the health of the app. |
|
||||
| » `[any property]` | [codersdk.WorkspaceAppHealth](#codersdkworkspaceapphealth) | false | | |
|
||||
|
||||
## agentsdk.PostLifecycleRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"changed_at": "string",
|
||||
"state": "created"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | -------------------------------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `changed_at` | string | false | | |
|
||||
| `state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | |
|
||||
|
||||
## agentsdk.PostLogSourceRequest
|
||||
|
||||
```json
|
||||
@@ -421,132 +158,6 @@
|
||||
| `icon` | string | false | | |
|
||||
| `id` | string | false | | ID is a unique identifier for the log source. It is scoped to a workspace agent, and can be statically defined inside code to prevent duplicate sources from being created for the same agent. |
|
||||
|
||||
## agentsdk.PostMetadataRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": [
|
||||
{
|
||||
"age": 0,
|
||||
"collected_at": "2019-08-24T14:15:22Z",
|
||||
"error": "string",
|
||||
"key": "string",
|
||||
"value": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ---------- | ----------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `metadata` | array of [agentsdk.Metadata](#agentsdkmetadata) | false | | |
|
||||
|
||||
## agentsdk.PostMetadataRequestDeprecated
|
||||
|
||||
```json
|
||||
{
|
||||
"age": 0,
|
||||
"collected_at": "2019-08-24T14:15:22Z",
|
||||
"error": "string",
|
||||
"value": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `age` | integer | false | | Age is the number of seconds since the metadata was collected. It is provided in addition to CollectedAt to protect against clock skew. |
|
||||
| `collected_at` | string | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `value` | string | false | | |
|
||||
|
||||
## agentsdk.PostStartupRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"expanded_directory": "string",
|
||||
"subsystems": ["envbox"],
|
||||
"version": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------------- | ----------------------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `expanded_directory` | string | false | | |
|
||||
| `subsystems` | array of [codersdk.AgentSubsystem](#codersdkagentsubsystem) | false | | |
|
||||
| `version` | string | false | | |
|
||||
|
||||
## agentsdk.Stats
|
||||
|
||||
```json
|
||||
{
|
||||
"connection_count": 0,
|
||||
"connection_median_latency_ms": 0,
|
||||
"connections_by_proto": {
|
||||
"property1": 0,
|
||||
"property2": 0
|
||||
},
|
||||
"metrics": [
|
||||
{
|
||||
"labels": [
|
||||
{
|
||||
"name": "string",
|
||||
"value": "string"
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"type": "counter",
|
||||
"value": 0
|
||||
}
|
||||
],
|
||||
"rx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"session_count_jetbrains": 0,
|
||||
"session_count_reconnecting_pty": 0,
|
||||
"session_count_ssh": 0,
|
||||
"session_count_vscode": 0,
|
||||
"tx_bytes": 0,
|
||||
"tx_packets": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------------------------- | ----------------------------------------------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `connection_count` | integer | false | | Connection count is the number of connections received by an agent. |
|
||||
| `connection_median_latency_ms` | number | false | | Connection median latency ms is the median latency of all connections in milliseconds. |
|
||||
| `connections_by_proto` | object | false | | Connections by proto is a count of connections by protocol. |
|
||||
| » `[any property]` | integer | false | | |
|
||||
| `metrics` | array of [agentsdk.AgentMetric](#agentsdkagentmetric) | false | | Metrics collected by the agent |
|
||||
| `rx_bytes` | integer | false | | Rx bytes is the number of received bytes. |
|
||||
| `rx_packets` | integer | false | | Rx packets is the number of received packets. |
|
||||
| `session_count_jetbrains` | integer | false | | Session count jetbrains is the number of connections received by an agent that are from our JetBrains extension. |
|
||||
| `session_count_reconnecting_pty` | integer | false | | Session count reconnecting pty is the number of connections received by an agent that are from the reconnecting web terminal. |
|
||||
| `session_count_ssh` | integer | false | | Session count ssh is the number of connections received by an agent that are normal, non-tagged SSH sessions. |
|
||||
| `session_count_vscode` | integer | false | | Session count vscode is the number of connections received by an agent that are from our VS Code extension. |
|
||||
| `tx_bytes` | integer | false | | Tx bytes is the number of transmitted bytes. |
|
||||
| `tx_packets` | integer | false | | Tx packets is the number of transmitted bytes. |
|
||||
|
||||
## agentsdk.StatsResponse
|
||||
|
||||
```json
|
||||
{
|
||||
"report_interval": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ----------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------ |
|
||||
| `report_interval` | integer | false | | Report interval is the duration after which the agent should send stats again. |
|
||||
|
||||
## coderd.SCIMUser
|
||||
|
||||
```json
|
||||
@@ -6433,28 +6044,6 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
| `id` | string | false | | |
|
||||
| `workspace_agent_id` | string | false | | |
|
||||
|
||||
## codersdk.WorkspaceAgentMetadataDescription
|
||||
|
||||
```json
|
||||
{
|
||||
"display_name": "string",
|
||||
"interval": 0,
|
||||
"key": "string",
|
||||
"script": "string",
|
||||
"timeout": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------- | -------- | ------------ | ----------- |
|
||||
| `display_name` | string | false | | |
|
||||
| `interval` | integer | false | | |
|
||||
| `key` | string | false | | |
|
||||
| `script` | string | false | | |
|
||||
| `timeout` | integer | false | | |
|
||||
|
||||
## codersdk.WorkspaceAgentPortShare
|
||||
|
||||
```json
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
} from "../helpers";
|
||||
import { beforeCoderTest } from "../hooks";
|
||||
|
||||
// we no longer support versions prior to single tailnet: https://github.com/coder/coder/commit/d7cbdbd9c64ad26821e6b35834c59ecf85dcd9d4
|
||||
const agentVersion = "v0.27.0";
|
||||
// we no longer support versions w/o DRPC
|
||||
const agentVersion = "v2.12.1";
|
||||
|
||||
test.beforeEach(({ page }) => beforeCoderTest(page));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user