mirror of https://github.com/coder/coder.git
feat: Add fallback troubleshooting URL for coder agents (#5005)
This commit is contained in:
parent
1f4f0cee2a
commit
c1ecc91aab
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/coder/coder/cli/cliui"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/pty/ptytest"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
func TestAgent(t *testing.T) {
|
||||
|
@ -49,3 +50,50 @@ func TestAgent(t *testing.T) {
|
|||
disconnected.Store(true)
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestAgentTimeoutWithTroubleshootingURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, _ := testutil.Context(t)
|
||||
|
||||
wantURL := "https://coder.com/troubleshoot"
|
||||
|
||||
var connected, timeout atomic.Bool
|
||||
cmd := &cobra.Command{
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := cliui.Agent(cmd.Context(), cmd.OutOrStdout(), cliui.AgentOptions{
|
||||
WorkspaceName: "example",
|
||||
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
|
||||
agent := codersdk.WorkspaceAgent{
|
||||
Status: codersdk.WorkspaceAgentConnecting,
|
||||
TroubleshootingURL: "https://coder.com/troubleshoot",
|
||||
}
|
||||
switch {
|
||||
case connected.Load():
|
||||
agent.Status = codersdk.WorkspaceAgentConnected
|
||||
case timeout.Load():
|
||||
agent.Status = codersdk.WorkspaceAgentTimeout
|
||||
}
|
||||
return agent, nil
|
||||
},
|
||||
FetchInterval: time.Millisecond,
|
||||
WarnInterval: 5 * time.Millisecond,
|
||||
})
|
||||
return err
|
||||
},
|
||||
}
|
||||
ptty := ptytest.New(t)
|
||||
cmd.SetOutput(ptty.Output())
|
||||
cmd.SetIn(ptty.Input())
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
err := cmd.ExecuteContext(ctx)
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
ptty.ExpectMatch("Don't panic")
|
||||
timeout.Store(true)
|
||||
ptty.ExpectMatch(wantURL)
|
||||
connected.Store(true)
|
||||
<-done
|
||||
}
|
||||
|
|
|
@ -347,6 +347,13 @@ func newConfig() *codersdk.DeploymentConfig {
|
|||
Hidden: true,
|
||||
Default: 10 * time.Minute,
|
||||
},
|
||||
AgentFallbackTroubleshootingURL: &codersdk.DeploymentConfigField[string]{
|
||||
Name: "Agent Fallback Troubleshooting URL",
|
||||
Usage: "URL to use for agent troubleshooting when not set in the template",
|
||||
Flag: "agent-fallback-troubleshooting-url",
|
||||
Hidden: true,
|
||||
Default: "https://coder.com/docs/coder-oss/latest/templates#troubleshooting-templates",
|
||||
},
|
||||
AuditLogging: &codersdk.DeploymentConfigField[bool]{
|
||||
Name: "Audit Logging",
|
||||
Usage: "Specifies whether audit logging is enabled.",
|
||||
|
|
|
@ -262,7 +262,7 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
|
|||
}
|
||||
}
|
||||
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error reading job agent.",
|
||||
|
|
|
@ -52,7 +52,7 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
return
|
||||
}
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error reading workspace agent.",
|
||||
|
@ -67,7 +67,7 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
|||
func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error reading workspace agent.",
|
||||
|
@ -138,7 +138,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
|
|||
func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error reading workspace agent.",
|
||||
|
@ -192,7 +192,7 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error reading workspace agent.",
|
||||
|
@ -269,7 +269,7 @@ func (api *API) workspaceAgentListeningPorts(rw http.ResponseWriter, r *http.Req
|
|||
return
|
||||
}
|
||||
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error reading workspace agent.",
|
||||
|
@ -660,7 +660,7 @@ func convertApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp {
|
|||
return apps
|
||||
}
|
||||
|
||||
func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, agentInactiveDisconnectTimeout time.Duration) (codersdk.WorkspaceAgent, error) {
|
||||
func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string) (codersdk.WorkspaceAgent, error) {
|
||||
var envs map[string]string
|
||||
if dbAgent.EnvironmentVariables.Valid {
|
||||
err := json.Unmarshal(dbAgent.EnvironmentVariables.RawMessage, &envs)
|
||||
|
@ -668,6 +668,10 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
|
|||
return codersdk.WorkspaceAgent{}, xerrors.Errorf("unmarshal env vars: %w", err)
|
||||
}
|
||||
}
|
||||
troubleshootingURL := agentFallbackTroubleshootingURL
|
||||
if dbAgent.TroubleshootingURL != "" {
|
||||
troubleshootingURL = dbAgent.TroubleshootingURL
|
||||
}
|
||||
workspaceAgent := codersdk.WorkspaceAgent{
|
||||
ID: dbAgent.ID,
|
||||
CreatedAt: dbAgent.CreatedAt,
|
||||
|
@ -683,7 +687,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
|
|||
Directory: dbAgent.Directory,
|
||||
Apps: apps,
|
||||
ConnectionTimeoutSeconds: dbAgent.ConnectionTimeoutSeconds,
|
||||
TroubleshootingURL: dbAgent.TroubleshootingURL,
|
||||
TroubleshootingURL: troubleshootingURL,
|
||||
}
|
||||
node := coordinator.Node(dbAgent.ID)
|
||||
if node != nil {
|
||||
|
|
|
@ -76,7 +76,7 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
_, err = client.WorkspaceAgent(ctx, workspace.LatestBuild.Resources[0].Agents[0].ID)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
t.Run("Timeout", func(t *testing.T) {
|
||||
t.Run("HasFallbackTroubleshootingURL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
IncludeProvisionerDaemon: true,
|
||||
|
@ -99,8 +99,6 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
Auth: &proto.Agent_Token{
|
||||
Token: authToken,
|
||||
},
|
||||
ConnectionTimeoutSeconds: 1,
|
||||
TroubleshootingUrl: "https://example.com/troubleshoot",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
|
@ -115,13 +113,63 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
|
||||
defer cancel()
|
||||
|
||||
workspace, err := client.Workspace(ctx, workspace.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, workspace.LatestBuild.Resources[0].Agents[0].TroubleshootingURL)
|
||||
t.Log(workspace.LatestBuild.Resources[0].Agents[0].TroubleshootingURL)
|
||||
})
|
||||
t.Run("Timeout", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
IncludeProvisionerDaemon: true,
|
||||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
authToken := uuid.NewString()
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
wantTroubleshootingURL := "https://example.com/troubleshoot"
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
Agents: []*proto.Agent{{
|
||||
Id: uuid.NewString(),
|
||||
Directory: tmpDir,
|
||||
Auth: &proto.Agent_Token{
|
||||
Token: authToken,
|
||||
},
|
||||
ConnectionTimeoutSeconds: 1,
|
||||
TroubleshootingUrl: wantTroubleshootingURL,
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
|
||||
defer cancel()
|
||||
|
||||
var err error
|
||||
testutil.Eventually(ctx, t, func(ctx context.Context) (done bool) {
|
||||
workspace, err := client.Workspace(ctx, workspace.ID)
|
||||
workspace, err = client.Workspace(ctx, workspace.ID)
|
||||
if !assert.NoError(t, err) {
|
||||
return false
|
||||
}
|
||||
return workspace.LatestBuild.Resources[0].Agents[0].Status == codersdk.WorkspaceAgentTimeout
|
||||
}, testutil.IntervalMedium, "agent status timeout")
|
||||
|
||||
require.Equal(t, wantTroubleshootingURL, workspace.LatestBuild.Resources[0].Agents[0].TroubleshootingURL)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -902,7 +902,7 @@ func (api *API) convertWorkspaceBuild(
|
|||
apiAgents := make([]codersdk.WorkspaceAgent, 0)
|
||||
for _, agent := range agents {
|
||||
apps := appsByAgentID[agent.ID]
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(apps), api.AgentInactiveDisconnectTimeout)
|
||||
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(apps), api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
|
||||
if err != nil {
|
||||
return codersdk.WorkspaceBuild{}, xerrors.Errorf("converting workspace agent: %w", err)
|
||||
}
|
||||
|
|
|
@ -11,35 +11,36 @@ import (
|
|||
|
||||
// DeploymentConfig is the central configuration for the coder server.
|
||||
type DeploymentConfig struct {
|
||||
AccessURL *DeploymentConfigField[string] `json:"access_url" typescript:",notnull"`
|
||||
WildcardAccessURL *DeploymentConfigField[string] `json:"wildcard_access_url" typescript:",notnull"`
|
||||
Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"`
|
||||
AutobuildPollInterval *DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval" typescript:",notnull"`
|
||||
DERP *DERP `json:"derp" typescript:",notnull"`
|
||||
GitAuth *DeploymentConfigField[[]GitAuthConfig] `json:"gitauth" typescript:",notnull"`
|
||||
Prometheus *PrometheusConfig `json:"prometheus" typescript:",notnull"`
|
||||
Pprof *PprofConfig `json:"pprof" typescript:",notnull"`
|
||||
ProxyTrustedHeaders *DeploymentConfigField[[]string] `json:"proxy_trusted_headers" typescript:",notnull"`
|
||||
ProxyTrustedOrigins *DeploymentConfigField[[]string] `json:"proxy_trusted_origins" typescript:",notnull"`
|
||||
CacheDirectory *DeploymentConfigField[string] `json:"cache_directory" typescript:",notnull"`
|
||||
InMemoryDatabase *DeploymentConfigField[bool] `json:"in_memory_database" typescript:",notnull"`
|
||||
PostgresURL *DeploymentConfigField[string] `json:"pg_connection_url" typescript:",notnull"`
|
||||
OAuth2 *OAuth2Config `json:"oauth2" typescript:",notnull"`
|
||||
OIDC *OIDCConfig `json:"oidc" typescript:",notnull"`
|
||||
Telemetry *TelemetryConfig `json:"telemetry" typescript:",notnull"`
|
||||
TLS *TLSConfig `json:"tls" typescript:",notnull"`
|
||||
Trace *TraceConfig `json:"trace" typescript:",notnull"`
|
||||
SecureAuthCookie *DeploymentConfigField[bool] `json:"secure_auth_cookie" typescript:",notnull"`
|
||||
SSHKeygenAlgorithm *DeploymentConfigField[string] `json:"ssh_keygen_algorithm" typescript:",notnull"`
|
||||
AutoImportTemplates *DeploymentConfigField[[]string] `json:"auto_import_templates" typescript:",notnull"`
|
||||
MetricsCacheRefreshInterval *DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval" typescript:",notnull"`
|
||||
AgentStatRefreshInterval *DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval" typescript:",notnull"`
|
||||
AuditLogging *DeploymentConfigField[bool] `json:"audit_logging" typescript:",notnull"`
|
||||
BrowserOnly *DeploymentConfigField[bool] `json:"browser_only" typescript:",notnull"`
|
||||
SCIMAPIKey *DeploymentConfigField[string] `json:"scim_api_key" typescript:",notnull"`
|
||||
Provisioner *ProvisionerConfig `json:"provisioner" typescript:",notnull"`
|
||||
APIRateLimit *DeploymentConfigField[int] `json:"api_rate_limit" typescript:",notnull"`
|
||||
Experimental *DeploymentConfigField[bool] `json:"experimental" typescript:",notnull"`
|
||||
AccessURL *DeploymentConfigField[string] `json:"access_url" typescript:",notnull"`
|
||||
WildcardAccessURL *DeploymentConfigField[string] `json:"wildcard_access_url" typescript:",notnull"`
|
||||
Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"`
|
||||
AutobuildPollInterval *DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval" typescript:",notnull"`
|
||||
DERP *DERP `json:"derp" typescript:",notnull"`
|
||||
GitAuth *DeploymentConfigField[[]GitAuthConfig] `json:"gitauth" typescript:",notnull"`
|
||||
Prometheus *PrometheusConfig `json:"prometheus" typescript:",notnull"`
|
||||
Pprof *PprofConfig `json:"pprof" typescript:",notnull"`
|
||||
ProxyTrustedHeaders *DeploymentConfigField[[]string] `json:"proxy_trusted_headers" typescript:",notnull"`
|
||||
ProxyTrustedOrigins *DeploymentConfigField[[]string] `json:"proxy_trusted_origins" typescript:",notnull"`
|
||||
CacheDirectory *DeploymentConfigField[string] `json:"cache_directory" typescript:",notnull"`
|
||||
InMemoryDatabase *DeploymentConfigField[bool] `json:"in_memory_database" typescript:",notnull"`
|
||||
PostgresURL *DeploymentConfigField[string] `json:"pg_connection_url" typescript:",notnull"`
|
||||
OAuth2 *OAuth2Config `json:"oauth2" typescript:",notnull"`
|
||||
OIDC *OIDCConfig `json:"oidc" typescript:",notnull"`
|
||||
Telemetry *TelemetryConfig `json:"telemetry" typescript:",notnull"`
|
||||
TLS *TLSConfig `json:"tls" typescript:",notnull"`
|
||||
Trace *TraceConfig `json:"trace" typescript:",notnull"`
|
||||
SecureAuthCookie *DeploymentConfigField[bool] `json:"secure_auth_cookie" typescript:",notnull"`
|
||||
SSHKeygenAlgorithm *DeploymentConfigField[string] `json:"ssh_keygen_algorithm" typescript:",notnull"`
|
||||
AutoImportTemplates *DeploymentConfigField[[]string] `json:"auto_import_templates" typescript:",notnull"`
|
||||
MetricsCacheRefreshInterval *DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval" typescript:",notnull"`
|
||||
AgentStatRefreshInterval *DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval" typescript:",notnull"`
|
||||
AgentFallbackTroubleshootingURL *DeploymentConfigField[string] `json:"agent_fallback_troubleshooting_url" typescript:",notnull"`
|
||||
AuditLogging *DeploymentConfigField[bool] `json:"audit_logging" typescript:",notnull"`
|
||||
BrowserOnly *DeploymentConfigField[bool] `json:"browser_only" typescript:",notnull"`
|
||||
SCIMAPIKey *DeploymentConfigField[string] `json:"scim_api_key" typescript:",notnull"`
|
||||
Provisioner *ProvisionerConfig `json:"provisioner" typescript:",notnull"`
|
||||
APIRateLimit *DeploymentConfigField[int] `json:"api_rate_limit" typescript:",notnull"`
|
||||
Experimental *DeploymentConfigField[bool] `json:"experimental" typescript:",notnull"`
|
||||
}
|
||||
|
||||
type DERP struct {
|
||||
|
|
|
@ -56,7 +56,7 @@ type WorkspaceAgent struct {
|
|||
// DERPLatency is mapped by region name (e.g. "New York City", "Seattle").
|
||||
DERPLatency map[string]DERPRegion `json:"latency,omitempty"`
|
||||
ConnectionTimeoutSeconds int32 `json:"connection_timeout_seconds"`
|
||||
TroubleshootingURL string `json:"troubleshooting_url,omitempty"`
|
||||
TroubleshootingURL string `json:"troubleshooting_url"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentResourceMetadata struct {
|
||||
|
|
|
@ -299,6 +299,7 @@ export interface DeploymentConfig {
|
|||
readonly auto_import_templates: DeploymentConfigField<string[]>
|
||||
readonly metrics_cache_refresh_interval: DeploymentConfigField<number>
|
||||
readonly agent_stat_refresh_interval: DeploymentConfigField<number>
|
||||
readonly agent_fallback_troubleshooting_url: DeploymentConfigField<string>
|
||||
readonly audit_logging: DeploymentConfigField<boolean>
|
||||
readonly browser_only: DeploymentConfigField<boolean>
|
||||
readonly scim_api_key: DeploymentConfigField<string>
|
||||
|
@ -817,7 +818,7 @@ export interface WorkspaceAgent {
|
|||
readonly apps: WorkspaceApp[]
|
||||
readonly latency?: Record<string, DERPRegion>
|
||||
readonly connection_timeout_seconds: number
|
||||
readonly troubleshooting_url?: string
|
||||
readonly troubleshooting_url: string
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
|
|
|
@ -45,7 +45,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
>
|
||||
<Stack direction="row" alignItems="baseline">
|
||||
<div className={styles.agentStatusWrapper}>
|
||||
<AgentStatus agent={agent} workspace={workspace} />
|
||||
<AgentStatus agent={agent} />
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.agentName}>{agent.name}</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Tooltip from "@material-ui/core/Tooltip"
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { combineClasses } from "util/combineClasses"
|
||||
import { Workspace, WorkspaceAgent } from "api/typesGenerated"
|
||||
import { WorkspaceAgent } from "api/typesGenerated"
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import WarningRounded from "@material-ui/icons/WarningRounded"
|
||||
|
@ -58,15 +58,12 @@ const ConnectingStatus: React.FC = () => {
|
|||
|
||||
const TimeoutStatus: React.FC<{
|
||||
agent: WorkspaceAgent
|
||||
workspace: Workspace
|
||||
}> = ({ agent, workspace }) => {
|
||||
}> = ({ agent }) => {
|
||||
const { t } = useTranslation("agent")
|
||||
const styles = useStyles()
|
||||
const anchorRef = useRef<SVGSVGElement>(null)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const id = isOpen ? "timeout-popover" : undefined
|
||||
const troubleshootLink =
|
||||
agent.troubleshooting_url ?? `/templates/${workspace.template_name}#readme`
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -88,7 +85,11 @@ const TimeoutStatus: React.FC<{
|
|||
<HelpTooltipTitle>{t("timeoutTooltip.title")}</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
{t("timeoutTooltip.message")}{" "}
|
||||
<Link target="_blank" rel="noreferrer" href={troubleshootLink}>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={agent.troubleshooting_url}
|
||||
>
|
||||
{t("timeoutTooltip.link")}
|
||||
</Link>
|
||||
.
|
||||
|
@ -100,8 +101,7 @@ const TimeoutStatus: React.FC<{
|
|||
|
||||
export const AgentStatus: React.FC<{
|
||||
agent: WorkspaceAgent
|
||||
workspace: Workspace
|
||||
}> = ({ agent, workspace }) => {
|
||||
}> = ({ agent }) => {
|
||||
return (
|
||||
<ChooseOne>
|
||||
<Cond condition={agent.status === "connected"}>
|
||||
|
@ -111,7 +111,7 @@ export const AgentStatus: React.FC<{
|
|||
<DisconnectedStatus />
|
||||
</Cond>
|
||||
<Cond condition={agent.status === "timeout"}>
|
||||
<TimeoutStatus agent={agent} workspace={workspace} />
|
||||
<TimeoutStatus agent={agent} />
|
||||
</Cond>
|
||||
<Cond>
|
||||
<ConnectingStatus />
|
||||
|
|
Loading…
Reference in New Issue