mirror of
https://github.com/coder/coder.git
synced 2026-06-03 04:58:23 +00:00
c33812a430
When a user creates a workspace, opens the web terminal, then the workspace stops but the web terminal remains open the web terminal will retry the connection. Coder will issue a HTTP 502 Bad Gateway response when this occurs because coderd cannot connect to the workspace agent, however this is problematic as any load balancer sitting in front of Coder sees a 502 and thinks Coder is unhealthy. The main change is in https://github.com/coder/coder/pull/23090/changes#diff-bbe3b56ed3532289481a0e977867cd15048b7ca718ce676aae3f3332378eebc2R97, however the main test and downstream tests are also updated. This PR changes the response to a [HTTP 404](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/404) after internal discussion. <img width="1832" height="1511" alt="image" src="https://github.com/user-attachments/assets/0baff80d-bb98-4644-89cd-e80c87947098" /> Created with the help of Mux, reviewed and tested by a human
151 lines
4.8 KiB
Go
151 lines
4.8 KiB
Go
package workspaceapps
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"path"
|
|
|
|
"cdr.dev/slog/v3"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
"github.com/coder/coder/v2/site"
|
|
)
|
|
|
|
// WriteWorkspaceApp404 writes a HTML 404 error page for a workspace app. If
|
|
// appReq is not nil, it will be used to log the request details at debug level.
|
|
//
|
|
// The 'warnings' parameter is sent to the user, 'details' is only shown in the logs.
|
|
func WriteWorkspaceApp404(log slog.Logger, accessURL *url.URL, rw http.ResponseWriter, r *http.Request, appReq *Request, warnings []string, details string) {
|
|
if appReq != nil {
|
|
slog.Helper()
|
|
log.Debug(r.Context(),
|
|
"workspace app 404: "+details,
|
|
slog.F("username_or_id", appReq.UsernameOrID),
|
|
slog.F("workspace_and_agent", appReq.WorkspaceAndAgent),
|
|
slog.F("workspace_name_or_id", appReq.WorkspaceNameOrID),
|
|
slog.F("agent_name_or_id", appReq.AgentNameOrID),
|
|
slog.F("app_slug_or_port", appReq.AppSlugOrPort),
|
|
slog.F("hostname_prefix", appReq.Prefix),
|
|
slog.F("warnings", warnings),
|
|
)
|
|
}
|
|
|
|
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
|
|
Status: http.StatusNotFound,
|
|
Title: "Application Not Found",
|
|
Description: "The application or workspace you are trying to access does not exist or you do not have permission to access it.",
|
|
Warnings: warnings,
|
|
Actions: []site.Action{
|
|
{
|
|
URL: accessURL.String(),
|
|
Text: "Back to site",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// WriteWorkspaceApp500 writes a HTML 500 error page for a workspace app. If
|
|
// appReq is not nil, it's fields will be added to the logged error message.
|
|
func WriteWorkspaceApp500(log slog.Logger, accessURL *url.URL, rw http.ResponseWriter, r *http.Request, appReq *Request, err error, msg string) {
|
|
ctx := r.Context()
|
|
if appReq != nil {
|
|
slog.Helper()
|
|
ctx = slog.With(ctx,
|
|
slog.F("username_or_id", appReq.UsernameOrID),
|
|
slog.F("workspace_and_agent", appReq.WorkspaceAndAgent),
|
|
slog.F("workspace_name_or_id", appReq.WorkspaceNameOrID),
|
|
slog.F("agent_name_or_id", appReq.AgentNameOrID),
|
|
slog.F("app_name_or_port", appReq.AppSlugOrPort),
|
|
slog.F("hostname_prefix", appReq.Prefix),
|
|
)
|
|
}
|
|
log.Warn(ctx,
|
|
"workspace app auth server error: "+msg,
|
|
slog.Error(err),
|
|
)
|
|
|
|
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
|
|
Status: http.StatusInternalServerError,
|
|
Title: "Internal Server Error",
|
|
Description: "An internal server error occurred.",
|
|
Actions: []site.Action{
|
|
{
|
|
URL: accessURL.String(),
|
|
Text: "Back to site",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// WriteWorkspaceAppOffline writes a HTML 404 error page for a workspace app. If
|
|
// appReq is not nil, it will be used to log the request details at debug level.
|
|
func WriteWorkspaceAppOffline(log slog.Logger, accessURL *url.URL, rw http.ResponseWriter, r *http.Request, appReq *Request, msg string) {
|
|
if appReq != nil {
|
|
slog.Helper()
|
|
log.Debug(r.Context(),
|
|
"workspace app unavailable: "+msg,
|
|
slog.F("username_or_id", appReq.UsernameOrID),
|
|
slog.F("workspace_and_agent", appReq.WorkspaceAndAgent),
|
|
slog.F("workspace_name_or_id", appReq.WorkspaceNameOrID),
|
|
slog.F("agent_name_or_id", appReq.AgentNameOrID),
|
|
slog.F("app_slug_or_port", appReq.AppSlugOrPort),
|
|
slog.F("hostname_prefix", appReq.Prefix),
|
|
)
|
|
}
|
|
|
|
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
|
|
Status: http.StatusNotFound,
|
|
Title: "Application Unavailable",
|
|
Description: msg,
|
|
Actions: []site.Action{
|
|
{
|
|
Text: "Retry",
|
|
},
|
|
{
|
|
URL: accessURL.String(),
|
|
Text: "Back to site",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// WriteWorkspaceOffline writes a HTML 400 error page for a workspace app. If
|
|
// appReq is not nil, it will be used to log the request details at debug level.
|
|
func WriteWorkspaceOffline(log slog.Logger, accessURL *url.URL, rw http.ResponseWriter, r *http.Request, appReq *Request) {
|
|
if appReq != nil {
|
|
slog.Helper()
|
|
log.Debug(r.Context(),
|
|
"workspace app unavailable: workspace stopped",
|
|
slog.F("username_or_id", appReq.UsernameOrID),
|
|
slog.F("workspace_and_agent", appReq.WorkspaceAndAgent),
|
|
slog.F("workspace_name_or_id", appReq.WorkspaceNameOrID),
|
|
slog.F("agent_name_or_id", appReq.AgentNameOrID),
|
|
slog.F("app_slug_or_port", appReq.AppSlugOrPort),
|
|
slog.F("hostname_prefix", appReq.Prefix),
|
|
)
|
|
}
|
|
|
|
actions := []site.Action{
|
|
{
|
|
URL: accessURL.String(),
|
|
Text: "Back to site",
|
|
},
|
|
}
|
|
|
|
workspaceURL, err := url.Parse(accessURL.String())
|
|
if err == nil {
|
|
workspaceURL.Path = path.Join(accessURL.Path, "@"+appReq.UsernameOrID, appReq.WorkspaceNameOrID)
|
|
actions = append(actions, site.Action{
|
|
URL: workspaceURL.String(),
|
|
Text: "View workspace",
|
|
})
|
|
}
|
|
|
|
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
|
|
Status: http.StatusBadRequest,
|
|
Title: "Workspace Offline",
|
|
Description: fmt.Sprintf("Last workspace transition was to the %q state. Start the workspace to access its applications.", codersdk.WorkspaceTransitionStop),
|
|
Actions: actions,
|
|
})
|
|
}
|