chore: add workspace proxies to the backend (#7032)

Co-authored-by: Dean Sheather <dean@deansheather.com>
This commit is contained in:
Steven Masley
2023-04-17 14:57:21 -05:00
committed by GitHub
parent dc5e16ae22
commit 658246d5f2
61 changed files with 3641 additions and 757 deletions

View File

@ -79,6 +79,10 @@ type Client struct {
HTTPClient *http.Client
URL *url.URL
// SessionTokenHeader is an optional custom header to use for setting tokens. By
// default 'Coder-Session-Token' is used.
SessionTokenHeader string
// Logger is optionally provided to log requests.
// Method, URL, and response code will be logged by default.
Logger slog.Logger
@ -150,7 +154,12 @@ func (c *Client) Request(ctx context.Context, method, path string, body interfac
if err != nil {
return nil, xerrors.Errorf("create request: %w", err)
}
req.Header.Set(SessionTokenHeader, c.SessionToken())
tokenHeader := c.SessionTokenHeader
if tokenHeader == "" {
tokenHeader = SessionTokenHeader
}
req.Header.Set(tokenHeader, c.SessionToken())
if r != nil {
req.Header.Set("Content-Type", "application/json")

View File

@ -1575,6 +1575,20 @@ type BuildInfoResponse struct {
ExternalURL string `json:"external_url"`
// Version returns the semantic version of the build.
Version string `json:"version"`
// DashboardURL is the URL to hit the deployment's dashboard.
// For external workspace proxies, this is the coderd they are connected
// to.
DashboardURL string `json:"dashboard_url"`
WorkspaceProxy bool `json:"workspace_proxy"`
}
type WorkspaceProxyBuildInfo struct {
// TODO: @emyrk what should we include here?
WorkspaceProxy bool `json:"workspace_proxy"`
// DashboardURL is the URL of the coderd this proxy is connected to.
DashboardURL string `json:"dashboard_url"`
}
// CanonicalVersion trims build information from the version.

View File

@ -200,18 +200,12 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
if err != nil {
return nil, xerrors.Errorf("parse url: %w", err)
}
jar, err := cookiejar.New(nil)
if err != nil {
return nil, xerrors.Errorf("create cookie jar: %w", err)
}
jar.SetCookies(coordinateURL, []*http.Cookie{{
Name: SessionTokenCookie,
Value: c.SessionToken(),
}})
httpClient := &http.Client{
Jar: jar,
Transport: c.HTTPClient.Transport,
coordinateHeaders := make(http.Header)
tokenHeader := SessionTokenHeader
if c.SessionTokenHeader != "" {
tokenHeader = c.SessionTokenHeader
}
coordinateHeaders.Set(tokenHeader, c.SessionToken())
ctx, cancel := context.WithCancel(ctx)
defer func() {
if err != nil {
@ -227,7 +221,8 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
options.Logger.Debug(ctx, "connecting")
// nolint:bodyclose
ws, res, err := websocket.Dial(ctx, coordinateURL.String(), &websocket.DialOptions{
HTTPClient: httpClient,
HTTPClient: c.HTTPClient,
HTTPHeader: coordinateHeaders,
// Need to disable compression to avoid a data-race.
CompressionMode: websocket.CompressionDisabled,
})

View File

@ -11,19 +11,10 @@ import (
"github.com/google/uuid"
)
type CreateWorkspaceProxyRequest struct {
Name string `json:"name"`
DisplayName string `json:"display_name"`
Icon string `json:"icon"`
URL string `json:"url"`
WildcardHostname string `json:"wildcard_hostname"`
}
type WorkspaceProxy struct {
ID uuid.UUID `db:"id" json:"id" format:"uuid"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id" format:"uuid"`
Name string `db:"name" json:"name"`
Icon string `db:"icon" json:"icon"`
ID uuid.UUID `db:"id" json:"id" format:"uuid"`
Name string `db:"name" json:"name"`
Icon string `db:"icon" json:"icon"`
// Full url including scheme of the proxy api url: https://us.example.com
URL string `db:"url" json:"url"`
// WildcardHostname with the wildcard for subdomain based app hosting: *.us.example.com
@ -33,24 +24,37 @@ type WorkspaceProxy struct {
Deleted bool `db:"deleted" json:"deleted"`
}
func (c *Client) CreateWorkspaceProxy(ctx context.Context, req CreateWorkspaceProxyRequest) (WorkspaceProxy, error) {
type CreateWorkspaceProxyRequest struct {
Name string `json:"name"`
DisplayName string `json:"display_name"`
Icon string `json:"icon"`
URL string `json:"url"`
WildcardHostname string `json:"wildcard_hostname"`
}
type CreateWorkspaceProxyResponse struct {
Proxy WorkspaceProxy `json:"proxy"`
ProxyToken string `json:"proxy_token"`
}
func (c *Client) CreateWorkspaceProxy(ctx context.Context, req CreateWorkspaceProxyRequest) (CreateWorkspaceProxyResponse, error) {
res, err := c.Request(ctx, http.MethodPost,
"/api/v2/workspaceproxies",
req,
)
if err != nil {
return WorkspaceProxy{}, xerrors.Errorf("make request: %w", err)
return CreateWorkspaceProxyResponse{}, xerrors.Errorf("make request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return WorkspaceProxy{}, ReadBodyAsError(res)
return CreateWorkspaceProxyResponse{}, ReadBodyAsError(res)
}
var resp WorkspaceProxy
var resp CreateWorkspaceProxyResponse
return resp, json.NewDecoder(res.Body).Decode(&resp)
}
func (c *Client) WorkspaceProxiesByOrganization(ctx context.Context) ([]WorkspaceProxy, error) {
func (c *Client) WorkspaceProxies(ctx context.Context) ([]WorkspaceProxy, error) {
res, err := c.Request(ctx, http.MethodGet,
"/api/v2/workspaceproxies",
nil,