From f9b1656b0e1a214986182aadf395a3c4efcf3828 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Tue, 25 Apr 2023 03:00:42 +0000 Subject: [PATCH 1/4] feat: add regions endpoint for proxies feature --- coderd/coderd.go | 5 + coderd/workspaceproxies.go | 67 +++++++++++ coderd/workspaceproxies_test.go | 67 +++++++++++ codersdk/workspaceproxy.go | 41 +++++++ enterprise/coderd/coderd.go | 13 +- enterprise/coderd/workspaceproxy.go | 54 ++++++++- enterprise/coderd/workspaceproxy_test.go | 146 +++++++++++++++++++++++ 7 files changed, 387 insertions(+), 6 deletions(-) create mode 100644 coderd/workspaceproxies.go create mode 100644 coderd/workspaceproxies_test.go diff --git a/coderd/coderd.go b/coderd/coderd.go index a5cf693a3d8b6..cc8aca086e024 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -461,6 +461,11 @@ func New(options *Options) *API { r.Post("/csp/reports", api.logReportCSPViolations) r.Get("/buildinfo", buildInfo(api.AccessURL)) + // /regions is overridden in the enterprise version + r.Group(func(r chi.Router) { + r.Use(apiKeyMiddleware) + r.Get("/regions", api.regions) + }) r.Route("/deployment", func(r chi.Router) { r.Use(apiKeyMiddleware) r.Get("/config", api.deploymentValues) diff --git a/coderd/workspaceproxies.go b/coderd/workspaceproxies.go new file mode 100644 index 0000000000000..7bd5eed3f479b --- /dev/null +++ b/coderd/workspaceproxies.go @@ -0,0 +1,67 @@ +package coderd + +import ( + "context" + "database/sql" + "net/http" + + "github.com/google/uuid" + "golang.org/x/xerrors" + + "github.com/coder/coder/coderd/database/dbauthz" + "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" +) + +func (api *API) PrimaryRegion(ctx context.Context) (codersdk.Region, error) { + deploymentIDStr, err := api.Database.GetDeploymentID(ctx) + if xerrors.Is(err, sql.ErrNoRows) { + // This shouldn't happen but it's pretty easy to avoid this causing + // issues by falling back to a nil UUID. + deploymentIDStr = uuid.Nil.String() + } else if err != nil { + return codersdk.Region{}, xerrors.Errorf("get deployment ID: %w", err) + } + deploymentID, err := uuid.Parse(deploymentIDStr) + if err != nil { + // This also shouldn't happen but we fallback to nil UUID. + deploymentID = uuid.Nil + } + + return codersdk.Region{ + ID: deploymentID, + // TODO: provide some way to customize these fields for the primary + // region + Name: "primary", + DisplayName: "Default", + IconURL: "/emojis/1f60e.png", // face with sunglasses + Healthy: true, + PathAppURL: api.AccessURL.String(), + WildcardHostname: api.AppHostname, + }, nil +} + +// @Summary Get site-wide regions for workspace connections +// @ID get-site-wide-regions-for-workspace-connections +// @Security CoderSessionToken +// @Produce json +// @Tags WorkspaceProxies +// @Success 200 {object} codersdk.RegionsResponse +// @Router /regions [get] +func (api *API) regions(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + //nolint:gocritic // this route intentionally requests resources that users + // cannot usually access in order to give them a full list of available + // regions. + ctx = dbauthz.AsSystemRestricted(ctx) + + region, err := api.PrimaryRegion(ctx) + if err != nil { + httpapi.InternalServerError(rw, err) + return + } + + httpapi.Write(ctx, rw, http.StatusOK, codersdk.RegionsResponse{ + Regions: []codersdk.Region{region}, + }) +} diff --git a/coderd/workspaceproxies_test.go b/coderd/workspaceproxies_test.go new file mode 100644 index 0000000000000..d11ab8fbdd975 --- /dev/null +++ b/coderd/workspaceproxies_test.go @@ -0,0 +1,67 @@ +package coderd_test + +import ( + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/coderd/database/dbtestutil" + "github.com/coder/coder/codersdk" + "github.com/coder/coder/testutil" +) + +func TestRegions(t *testing.T) { + t.Parallel() + + t.Run("OK", func(t *testing.T) { + t.Parallel() + const appHostname = "*.apps.coder.test" + + db, pubsub := dbtestutil.NewDB(t) + deploymentID := uuid.New() + + ctx := testutil.Context(t, testutil.WaitLong) + err := db.InsertDeploymentID(ctx, deploymentID.String()) + require.NoError(t, err) + + client := coderdtest.New(t, &coderdtest.Options{ + AppHostname: appHostname, + Database: db, + Pubsub: pubsub, + }) + _ = coderdtest.CreateFirstUser(t, client) + + regions, err := client.Regions(ctx) + require.NoError(t, err) + + require.Len(t, regions, 1) + require.NotEqual(t, uuid.Nil, regions[0].ID) + require.Equal(t, regions[0].ID, deploymentID) + require.Equal(t, "primary", regions[0].Name) + require.Equal(t, "Default", regions[0].DisplayName) + require.NotEmpty(t, regions[0].IconURL) + require.True(t, regions[0].Healthy) + require.Equal(t, client.URL.String(), regions[0].PathAppURL) + require.Equal(t, appHostname, regions[0].WildcardHostname) + + // Ensure the primary region ID is constant. + regions2, err := client.Regions(ctx) + require.NoError(t, err) + require.Equal(t, regions[0].ID, regions2[0].ID) + }) + + t.Run("RequireAuth", func(t *testing.T) { + t.Parallel() + + ctx := testutil.Context(t, testutil.WaitLong) + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + + unauthedClient := codersdk.New(client.URL) + regions, err := unauthedClient.Regions(ctx) + require.Error(t, err) + require.Empty(t, regions) + }) +} diff --git a/codersdk/workspaceproxy.go b/codersdk/workspaceproxy.go index 57f180b4e7aff..336d37e30b283 100644 --- a/codersdk/workspaceproxy.go +++ b/codersdk/workspaceproxy.go @@ -130,3 +130,44 @@ func (c *Client) DeleteWorkspaceProxyByName(ctx context.Context, name string) er func (c *Client) DeleteWorkspaceProxyByID(ctx context.Context, id uuid.UUID) error { return c.DeleteWorkspaceProxyByName(ctx, id.String()) } + +type RegionsResponse struct { + Regions []Region `json:"regions"` +} + +type Region struct { + ID uuid.UUID `json:"id" format:"uuid"` + Name string `json:"name"` + DisplayName string `json:"display_name"` + IconURL string `json:"icon_url"` + Healthy bool `json:"healthy"` + + // PathAppURL is the URL to the base path for path apps. Optional + // unless wildcard_hostname is set. + // E.g. https://us.example.com + PathAppURL string `json:"path_app_url"` + + // WildcardHostname is the wildcard hostname for subdomain apps. + // E.g. *.us.example.com + // E.g. *--suffix.au.example.com + // Optional. Does not need to be on the same domain as PathAppURL. + WildcardHostname string `json:"wildcard_hostname"` +} + +func (c *Client) Regions(ctx context.Context) ([]Region, error) { + res, err := c.Request(ctx, http.MethodGet, + "/api/v2/regions", + nil, + ) + if err != nil { + return nil, xerrors.Errorf("make request: %w", err) + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, ReadBodyAsError(res) + } + + var regions RegionsResponse + return regions.Regions, json.NewDecoder(res.Body).Decode(®ions) +} diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index ed0aea963b0e8..3a7ac382506e2 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -74,6 +74,11 @@ func New(ctx context.Context, options *Options) (*API, error) { api.AGPL.APIHandler.Group(func(r chi.Router) { r.Get("/entitlements", api.serveEntitlements) + // /regions overrides the AGPL /regions endpoint + r.Group(func(r chi.Router) { + r.Use(apiKeyMiddleware) + r.Get("/regions", api.regions) + }) r.Route("/replicas", func(r chi.Router) { r.Use(apiKeyMiddleware) r.Get("/", api.replicas) @@ -231,7 +236,7 @@ func New(ctx context.Context, options *Options) (*API, error) { if api.AGPL.Experiments.Enabled(codersdk.ExperimentMoons) { // Proxy health is a moon feature. - api.proxyHealth, err = proxyhealth.New(&proxyhealth.Options{ + api.ProxyHealth, err = proxyhealth.New(&proxyhealth.Options{ Interval: time.Second * 5, DB: api.Database, Logger: options.Logger.Named("proxyhealth"), @@ -241,7 +246,7 @@ func New(ctx context.Context, options *Options) (*API, error) { if err != nil { return nil, xerrors.Errorf("initialize proxy health: %w", err) } - go api.proxyHealth.Run(ctx) + go api.ProxyHealth.Run(ctx) // Force the initial loading of the cache. Do this in a go routine in case // the calls to the workspace proxies hang and this takes some time. go api.forceWorkspaceProxyHealthUpdate(ctx) @@ -287,8 +292,8 @@ type API struct { replicaManager *replicasync.Manager // Meshes DERP connections from multiple replicas. derpMesh *derpmesh.Mesh - // proxyHealth checks the reachability of all workspace proxies. - proxyHealth *proxyhealth.ProxyHealth + // ProxyHealth checks the reachability of all workspace proxies. + ProxyHealth *proxyhealth.ProxyHealth entitlementsMu sync.RWMutex entitlements codersdk.Entitlements diff --git a/enterprise/coderd/workspaceproxy.go b/enterprise/coderd/workspaceproxy.go index 136a000e57289..c2bae1560a823 100644 --- a/enterprise/coderd/workspaceproxy.go +++ b/enterprise/coderd/workspaceproxy.go @@ -16,6 +16,7 @@ import ( agpl "github.com/coder/coder/coderd" "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" + "github.com/coder/coder/coderd/database/dbauthz" "github.com/coder/coder/coderd/httpapi" "github.com/coder/coder/coderd/httpmw" "github.com/coder/coder/coderd/rbac" @@ -29,11 +30,60 @@ import ( // forceWorkspaceProxyHealthUpdate forces an update of the proxy health. // This is useful when a proxy is created or deleted. Errors will be logged. func (api *API) forceWorkspaceProxyHealthUpdate(ctx context.Context) { - if err := api.proxyHealth.ForceUpdate(ctx); err != nil { + if err := api.ProxyHealth.ForceUpdate(ctx); err != nil { api.Logger.Error(ctx, "force proxy health update", slog.Error(err)) } } +// NOTE: this doesn't need a swagger definition since AGPL already has one, and +// this route overrides the AGPL one. +func (api *API) regions(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + //nolint:gocritic // this route intentionally requests resources that users + // cannot usually access in order to give them a full list of available + // regions. + ctx = dbauthz.AsSystemRestricted(ctx) + + primaryRegion, err := api.AGPL.PrimaryRegion(ctx) + if err != nil { + httpapi.InternalServerError(rw, err) + return + } + regions := []codersdk.Region{primaryRegion} + + proxies, err := api.Database.GetWorkspaceProxies(ctx) + if err != nil { + httpapi.InternalServerError(rw, err) + return + } + + proxyHealth := api.ProxyHealth.HealthStatus() + for _, proxy := range proxies { + if proxy.Deleted { + continue + } + + health, ok := proxyHealth[proxy.ID] + if !ok { + health.Status = proxyhealth.Unknown + } + + regions = append(regions, codersdk.Region{ + ID: proxy.ID, + Name: proxy.Name, + DisplayName: proxy.DisplayName, + IconURL: proxy.Icon, + Healthy: health.Status == proxyhealth.Healthy, + PathAppURL: proxy.Url, + WildcardHostname: proxy.WildcardHostname, + }) + } + + httpapi.Write(ctx, rw, http.StatusOK, codersdk.RegionsResponse{ + Regions: regions, + }) +} + // @Summary Delete workspace proxy // @ID delete-workspace-proxy // @Security CoderSessionToken @@ -180,7 +230,7 @@ func (api *API) workspaceProxies(rw http.ResponseWriter, r *http.Request) { return } - statues := api.proxyHealth.HealthStatus() + statues := api.ProxyHealth.HealthStatus() httpapi.Write(ctx, rw, http.StatusOK, convertProxies(proxies, statues)) } diff --git a/enterprise/coderd/workspaceproxy_test.go b/enterprise/coderd/workspaceproxy_test.go index ec467986efd5c..4a48a0b7349da 100644 --- a/enterprise/coderd/workspaceproxy_test.go +++ b/enterprise/coderd/workspaceproxy_test.go @@ -28,6 +28,152 @@ import ( "github.com/coder/coder/testutil" ) +func TestRegions(t *testing.T) { + t.Parallel() + + const appHostname = "*.apps.coder.test" + + t.Run("OK", func(t *testing.T) { + t.Parallel() + + dv := coderdtest.DeploymentValues(t) + dv.Experiments = []string{ + string(codersdk.ExperimentMoons), + "*", + } + + db, pubsub := dbtestutil.NewDB(t) + deploymentID := uuid.New() + + ctx := testutil.Context(t, testutil.WaitLong) + err := db.InsertDeploymentID(ctx, deploymentID.String()) + require.NoError(t, err) + + client := coderdenttest.New(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + AppHostname: appHostname, + Database: db, + Pubsub: pubsub, + DeploymentValues: dv, + }, + }) + _ = coderdtest.CreateFirstUser(t, client) + + regions, err := client.Regions(ctx) + require.NoError(t, err) + + require.Len(t, regions, 1) + require.NotEqual(t, uuid.Nil, regions[0].ID) + require.Equal(t, regions[0].ID, deploymentID) + require.Equal(t, "primary", regions[0].Name) + require.Equal(t, "Default", regions[0].DisplayName) + require.NotEmpty(t, regions[0].IconURL) + require.True(t, regions[0].Healthy) + require.Equal(t, client.URL.String(), regions[0].PathAppURL) + require.Equal(t, appHostname, regions[0].WildcardHostname) + + // Ensure the primary region ID is constant. + regions2, err := client.Regions(ctx) + require.NoError(t, err) + require.Equal(t, regions[0].ID, regions2[0].ID) + }) + + t.Run("WithProxies", func(t *testing.T) { + t.Parallel() + + dv := coderdtest.DeploymentValues(t) + dv.Experiments = []string{ + string(codersdk.ExperimentMoons), + "*", + } + + db, pubsub := dbtestutil.NewDB(t) + deploymentID := uuid.New() + + ctx := testutil.Context(t, testutil.WaitLong) + err := db.InsertDeploymentID(ctx, deploymentID.String()) + require.NoError(t, err) + + client, closer, api := coderdenttest.NewWithAPI(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + AppHostname: appHostname, + Database: db, + Pubsub: pubsub, + DeploymentValues: dv, + }, + }) + t.Cleanup(func() { + _ = closer.Close() + }) + _ = coderdtest.CreateFirstUser(t, client) + _ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureWorkspaceProxy: 1, + }, + }) + + const proxyName = "hello" + _ = coderdenttest.NewWorkspaceProxy(t, api, client, &coderdenttest.ProxyOptions{ + Name: proxyName, + AppHostname: appHostname + ".proxy", + }) + proxy, err := db.GetWorkspaceProxyByName(ctx, proxyName) + require.NoError(t, err) + + // Refresh proxy health. + err = api.ProxyHealth.ForceUpdate(ctx) + require.NoError(t, err) + + regions, err := client.Regions(ctx) + require.NoError(t, err) + require.Len(t, regions, 2) + + // Region 0 is the primary require.Len(t, regions, 1) + require.NotEqual(t, uuid.Nil, regions[0].ID) + require.Equal(t, regions[0].ID, deploymentID) + require.Equal(t, "primary", regions[0].Name) + require.Equal(t, "Default", regions[0].DisplayName) + require.NotEmpty(t, regions[0].IconURL) + require.True(t, regions[0].Healthy) + require.Equal(t, client.URL.String(), regions[0].PathAppURL) + require.Equal(t, appHostname, regions[0].WildcardHostname) + + // Region 1 is the proxy. + require.NotEqual(t, uuid.Nil, regions[1].ID) + require.Equal(t, proxy.ID, regions[1].ID) + require.Equal(t, proxy.Name, regions[1].Name) + require.Equal(t, proxy.DisplayName, regions[1].DisplayName) + require.Equal(t, proxy.Icon, regions[1].IconURL) + require.True(t, regions[1].Healthy) + require.Equal(t, proxy.Url, regions[1].PathAppURL) + require.Equal(t, proxy.WildcardHostname, regions[1].WildcardHostname) + }) + + t.Run("RequireAuth", func(t *testing.T) { + t.Parallel() + + dv := coderdtest.DeploymentValues(t) + dv.Experiments = []string{ + string(codersdk.ExperimentMoons), + "*", + } + + ctx := testutil.Context(t, testutil.WaitLong) + client := coderdenttest.New(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + AppHostname: appHostname, + DeploymentValues: dv, + }, + }) + _ = coderdtest.CreateFirstUser(t, client) + + unauthedClient := codersdk.New(client.URL) + regions, err := unauthedClient.Regions(ctx) + require.Error(t, err) + require.Empty(t, regions) + }) +} + func TestWorkspaceProxyCRUD(t *testing.T) { t.Parallel() From b5f238a65527670d997fc9bbe1f75df4e76ca172 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 25 Apr 2023 08:39:39 -0500 Subject: [PATCH 2/4] Make gen --- coderd/apidoc/docs.go | 65 ++++++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 61 +++++++++++++++++++++++++++++++++++ docs/api/schemas.md | 50 +++++++++++++++++++++++++++++ docs/manifest.json | 4 +++ 4 files changed, 180 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 4cb4940a03775..0147bacc1df4a 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1727,6 +1727,31 @@ const docTemplate = `{ } } }, + "/regions": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "WorkspaceProxies" + ], + "summary": "Get site-wide regions for workspace connections", + "operationId": "get-site-wide-regions-for-workspace-connections", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.RegionsResponse" + } + } + } + } + }, "/replicas": { "get": { "security": [ @@ -8316,6 +8341,46 @@ const docTemplate = `{ } } }, + "codersdk.Region": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "healthy": { + "type": "boolean" + }, + "icon_url": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "path_app_url": { + "description": "PathAppURL is the URL to the base path for path apps. Optional\nunless wildcard_hostname is set.\nE.g. https://us.example.com", + "type": "string" + }, + "wildcard_hostname": { + "description": "WildcardHostname is the wildcard hostname for subdomain apps.\nE.g. *.us.example.com\nE.g. *--suffix.au.example.com\nOptional. Does not need to be on the same domain as PathAppURL.", + "type": "string" + } + } + }, + "codersdk.RegionsResponse": { + "type": "object", + "properties": { + "regions": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Region" + } + } + } + }, "codersdk.Replica": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 558360d0c0cc6..6bd005bea8f67 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1501,6 +1501,27 @@ } } }, + "/regions": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["WorkspaceProxies"], + "summary": "Get site-wide regions for workspace connections", + "operationId": "get-site-wide-regions-for-workspace-connections", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.RegionsResponse" + } + } + } + } + }, "/replicas": { "get": { "security": [ @@ -7455,6 +7476,46 @@ } } }, + "codersdk.Region": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "healthy": { + "type": "boolean" + }, + "icon_url": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "path_app_url": { + "description": "PathAppURL is the URL to the base path for path apps. Optional\nunless wildcard_hostname is set.\nE.g. https://us.example.com", + "type": "string" + }, + "wildcard_hostname": { + "description": "WildcardHostname is the wildcard hostname for subdomain apps.\nE.g. *.us.example.com\nE.g. *--suffix.au.example.com\nOptional. Does not need to be on the same domain as PathAppURL.", + "type": "string" + } + } + }, + "codersdk.RegionsResponse": { + "type": "object", + "properties": { + "regions": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.Region" + } + } + } + }, "codersdk.Replica": { "type": "object", "properties": { diff --git a/docs/api/schemas.md b/docs/api/schemas.md index d297b31169065..145824503cc2a 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3480,6 +3480,56 @@ Parameter represents a set value for the scope. | `api` | integer | false | | | | `disable_all` | boolean | false | | | +## codersdk.Region + +```json +{ + "display_name": "string", + "healthy": true, + "icon_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "path_app_url": "string", + "wildcard_hostname": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------- | ------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `display_name` | string | false | | | +| `healthy` | boolean | false | | | +| `icon_url` | string | false | | | +| `id` | string | false | | | +| `name` | string | false | | | +| `path_app_url` | string | false | | Path app URL is the URL to the base path for path apps. Optional unless wildcard_hostname is set. E.g. https://us.example.com | +| `wildcard_hostname` | string | false | | Wildcard hostname is the wildcard hostname for subdomain apps. E.g. _.us.example.com E.g. _--suffix.au.example.com Optional. Does not need to be on the same domain as PathAppURL. | + +## codersdk.RegionsResponse + +```json +{ + "regions": [ + { + "display_name": "string", + "healthy": true, + "icon_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "path_app_url": "string", + "wildcard_hostname": "string" + } + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------- | ------------------------------------------- | -------- | ------------ | ----------- | +| `regions` | array of [codersdk.Region](#codersdkregion) | false | | | + ## codersdk.Replica ```json diff --git a/docs/manifest.json b/docs/manifest.json index f681b509a0ed1..45b7d24f6feab 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -466,6 +466,10 @@ "title": "Users", "path": "./api/users.md" }, + { + "title": "WorkspaceProxies", + "path": "./api/workspaceproxies.md" + }, { "title": "Workspaces", "path": "./api/workspaces.md" From b88a7278e04def2c975e54085a30aff732d62443 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 25 Apr 2023 08:39:51 -0500 Subject: [PATCH 3/4] fixup! Make gen --- docs/api/workspaceproxies.md | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docs/api/workspaceproxies.md diff --git a/docs/api/workspaceproxies.md b/docs/api/workspaceproxies.md new file mode 100644 index 0000000000000..5cd961c2a5410 --- /dev/null +++ b/docs/api/workspaceproxies.md @@ -0,0 +1,42 @@ +# WorkspaceProxies + +## Get site-wide regions for workspace connections + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/regions \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /regions` + +### Example responses + +> 200 Response + +```json +{ + "regions": [ + { + "display_name": "string", + "healthy": true, + "icon_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "name": "string", + "path_app_url": "string", + "wildcard_hostname": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | -------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.RegionsResponse](schemas.md#codersdkregionsresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). From 86aa52b4aafea5de7552aeaf78b268e36a575993 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 25 Apr 2023 14:24:35 +0000 Subject: [PATCH 4/4] Make gen didn't run the types gen --- site/src/api/typesGenerated.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index e2bb4a33f589d..6c3e7f0cea6bf 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -707,6 +707,22 @@ export interface RateLimitConfig { readonly api: number } +// From codersdk/workspaceproxy.go +export interface Region { + readonly id: string + readonly name: string + readonly display_name: string + readonly icon_url: string + readonly healthy: boolean + readonly path_app_url: string + readonly wildcard_hostname: string +} + +// From codersdk/workspaceproxy.go +export interface RegionsResponse { + readonly regions: Region[] +} + // From codersdk/replicas.go export interface Replica { readonly id: string