chore: Add workspace proxy enterprise cli commands (#7176)

* feat: Add workspace proxy enterprise cli commands
* chore: Handle custom workspace proxy options. Remove excess
* chore: Add endpoint to register workspace proxies
This commit is contained in:
Steven Masley
2023-04-20 09:48:47 -05:00
committed by GitHub
parent 8926c10b7d
commit a5a5c4d400
30 changed files with 1558 additions and 107 deletions
+9 -8
View File
@@ -100,15 +100,16 @@ func New(ctx context.Context, options *Options) (*API, error) {
}),
)
r.Post("/issue-signed-app-token", api.workspaceProxyIssueSignedAppToken)
r.Post("/register", api.workspaceProxyRegister)
})
r.Route("/{workspaceproxy}", func(r chi.Router) {
r.Use(
apiKeyMiddleware,
httpmw.ExtractWorkspaceProxyParam(api.Database),
)
r.Delete("/", api.deleteWorkspaceProxy)
})
// TODO: Add specific workspace proxy endpoints.
// r.Route("/{proxyName}", func(r chi.Router) {
// r.Use(
// httpmw.ExtractWorkspaceProxyByNameParam(api.Database),
// )
//
// r.Get("/", api.workspaceProxyByName)
// })
})
r.Route("/organizations/{organization}/groups", func(r chi.Router) {
r.Use(
+1 -2
View File
@@ -115,14 +115,13 @@ func NewWorkspaceProxy(t *testing.T, coderdAPI *coderd.API, owner *codersdk.Clie
})
require.NoError(t, err, "failed to create workspace proxy")
wssrv, err := wsproxy.New(&wsproxy.Options{
wssrv, err := wsproxy.New(ctx, &wsproxy.Options{
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
DashboardURL: coderdAPI.AccessURL,
AccessURL: accessURL,
AppHostname: options.AppHostname,
AppHostnameRegex: appHostnameRegex,
RealIPConfig: coderdAPI.RealIPConfig,
AppSecurityKey: coderdAPI.AppSecurityKey,
Tracing: coderdAPI.TracerProvider,
APIRateLimit: coderdAPI.APIRateLimit,
SecureAuthCookie: coderdAPI.SecureAuthCookie,
+106
View File
@@ -13,12 +13,55 @@ import (
"github.com/coder/coder/coderd/audit"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/workspaceapps"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/cryptorand"
"github.com/coder/coder/enterprise/wsproxy/wsproxysdk"
)
// @Summary Delete workspace proxy
// @ID delete-workspace-proxy
// @Security CoderSessionToken
// @Produce json
// @Tags Enterprise
// @Param workspaceproxy path string true "Proxy ID or name" format(uuid)
// @Success 200 {object} codersdk.Response
// @Router /workspaceproxies/{workspaceproxy} [delete]
func (api *API) deleteWorkspaceProxy(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
proxy = httpmw.WorkspaceProxyParam(r)
auditor = api.AGPL.Auditor.Load()
aReq, commitAudit = audit.InitRequest[database.WorkspaceProxy](rw, &audit.RequestParams{
Audit: *auditor,
Log: api.Logger,
Request: r,
Action: database.AuditActionCreate,
})
)
aReq.Old = proxy
defer commitAudit()
err := api.Database.UpdateWorkspaceProxyDeleted(ctx, database.UpdateWorkspaceProxyDeletedParams{
ID: proxy.ID,
Deleted: true,
})
if httpapi.Is404Error(err) {
httpapi.ResourceNotFound(rw)
return
}
if err != nil {
httpapi.InternalServerError(rw, err)
return
}
aReq.New = database.WorkspaceProxy{}
httpapi.Write(ctx, rw, http.StatusOK, codersdk.Response{
Message: "Proxy has been deleted!",
})
}
// @Summary Create workspace proxy
// @ID create-workspace-proxy
// @Security CoderSessionToken
@@ -208,3 +251,66 @@ func (api *API) workspaceProxyIssueSignedAppToken(rw http.ResponseWriter, r *htt
SignedTokenStr: tokenStr,
})
}
// workspaceProxyRegister is used to register a new workspace proxy. When a proxy
// comes online, it will announce itself to this endpoint. This updates its values
// in the database and returns a signed token that can be used to authenticate
// tokens.
//
// @Summary Register workspace proxy
// @ID register-workspace-proxy
// @Security CoderSessionToken
// @Accept json
// @Produce json
// @Tags Enterprise
// @Param request body wsproxysdk.RegisterWorkspaceProxyRequest true "Issue signed app token request"
// @Success 201 {object} wsproxysdk.RegisterWorkspaceProxyResponse
// @Router /workspaceproxies/me/register [post]
// @x-apidocgen {"skip": true}
func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
proxy = httpmw.WorkspaceProxy(r)
)
var req wsproxysdk.RegisterWorkspaceProxyRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}
if err := validateProxyURL(req.AccessURL); err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "URL is invalid.",
Detail: err.Error(),
})
return
}
if req.WildcardHostname != "" {
if _, err := httpapi.CompileHostnamePattern(req.WildcardHostname); err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Wildcard URL is invalid.",
Detail: err.Error(),
})
return
}
}
_, err := api.Database.RegisterWorkspaceProxy(ctx, database.RegisterWorkspaceProxyParams{
ID: proxy.ID,
Url: req.AccessURL,
WildcardHostname: req.WildcardHostname,
})
if httpapi.Is404Error(err) {
httpapi.ResourceNotFound(rw)
return
}
if err != nil {
httpapi.InternalServerError(rw, err)
return
}
httpapi.Write(ctx, rw, http.StatusCreated, wsproxysdk.RegisterWorkspaceProxyResponse{
AppSecurityKey: api.AppSecurityKey.String(),
})
}
+36
View File
@@ -62,6 +62,42 @@ func TestWorkspaceProxyCRUD(t *testing.T) {
require.Equal(t, proxyRes.Proxy, proxies[0])
require.NotEmpty(t, proxyRes.ProxyToken)
})
t.Run("delete", func(t *testing.T) {
t.Parallel()
dv := coderdtest.DeploymentValues(t)
dv.Experiments = []string{
string(codersdk.ExperimentMoons),
"*",
}
client := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
DeploymentValues: dv,
},
})
_ = coderdtest.CreateFirstUser(t, client)
_ = coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureWorkspaceProxy: 1,
},
})
ctx := testutil.Context(t, testutil.WaitLong)
proxyRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
Name: namesgenerator.GetRandomName(1),
Icon: "/emojis/flag.png",
URL: "https://" + namesgenerator.GetRandomName(1) + ".com",
WildcardHostname: "*.sub.example.com",
})
require.NoError(t, err)
err = client.DeleteWorkspaceProxyByID(ctx, proxyRes.Proxy.ID)
require.NoError(t, err, "failed to delete workspace proxy")
proxies, err := client.WorkspaceProxies(ctx)
require.NoError(t, err)
require.Len(t, proxies, 0)
})
}
func TestIssueSignedAppToken(t *testing.T) {