mirror of https://github.com/coder/coder.git
chore: Remove interface from coderd and lift API surface (#1772)
Abstracting coderd into an interface added misdirection because the interface was never intended to be fulfilled outside of a single implementation. This lifts the abstraction, and attaches all handlers to a root struct named `*coderd.API`.
This commit is contained in:
parent
c78f947e09
commit
31b819e83f
|
@ -273,7 +273,7 @@ func server() *cobra.Command {
|
|||
}
|
||||
}
|
||||
|
||||
coderDaemon := coderd.New(options)
|
||||
coderAPI := coderd.New(options)
|
||||
client := codersdk.New(localURL)
|
||||
if tlsEnable {
|
||||
// Secure transport isn't needed for locally communicating!
|
||||
|
@ -299,7 +299,7 @@ func server() *cobra.Command {
|
|||
errCh := make(chan error, 1)
|
||||
provisionerDaemons := make([]*provisionerd.Server, 0)
|
||||
for i := 0; uint8(i) < provisionerDaemonCount; i++ {
|
||||
daemonClose, err := newProvisionerDaemon(cmd.Context(), coderDaemon, logger, cacheDir, errCh, dev)
|
||||
daemonClose, err := newProvisionerDaemon(cmd.Context(), coderAPI, logger, cacheDir, errCh, dev)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create provisioner daemon: %w", err)
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ func server() *cobra.Command {
|
|||
// These errors are typically noise like "TLS: EOF". Vault does similar:
|
||||
// https://github.com/hashicorp/vault/blob/e2490059d0711635e529a4efcbaa1b26998d6e1c/command/server.go#L2714
|
||||
ErrorLog: log.New(io.Discard, "", 0),
|
||||
Handler: coderDaemon.Handler(),
|
||||
Handler: coderAPI.Handler,
|
||||
BaseContext: func(_ net.Listener) context.Context {
|
||||
return shutdownConnsCtx
|
||||
},
|
||||
|
@ -387,7 +387,7 @@ func server() *cobra.Command {
|
|||
signal.Notify(stopChan, os.Interrupt)
|
||||
select {
|
||||
case <-cmd.Context().Done():
|
||||
coderDaemon.CloseWait()
|
||||
coderAPI.Close()
|
||||
return cmd.Context().Err()
|
||||
case err := <-tunnelErrChan:
|
||||
if err != nil {
|
||||
|
@ -395,7 +395,7 @@ func server() *cobra.Command {
|
|||
}
|
||||
case err := <-errCh:
|
||||
shutdownConns()
|
||||
coderDaemon.CloseWait()
|
||||
coderAPI.Close()
|
||||
return err
|
||||
case <-stopChan:
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ func server() *cobra.Command {
|
|||
|
||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), cliui.Styles.Prompt.String()+"Waiting for WebSocket connections to close...\n")
|
||||
shutdownConns()
|
||||
coderDaemon.CloseWait()
|
||||
coderAPI.Close()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@ -555,7 +555,7 @@ func createFirstUser(cmd *cobra.Command, client *codersdk.Client, cfg config.Roo
|
|||
}
|
||||
|
||||
// nolint:revive
|
||||
func newProvisionerDaemon(ctx context.Context, coderDaemon coderd.CoderD,
|
||||
func newProvisionerDaemon(ctx context.Context, coderAPI *coderd.API,
|
||||
logger slog.Logger, cacheDir string, errChan chan error, dev bool) (*provisionerd.Server, error) {
|
||||
err := os.MkdirAll(cacheDir, 0700)
|
||||
if err != nil {
|
||||
|
@ -595,7 +595,7 @@ func newProvisionerDaemon(ctx context.Context, coderDaemon coderd.CoderD,
|
|||
}()
|
||||
provisioners[string(database.ProvisionerTypeEcho)] = proto.NewDRPCProvisionerClient(provisionersdk.Conn(echoClient))
|
||||
}
|
||||
return provisionerd.New(coderDaemon.ListenProvisionerDaemon, &provisionerd.Options{
|
||||
return provisionerd.New(coderAPI.ListenProvisionerDaemon, &provisionerd.Options{
|
||||
Logger: logger,
|
||||
PollInterval: 500 * time.Millisecond,
|
||||
UpdateInterval: 500 * time.Millisecond,
|
||||
|
|
|
@ -12,12 +12,12 @@ import (
|
|||
"github.com/coder/coder/coderd/rbac"
|
||||
)
|
||||
|
||||
func AuthorizeFilter[O rbac.Objecter](api *api, r *http.Request, action rbac.Action, objects []O) []O {
|
||||
func AuthorizeFilter[O rbac.Objecter](api *API, r *http.Request, action rbac.Action, objects []O) []O {
|
||||
roles := httpmw.UserRoles(r)
|
||||
return rbac.Filter(r.Context(), api.Authorizer, roles.ID.String(), roles.Roles, action, objects)
|
||||
}
|
||||
|
||||
func (api *api) Authorize(rw http.ResponseWriter, r *http.Request, action rbac.Action, object rbac.Objecter) bool {
|
||||
func (api *API) Authorize(rw http.ResponseWriter, r *http.Request, action rbac.Action, object rbac.Objecter) bool {
|
||||
roles := httpmw.UserRoles(r)
|
||||
err := api.Authorizer.ByRoleName(r.Context(), roles.ID.String(), roles.Roles, action, object.RBACObject())
|
||||
if err != nil {
|
||||
|
|
226
coderd/coderd.go
226
coderd/coderd.go
|
@ -28,7 +28,6 @@ import (
|
|||
"github.com/coder/coder/coderd/tracing"
|
||||
"github.com/coder/coder/coderd/turnconn"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/provisionerd/proto"
|
||||
"github.com/coder/coder/site"
|
||||
)
|
||||
|
||||
|
@ -56,22 +55,8 @@ type Options struct {
|
|||
TracerProvider *sdktrace.TracerProvider
|
||||
}
|
||||
|
||||
type CoderD interface {
|
||||
Handler() http.Handler
|
||||
CloseWait()
|
||||
|
||||
// An in-process provisionerd connection.
|
||||
ListenProvisionerDaemon(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error)
|
||||
}
|
||||
|
||||
type coderD struct {
|
||||
api *api
|
||||
router chi.Router
|
||||
options *Options
|
||||
}
|
||||
|
||||
// newRouter constructs the Chi Router for the given API.
|
||||
func newRouter(options *Options, a *api) chi.Router {
|
||||
// New constructs a Coder API handler.
|
||||
func New(options *Options) *API {
|
||||
if options.AgentConnectionUpdateFrequency == 0 {
|
||||
options.AgentConnectionUpdateFrequency = 3 * time.Second
|
||||
}
|
||||
|
@ -87,15 +72,19 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
panic(xerrors.Errorf("rego authorize panic: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
r := chi.NewRouter()
|
||||
api := &API{
|
||||
Options: options,
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
apiKeyMiddleware := httpmw.ExtractAPIKey(options.Database, &httpmw.OAuth2Configs{
|
||||
Github: options.GithubOAuth2Config,
|
||||
})
|
||||
|
||||
// TODO: @emyrk we should just move this into 'ExtractAPIKey'.
|
||||
authRolesMiddleware := httpmw.ExtractUserRoles(options.Database)
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Use(
|
||||
func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -103,7 +92,7 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
})
|
||||
},
|
||||
httpmw.Prometheus,
|
||||
tracing.HTTPMW(a.TracerProvider, "coderd.http"),
|
||||
tracing.HTTPMW(api.TracerProvider, "coderd.http"),
|
||||
)
|
||||
|
||||
r.Route("/api/v2", func(r chi.Router) {
|
||||
|
@ -116,7 +105,7 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
r.Use(
|
||||
// Specific routes can specify smaller limits.
|
||||
httpmw.RateLimitPerMinute(options.APIRateLimit),
|
||||
debugLogRequest(a.Logger),
|
||||
debugLogRequest(api.Logger),
|
||||
)
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(w, http.StatusOK, httpapi.Response{
|
||||
|
@ -139,8 +128,8 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
// file content is expensive so it should be small.
|
||||
httpmw.RateLimitPerMinute(12),
|
||||
)
|
||||
r.Get("/{hash}", a.fileByHash)
|
||||
r.Post("/", a.postFile)
|
||||
r.Get("/{hash}", api.fileByHash)
|
||||
r.Post("/", api.postFile)
|
||||
})
|
||||
r.Route("/organizations/{organization}", func(r chi.Router) {
|
||||
r.Use(
|
||||
|
@ -148,40 +137,40 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
httpmw.ExtractOrganizationParam(options.Database),
|
||||
authRolesMiddleware,
|
||||
)
|
||||
r.Get("/", a.organization)
|
||||
r.Get("/provisionerdaemons", a.provisionerDaemonsByOrganization)
|
||||
r.Post("/templateversions", a.postTemplateVersionsByOrganization)
|
||||
r.Get("/", api.organization)
|
||||
r.Get("/provisionerdaemons", api.provisionerDaemonsByOrganization)
|
||||
r.Post("/templateversions", api.postTemplateVersionsByOrganization)
|
||||
r.Route("/templates", func(r chi.Router) {
|
||||
r.Post("/", a.postTemplateByOrganization)
|
||||
r.Get("/", a.templatesByOrganization)
|
||||
r.Get("/{templatename}", a.templateByOrganizationAndName)
|
||||
r.Post("/", api.postTemplateByOrganization)
|
||||
r.Get("/", api.templatesByOrganization)
|
||||
r.Get("/{templatename}", api.templateByOrganizationAndName)
|
||||
})
|
||||
r.Route("/workspaces", func(r chi.Router) {
|
||||
r.Post("/", a.postWorkspacesByOrganization)
|
||||
r.Get("/", a.workspacesByOrganization)
|
||||
r.Post("/", api.postWorkspacesByOrganization)
|
||||
r.Get("/", api.workspacesByOrganization)
|
||||
r.Route("/{user}", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractUserParam(options.Database))
|
||||
r.Get("/{workspacename}", a.workspaceByOwnerAndName)
|
||||
r.Get("/", a.workspacesByOwner)
|
||||
r.Get("/{workspacename}", api.workspaceByOwnerAndName)
|
||||
r.Get("/", api.workspacesByOwner)
|
||||
})
|
||||
})
|
||||
r.Route("/members", func(r chi.Router) {
|
||||
r.Get("/roles", a.assignableOrgRoles)
|
||||
r.Get("/roles", api.assignableOrgRoles)
|
||||
r.Route("/{user}", func(r chi.Router) {
|
||||
r.Use(
|
||||
httpmw.ExtractUserParam(options.Database),
|
||||
httpmw.ExtractOrganizationMemberParam(options.Database),
|
||||
)
|
||||
r.Put("/roles", a.putMemberRoles)
|
||||
r.Put("/roles", api.putMemberRoles)
|
||||
})
|
||||
})
|
||||
})
|
||||
r.Route("/parameters/{scope}/{id}", func(r chi.Router) {
|
||||
r.Use(apiKeyMiddleware)
|
||||
r.Post("/", a.postParameter)
|
||||
r.Get("/", a.parameters)
|
||||
r.Post("/", api.postParameter)
|
||||
r.Get("/", api.parameters)
|
||||
r.Route("/{name}", func(r chi.Router) {
|
||||
r.Delete("/", a.deleteParameter)
|
||||
r.Delete("/", api.deleteParameter)
|
||||
})
|
||||
})
|
||||
r.Route("/templates/{template}", func(r chi.Router) {
|
||||
|
@ -191,12 +180,12 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
httpmw.ExtractTemplateParam(options.Database),
|
||||
)
|
||||
|
||||
r.Get("/", a.template)
|
||||
r.Delete("/", a.deleteTemplate)
|
||||
r.Get("/", api.template)
|
||||
r.Delete("/", api.deleteTemplate)
|
||||
r.Route("/versions", func(r chi.Router) {
|
||||
r.Get("/", a.templateVersionsByTemplate)
|
||||
r.Patch("/", a.patchActiveTemplateVersion)
|
||||
r.Get("/{templateversionname}", a.templateVersionByName)
|
||||
r.Get("/", api.templateVersionsByTemplate)
|
||||
r.Patch("/", api.patchActiveTemplateVersion)
|
||||
r.Get("/{templateversionname}", api.templateVersionByName)
|
||||
})
|
||||
})
|
||||
r.Route("/templateversions/{templateversion}", func(r chi.Router) {
|
||||
|
@ -206,23 +195,23 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
httpmw.ExtractTemplateVersionParam(options.Database),
|
||||
)
|
||||
|
||||
r.Get("/", a.templateVersion)
|
||||
r.Patch("/cancel", a.patchCancelTemplateVersion)
|
||||
r.Get("/schema", a.templateVersionSchema)
|
||||
r.Get("/parameters", a.templateVersionParameters)
|
||||
r.Get("/resources", a.templateVersionResources)
|
||||
r.Get("/logs", a.templateVersionLogs)
|
||||
r.Get("/", api.templateVersion)
|
||||
r.Patch("/cancel", api.patchCancelTemplateVersion)
|
||||
r.Get("/schema", api.templateVersionSchema)
|
||||
r.Get("/parameters", api.templateVersionParameters)
|
||||
r.Get("/resources", api.templateVersionResources)
|
||||
r.Get("/logs", api.templateVersionLogs)
|
||||
})
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.Get("/first", a.firstUser)
|
||||
r.Post("/first", a.postFirstUser)
|
||||
r.Post("/login", a.postLogin)
|
||||
r.Post("/logout", a.postLogout)
|
||||
r.Get("/authmethods", a.userAuthMethods)
|
||||
r.Get("/first", api.firstUser)
|
||||
r.Post("/first", api.postFirstUser)
|
||||
r.Post("/login", api.postLogin)
|
||||
r.Post("/logout", api.postLogout)
|
||||
r.Get("/authmethods", api.userAuthMethods)
|
||||
r.Route("/oauth2", func(r chi.Router) {
|
||||
r.Route("/github", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractOAuth2(options.GithubOAuth2Config))
|
||||
r.Get("/callback", a.userOAuth2Github)
|
||||
r.Get("/callback", api.userOAuth2Github)
|
||||
})
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
|
@ -230,62 +219,62 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
apiKeyMiddleware,
|
||||
authRolesMiddleware,
|
||||
)
|
||||
r.Post("/", a.postUser)
|
||||
r.Get("/", a.users)
|
||||
r.Post("/", api.postUser)
|
||||
r.Get("/", api.users)
|
||||
// These routes query information about site wide roles.
|
||||
r.Route("/roles", func(r chi.Router) {
|
||||
r.Get("/", a.assignableSiteRoles)
|
||||
r.Get("/", api.assignableSiteRoles)
|
||||
})
|
||||
r.Route("/{user}", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractUserParam(options.Database))
|
||||
r.Get("/", a.userByName)
|
||||
r.Put("/profile", a.putUserProfile)
|
||||
r.Get("/", api.userByName)
|
||||
r.Put("/profile", api.putUserProfile)
|
||||
r.Route("/status", func(r chi.Router) {
|
||||
r.Put("/suspend", a.putUserStatus(database.UserStatusSuspended))
|
||||
r.Put("/active", a.putUserStatus(database.UserStatusActive))
|
||||
r.Put("/suspend", api.putUserStatus(database.UserStatusSuspended))
|
||||
r.Put("/active", api.putUserStatus(database.UserStatusActive))
|
||||
})
|
||||
r.Route("/password", func(r chi.Router) {
|
||||
r.Put("/", a.putUserPassword)
|
||||
r.Put("/", api.putUserPassword)
|
||||
})
|
||||
// These roles apply to the site wide permissions.
|
||||
r.Put("/roles", a.putUserRoles)
|
||||
r.Get("/roles", a.userRoles)
|
||||
r.Put("/roles", api.putUserRoles)
|
||||
r.Get("/roles", api.userRoles)
|
||||
|
||||
r.Post("/authorization", a.checkPermissions)
|
||||
r.Post("/authorization", api.checkPermissions)
|
||||
|
||||
r.Post("/keys", a.postAPIKey)
|
||||
r.Post("/keys", api.postAPIKey)
|
||||
r.Route("/organizations", func(r chi.Router) {
|
||||
r.Post("/", a.postOrganizationsByUser)
|
||||
r.Get("/", a.organizationsByUser)
|
||||
r.Get("/{organizationname}", a.organizationByUserAndName)
|
||||
r.Post("/", api.postOrganizationsByUser)
|
||||
r.Get("/", api.organizationsByUser)
|
||||
r.Get("/{organizationname}", api.organizationByUserAndName)
|
||||
})
|
||||
r.Get("/gitsshkey", a.gitSSHKey)
|
||||
r.Put("/gitsshkey", a.regenerateGitSSHKey)
|
||||
r.Get("/gitsshkey", api.gitSSHKey)
|
||||
r.Put("/gitsshkey", api.regenerateGitSSHKey)
|
||||
})
|
||||
})
|
||||
})
|
||||
r.Route("/workspaceagents", func(r chi.Router) {
|
||||
r.Post("/azure-instance-identity", a.postWorkspaceAuthAzureInstanceIdentity)
|
||||
r.Post("/aws-instance-identity", a.postWorkspaceAuthAWSInstanceIdentity)
|
||||
r.Post("/google-instance-identity", a.postWorkspaceAuthGoogleInstanceIdentity)
|
||||
r.Post("/azure-instance-identity", api.postWorkspaceAuthAzureInstanceIdentity)
|
||||
r.Post("/aws-instance-identity", api.postWorkspaceAuthAWSInstanceIdentity)
|
||||
r.Post("/google-instance-identity", api.postWorkspaceAuthGoogleInstanceIdentity)
|
||||
r.Route("/me", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractWorkspaceAgent(options.Database))
|
||||
r.Get("/metadata", a.workspaceAgentMetadata)
|
||||
r.Get("/listen", a.workspaceAgentListen)
|
||||
r.Get("/gitsshkey", a.agentGitSSHKey)
|
||||
r.Get("/turn", a.workspaceAgentTurn)
|
||||
r.Get("/iceservers", a.workspaceAgentICEServers)
|
||||
r.Get("/metadata", api.workspaceAgentMetadata)
|
||||
r.Get("/listen", api.workspaceAgentListen)
|
||||
r.Get("/gitsshkey", api.agentGitSSHKey)
|
||||
r.Get("/turn", api.workspaceAgentTurn)
|
||||
r.Get("/iceservers", api.workspaceAgentICEServers)
|
||||
})
|
||||
r.Route("/{workspaceagent}", func(r chi.Router) {
|
||||
r.Use(
|
||||
apiKeyMiddleware,
|
||||
httpmw.ExtractWorkspaceAgentParam(options.Database),
|
||||
)
|
||||
r.Get("/", a.workspaceAgent)
|
||||
r.Get("/dial", a.workspaceAgentDial)
|
||||
r.Get("/turn", a.workspaceAgentTurn)
|
||||
r.Get("/pty", a.workspaceAgentPTY)
|
||||
r.Get("/iceservers", a.workspaceAgentICEServers)
|
||||
r.Get("/", api.workspaceAgent)
|
||||
r.Get("/dial", api.workspaceAgentDial)
|
||||
r.Get("/turn", api.workspaceAgentTurn)
|
||||
r.Get("/pty", api.workspaceAgentPTY)
|
||||
r.Get("/iceservers", api.workspaceAgentICEServers)
|
||||
})
|
||||
})
|
||||
r.Route("/workspaceresources/{workspaceresource}", func(r chi.Router) {
|
||||
|
@ -295,31 +284,31 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
httpmw.ExtractWorkspaceResourceParam(options.Database),
|
||||
httpmw.ExtractWorkspaceParam(options.Database),
|
||||
)
|
||||
r.Get("/", a.workspaceResource)
|
||||
r.Get("/", api.workspaceResource)
|
||||
})
|
||||
r.Route("/workspaces", func(r chi.Router) {
|
||||
r.Use(
|
||||
apiKeyMiddleware,
|
||||
authRolesMiddleware,
|
||||
)
|
||||
r.Get("/", a.workspaces)
|
||||
r.Get("/", api.workspaces)
|
||||
r.Route("/{workspace}", func(r chi.Router) {
|
||||
r.Use(
|
||||
httpmw.ExtractWorkspaceParam(options.Database),
|
||||
)
|
||||
r.Get("/", a.workspace)
|
||||
r.Get("/", api.workspace)
|
||||
r.Route("/builds", func(r chi.Router) {
|
||||
r.Get("/", a.workspaceBuilds)
|
||||
r.Post("/", a.postWorkspaceBuilds)
|
||||
r.Get("/{workspacebuildname}", a.workspaceBuildByName)
|
||||
r.Get("/", api.workspaceBuilds)
|
||||
r.Post("/", api.postWorkspaceBuilds)
|
||||
r.Get("/{workspacebuildname}", api.workspaceBuildByName)
|
||||
})
|
||||
r.Route("/autostart", func(r chi.Router) {
|
||||
r.Put("/", a.putWorkspaceAutostart)
|
||||
r.Put("/", api.putWorkspaceAutostart)
|
||||
})
|
||||
r.Route("/ttl", func(r chi.Router) {
|
||||
r.Put("/", a.putWorkspaceTTL)
|
||||
r.Put("/", api.putWorkspaceTTL)
|
||||
})
|
||||
r.Get("/watch", a.watchWorkspace)
|
||||
r.Get("/watch", api.watchWorkspace)
|
||||
})
|
||||
})
|
||||
r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) {
|
||||
|
@ -329,48 +318,33 @@ func newRouter(options *Options, a *api) chi.Router {
|
|||
httpmw.ExtractWorkspaceBuildParam(options.Database),
|
||||
httpmw.ExtractWorkspaceParam(options.Database),
|
||||
)
|
||||
r.Get("/", a.workspaceBuild)
|
||||
r.Patch("/cancel", a.patchCancelWorkspaceBuild)
|
||||
r.Get("/logs", a.workspaceBuildLogs)
|
||||
r.Get("/resources", a.workspaceBuildResources)
|
||||
r.Get("/state", a.workspaceBuildState)
|
||||
r.Get("/", api.workspaceBuild)
|
||||
r.Patch("/cancel", api.patchCancelWorkspaceBuild)
|
||||
r.Get("/logs", api.workspaceBuildLogs)
|
||||
r.Get("/resources", api.workspaceBuildResources)
|
||||
r.Get("/state", api.workspaceBuildState)
|
||||
})
|
||||
})
|
||||
|
||||
var _ = xerrors.New("test")
|
||||
|
||||
r.NotFound(site.DefaultHandler().ServeHTTP)
|
||||
return r
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
func New(options *Options) CoderD {
|
||||
a := &api{Options: options}
|
||||
return &coderD{
|
||||
api: a,
|
||||
router: newRouter(options, a),
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *coderD) CloseWait() {
|
||||
c.api.websocketWaitMutex.Lock()
|
||||
c.api.websocketWaitGroup.Wait()
|
||||
c.api.websocketWaitMutex.Unlock()
|
||||
}
|
||||
|
||||
func (c *coderD) Handler() http.Handler {
|
||||
return c.router
|
||||
}
|
||||
|
||||
// API contains all route handlers. Only HTTP handlers should
|
||||
// be added to this struct for code clarity.
|
||||
type api struct {
|
||||
type API struct {
|
||||
*Options
|
||||
|
||||
Handler chi.Router
|
||||
websocketWaitMutex sync.Mutex
|
||||
websocketWaitGroup sync.WaitGroup
|
||||
}
|
||||
|
||||
// Close waits for all WebSocket connections to drain before returning.
|
||||
func (api *API) Close() {
|
||||
api.websocketWaitMutex.Lock()
|
||||
api.websocketWaitGroup.Wait()
|
||||
api.websocketWaitMutex.Unlock()
|
||||
}
|
||||
|
||||
func debugLogRequest(log slog.Logger) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -40,7 +40,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
|
||||
authorizer := &fakeAuthorizer{}
|
||||
srv, client, _ := coderdtest.NewWithServer(t, &coderdtest.Options{
|
||||
client, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
|
||||
Authorizer: authorizer,
|
||||
IncludeProvisionerD: true,
|
||||
})
|
||||
|
@ -267,8 +267,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
|
|||
assertRoute[noTrailSlash] = v
|
||||
}
|
||||
|
||||
c, _ := srv.Config.Handler.(*chi.Mux)
|
||||
err = chi.Walk(c, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
|
||||
err = chi.Walk(api.Handler, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
|
||||
name := method + ":" + route
|
||||
t.Run(name, func(t *testing.T) {
|
||||
authorizer.reset()
|
||||
|
|
|
@ -69,16 +69,14 @@ type Options struct {
|
|||
IncludeProvisionerD bool
|
||||
}
|
||||
|
||||
// New constructs an in-memory coderd instance and returns
|
||||
// the connected client.
|
||||
// New constructs a codersdk client connected to an in-memory API instance.
|
||||
func New(t *testing.T, options *Options) *codersdk.Client {
|
||||
_, cli, _ := NewWithServer(t, options)
|
||||
return cli
|
||||
client, _ := NewWithAPI(t, options)
|
||||
return client
|
||||
}
|
||||
|
||||
// NewWithServer returns an in-memory coderd instance and
|
||||
// the HTTP server it started with.
|
||||
func NewWithServer(t *testing.T, options *Options) (*httptest.Server, *codersdk.Client, coderd.CoderD) {
|
||||
// NewWithAPI constructs a codersdk client connected to the returned in-memory API instance.
|
||||
func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, *coderd.API) {
|
||||
if options == nil {
|
||||
options = &Options{}
|
||||
}
|
||||
|
@ -144,7 +142,7 @@ func NewWithServer(t *testing.T, options *Options) (*httptest.Server, *codersdk.
|
|||
require.NoError(t, err)
|
||||
|
||||
// We set the handler after server creation for the access URL.
|
||||
coderDaemon := coderd.New(&coderd.Options{
|
||||
coderAPI := coderd.New(&coderd.Options{
|
||||
AgentConnectionUpdateFrequency: 150 * time.Millisecond,
|
||||
AccessURL: serverURL,
|
||||
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
|
||||
|
@ -160,24 +158,24 @@ func NewWithServer(t *testing.T, options *Options) (*httptest.Server, *codersdk.
|
|||
APIRateLimit: options.APIRateLimit,
|
||||
Authorizer: options.Authorizer,
|
||||
})
|
||||
srv.Config.Handler = coderDaemon.Handler()
|
||||
srv.Config.Handler = coderAPI.Handler
|
||||
if options.IncludeProvisionerD {
|
||||
_ = NewProvisionerDaemon(t, coderDaemon)
|
||||
_ = NewProvisionerDaemon(t, coderAPI)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
cancelFunc()
|
||||
_ = turnServer.Close()
|
||||
srv.Close()
|
||||
coderDaemon.CloseWait()
|
||||
coderAPI.Close()
|
||||
})
|
||||
|
||||
return srv, codersdk.New(serverURL), coderDaemon
|
||||
return codersdk.New(serverURL), coderAPI
|
||||
}
|
||||
|
||||
// NewProvisionerDaemon launches a provisionerd instance configured to work
|
||||
// well with coderd testing. It registers the "echo" provisioner for
|
||||
// quick testing.
|
||||
func NewProvisionerDaemon(t *testing.T, coderDaemon coderd.CoderD) io.Closer {
|
||||
func NewProvisionerDaemon(t *testing.T, coderAPI *coderd.API) io.Closer {
|
||||
echoClient, echoServer := provisionersdk.TransportPipe()
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
t.Cleanup(func() {
|
||||
|
@ -192,7 +190,7 @@ func NewProvisionerDaemon(t *testing.T, coderDaemon coderd.CoderD) io.Closer {
|
|||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
closer := provisionerd.New(coderDaemon.ListenProvisionerDaemon, &provisionerd.Options{
|
||||
closer := provisionerd.New(coderAPI.ListenProvisionerDaemon, &provisionerd.Options{
|
||||
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
|
||||
PollInterval: 50 * time.Millisecond,
|
||||
UpdateInterval: 250 * time.Millisecond,
|
||||
|
|
|
@ -14,9 +14,9 @@ func TestMain(m *testing.M) {
|
|||
|
||||
func TestNew(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
closer := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
closer := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) postFile(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
// This requires the site wide action to create files.
|
||||
// Once created, a user can read their own files uploaded
|
||||
|
@ -74,7 +74,7 @@ func (api *api) postFile(rw http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
}
|
||||
|
||||
func (api *api) fileByHash(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) fileByHash(rw http.ResponseWriter, r *http.Request) {
|
||||
hash := chi.URLParam(r, "hash")
|
||||
if hash == "" {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionUpdate, rbac.ResourceUserData.WithOwner(user.ID.String())) {
|
||||
|
@ -57,7 +57,7 @@ func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
}
|
||||
|
||||
func (api *api) gitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) gitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceUserData.WithOwner(user.ID.String())) {
|
||||
|
@ -81,7 +81,7 @@ func (api *api) gitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
}
|
||||
|
||||
func (api *api) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
agent := httpmw.WorkspaceAgent(r)
|
||||
resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), agent.ResourceID)
|
||||
if err != nil {
|
||||
|
|
|
@ -79,9 +79,9 @@ func TestGitSSHKey(t *testing.T) {
|
|||
func TestAgentGitSSHKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) putMemberRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) putMemberRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
member := httpmw.OrganizationMemberParam(r)
|
||||
|
@ -55,7 +55,7 @@ func (api *api) putMemberRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertOrganizationMember(updatedUser))
|
||||
}
|
||||
|
||||
func (api *api) updateOrganizationMemberRoles(ctx context.Context, args database.UpdateMemberRolesParams) (database.OrganizationMember, error) {
|
||||
func (api *API) updateOrganizationMemberRoles(ctx context.Context, args database.UpdateMemberRolesParams) (database.OrganizationMember, error) {
|
||||
// Enforce only site wide roles
|
||||
for _, r := range args.GrantedRoles {
|
||||
// Must be an org role for the org in the args
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) organization(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) organization(rw http.ResponseWriter, r *http.Request) {
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceOrganization.
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) postParameter(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postParameter(rw http.ResponseWriter, r *http.Request) {
|
||||
var createRequest codersdk.CreateParameterRequest
|
||||
if !httpapi.Read(rw, r, &createRequest) {
|
||||
return
|
||||
|
@ -63,7 +63,7 @@ func (api *api) postParameter(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusCreated, convertParameterValue(parameterValue))
|
||||
}
|
||||
|
||||
func (api *api) parameters(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) parameters(rw http.ResponseWriter, r *http.Request) {
|
||||
scope, scopeID, valid := readScopeAndID(rw, r)
|
||||
if !valid {
|
||||
return
|
||||
|
@ -89,7 +89,7 @@ func (api *api) parameters(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, apiParameterValues)
|
||||
}
|
||||
|
||||
func (api *api) deleteParameter(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) deleteParameter(rw http.ResponseWriter, r *http.Request) {
|
||||
scope, scopeID, valid := readScopeAndID(rw, r)
|
||||
if !valid {
|
||||
return
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
sdkproto "github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func (api *api) provisionerDaemonsByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) provisionerDaemonsByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
daemons, err := api.Database.GetProvisionerDaemons(r.Context())
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
|
@ -49,7 +49,7 @@ func (api *api) provisionerDaemonsByOrganization(rw http.ResponseWriter, r *http
|
|||
|
||||
// ListenProvisionerDaemon is an in-memory connection to a provisionerd. Useful when starting coderd and provisionerd
|
||||
// in the same process.
|
||||
func (c *coderD) ListenProvisionerDaemon(ctx context.Context) (client proto.DRPCProvisionerDaemonClient, err error) {
|
||||
func (api *API) ListenProvisionerDaemon(ctx context.Context) (client proto.DRPCProvisionerDaemonClient, err error) {
|
||||
clientSession, serverSession := provisionersdk.TransportPipe()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
|
@ -58,7 +58,7 @@ func (c *coderD) ListenProvisionerDaemon(ctx context.Context) (client proto.DRPC
|
|||
}
|
||||
}()
|
||||
|
||||
daemon, err := c.api.Database.InsertProvisionerDaemon(ctx, database.InsertProvisionerDaemonParams{
|
||||
daemon, err := api.Database.InsertProvisionerDaemon(ctx, database.InsertProvisionerDaemonParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: database.Now(),
|
||||
Name: namesgenerator.GetRandomName(1),
|
||||
|
@ -70,12 +70,12 @@ func (c *coderD) ListenProvisionerDaemon(ctx context.Context) (client proto.DRPC
|
|||
|
||||
mux := drpcmux.New()
|
||||
err = proto.DRPCRegisterProvisionerDaemon(mux, &provisionerdServer{
|
||||
AccessURL: c.options.AccessURL,
|
||||
AccessURL: api.AccessURL,
|
||||
ID: daemon.ID,
|
||||
Database: c.options.Database,
|
||||
Pubsub: c.options.Pubsub,
|
||||
Database: api.Database,
|
||||
Pubsub: api.Pubsub,
|
||||
Provisioners: daemon.Provisioners,
|
||||
Logger: c.options.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)),
|
||||
Logger: api.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -85,13 +85,13 @@ func (c *coderD) ListenProvisionerDaemon(ctx context.Context) (client proto.DRPC
|
|||
if xerrors.Is(err, io.EOF) {
|
||||
return
|
||||
}
|
||||
c.options.Logger.Debug(ctx, "drpc server error", slog.Error(err))
|
||||
api.Logger.Debug(ctx, "drpc server error", slog.Error(err))
|
||||
},
|
||||
})
|
||||
go func() {
|
||||
err = server.Serve(ctx, serverSession)
|
||||
if err != nil && !xerrors.Is(err, io.EOF) {
|
||||
c.options.Logger.Debug(ctx, "provisioner daemon disconnected", slog.Error(err))
|
||||
api.Logger.Debug(ctx, "provisioner daemon disconnected", slog.Error(err))
|
||||
}
|
||||
// close the sessions so we don't leak goroutines serving them.
|
||||
_ = clientSession.Close()
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
// 2. GET /logs?after=<timestamp>&follow
|
||||
// The combination of these responses should provide all current logs
|
||||
// to the consumer, and future logs are streamed in the follow request.
|
||||
func (api *api) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job database.ProvisionerJob) {
|
||||
func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job database.ProvisionerJob) {
|
||||
follow := r.URL.Query().Has("follow")
|
||||
afterRaw := r.URL.Query().Get("after")
|
||||
beforeRaw := r.URL.Query().Get("before")
|
||||
|
@ -178,7 +178,7 @@ func (api *api) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job
|
|||
}
|
||||
}
|
||||
|
||||
func (api *api) provisionerJobResources(rw http.ResponseWriter, r *http.Request, job database.ProvisionerJob) {
|
||||
func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, job database.ProvisionerJob) {
|
||||
if !job.CompletedAt.Valid {
|
||||
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
|
||||
Message: "Job hasn't completed!",
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// assignableSiteRoles returns all site wide roles that can be assigned.
|
||||
func (api *api) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
// TODO: @emyrk in the future, allow granular subsets of roles to be returned based on the
|
||||
// role of the user.
|
||||
|
||||
|
@ -24,7 +24,7 @@ func (api *api) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// assignableSiteRoles returns all site wide roles that can be assigned.
|
||||
func (api *api) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
// TODO: @emyrk in the future, allow granular subsets of roles to be returned based on the
|
||||
// role of the user.
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
|
@ -37,7 +37,7 @@ func (api *api) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertRoles(roles))
|
||||
}
|
||||
|
||||
func (api *api) checkPermissions(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) checkPermissions(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceUser.WithOwner(user.ID.String())) {
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
)
|
||||
|
||||
// Returns a single template.
|
||||
func (api *api) template(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) template(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
|
||||
workspaceCounts, err := api.Database.GetWorkspaceOwnerCountsByTemplateIDs(r.Context(), []uuid.UUID{template.ID})
|
||||
|
@ -44,7 +44,7 @@ func (api *api) template(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertTemplate(template, count))
|
||||
}
|
||||
|
||||
func (api *api) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionDelete, template) {
|
||||
return
|
||||
|
@ -84,7 +84,7 @@ func (api *api) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// Create a new template in an organization.
|
||||
func (api *api) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
var createTemplate codersdk.CreateTemplateRequest
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionCreate, rbac.ResourceTemplate.InOrg(organization.ID)) {
|
||||
|
@ -193,7 +193,7 @@ func (api *api) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
|
|||
httpapi.Write(rw, http.StatusCreated, template)
|
||||
}
|
||||
|
||||
func (api *api) templatesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
templates, err := api.Database.GetTemplatesByOrganization(r.Context(), database.GetTemplatesByOrganizationParams{
|
||||
OrganizationID: organization.ID,
|
||||
|
@ -230,7 +230,7 @@ func (api *api) templatesByOrganization(rw http.ResponseWriter, r *http.Request)
|
|||
httpapi.Write(rw, http.StatusOK, convertTemplates(templates, workspaceCounts))
|
||||
}
|
||||
|
||||
func (api *api) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
templateName := chi.URLParam(r, "templatename")
|
||||
template, err := api.Database.GetTemplateByOrganizationAndName(r.Context(), database.GetTemplateByOrganizationAndNameParams{
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) templateVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templateVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
templateVersion := httpmw.TemplateVersionParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, templateVersion) {
|
||||
return
|
||||
|
@ -36,7 +36,7 @@ func (api *api) templateVersion(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertTemplateVersion(templateVersion, convertProvisionerJob(job)))
|
||||
}
|
||||
|
||||
func (api *api) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
templateVersion := httpmw.TemplateVersionParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionUpdate, templateVersion) {
|
||||
return
|
||||
|
@ -79,7 +79,7 @@ func (api *api) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Reque
|
|||
})
|
||||
}
|
||||
|
||||
func (api *api) templateVersionSchema(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) {
|
||||
templateVersion := httpmw.TemplateVersionParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, templateVersion) {
|
||||
return
|
||||
|
@ -122,7 +122,7 @@ func (api *api) templateVersionSchema(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, apiSchemas)
|
||||
}
|
||||
|
||||
func (api *api) templateVersionParameters(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templateVersionParameters(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
templateVersion := httpmw.TemplateVersionParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, templateVersion) {
|
||||
|
@ -163,7 +163,7 @@ func (api *api) templateVersionParameters(rw http.ResponseWriter, r *http.Reques
|
|||
httpapi.Write(rw, http.StatusOK, values)
|
||||
}
|
||||
|
||||
func (api *api) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, template) {
|
||||
return
|
||||
|
@ -221,7 +221,7 @@ func (api *api) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque
|
|||
httpapi.Write(rw, http.StatusOK, apiVersion)
|
||||
}
|
||||
|
||||
func (api *api) templateVersionByName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, template) {
|
||||
return
|
||||
|
@ -258,7 +258,7 @@ func (api *api) templateVersionByName(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertTemplateVersion(templateVersion, convertProvisionerJob(job)))
|
||||
}
|
||||
|
||||
func (api *api) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionUpdate, template) {
|
||||
return
|
||||
|
@ -303,7 +303,7 @@ func (api *api) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
|
||||
// Creates a new version of a template. An import job is queued to parse the storage method provided.
|
||||
func (api *api) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
var req codersdk.CreateTemplateVersionRequest
|
||||
|
@ -415,7 +415,7 @@ func (api *api) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
|
|||
// provisioned, each resource can have an agent that dials back to coderd.
|
||||
// The agents returned are informative of the template version, and do not
|
||||
// return agents associated with any particular workspace.
|
||||
func (api *api) templateVersionResources(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request) {
|
||||
templateVersion := httpmw.TemplateVersionParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, templateVersion) {
|
||||
return
|
||||
|
@ -435,7 +435,7 @@ func (api *api) templateVersionResources(rw http.ResponseWriter, r *http.Request
|
|||
// template version. These logs are only associated with the template version,
|
||||
// and not any build logs for a workspace.
|
||||
// Eg: Logs returned from 'terraform plan' when uploading a new terraform file.
|
||||
func (api *api) templateVersionLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) templateVersionLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
templateVersion := httpmw.TemplateVersionParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, templateVersion) {
|
||||
return
|
||||
|
|
|
@ -28,14 +28,14 @@ type GithubOAuth2Config struct {
|
|||
AllowOrganizations []string
|
||||
}
|
||||
|
||||
func (api *api) userAuthMethods(rw http.ResponseWriter, _ *http.Request) {
|
||||
func (api *API) userAuthMethods(rw http.ResponseWriter, _ *http.Request) {
|
||||
httpapi.Write(rw, http.StatusOK, codersdk.AuthMethods{
|
||||
Password: true,
|
||||
Github: api.GithubOAuth2Config != nil,
|
||||
})
|
||||
}
|
||||
|
||||
func (api *api) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
|
||||
state := httpmw.OAuth2(r)
|
||||
|
||||
oauthClient := oauth2.NewClient(r.Context(), oauth2.StaticTokenSource(state.Token))
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
)
|
||||
|
||||
// Returns whether the initial user has been created or not.
|
||||
func (api *api) firstUser(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) {
|
||||
userCount, err := api.Database.GetUserCount(r.Context())
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
|
@ -47,7 +47,7 @@ func (api *api) firstUser(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// Creates the initial user for a Coder deployment.
|
||||
func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
|
||||
var createUser codersdk.CreateFirstUserRequest
|
||||
if !httpapi.Read(rw, r, &createUser) {
|
||||
return
|
||||
|
@ -103,7 +103,7 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
}
|
||||
|
||||
func (api *api) users(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) users(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
searchName = r.URL.Query().Get("search")
|
||||
statusFilter = r.URL.Query().Get("status")
|
||||
|
@ -161,7 +161,7 @@ func (api *api) users(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// Creates a new user.
|
||||
func (api *api) postUser(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
|
||||
// Create the user on the site
|
||||
if !api.Authorize(rw, r, rbac.ActionCreate, rbac.ResourceUser) {
|
||||
return
|
||||
|
@ -224,7 +224,7 @@ func (api *api) postUser(rw http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Returns the parameterized user requested. All validation
|
||||
// is completed in the middleware for this route.
|
||||
func (api *api) userByName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) userByName(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
organizationIDs, err := userOrganizationIDs(r.Context(), api, user)
|
||||
|
||||
|
@ -242,7 +242,7 @@ func (api *api) userByName(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertUser(user, organizationIDs))
|
||||
}
|
||||
|
||||
func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionUpdate, rbac.ResourceUser.WithID(user.ID.String())) {
|
||||
|
@ -311,7 +311,7 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile, organizationIDs))
|
||||
}
|
||||
|
||||
func (api *api) putUserStatus(status database.UserStatus) func(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseWriter, r *http.Request) {
|
||||
return func(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
apiKey := httpmw.APIKey(r)
|
||||
|
@ -352,7 +352,7 @@ func (api *api) putUserStatus(status database.UserStatus) func(rw http.ResponseW
|
|||
}
|
||||
}
|
||||
|
||||
func (api *api) putUserPassword(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
user = httpmw.UserParam(r)
|
||||
params codersdk.UpdateUserPasswordRequest
|
||||
|
@ -387,7 +387,7 @@ func (api *api) putUserPassword(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (api *api) userRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) userRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceUserData.
|
||||
|
@ -421,7 +421,7 @@ func (api *api) userRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (api *api) putUserRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
// User is the user to modify
|
||||
user := httpmw.UserParam(r)
|
||||
roles := httpmw.UserRoles(r)
|
||||
|
@ -469,7 +469,7 @@ func (api *api) putUserRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// updateSiteUserRoles will ensure only site wide roles are passed in as arguments.
|
||||
// If an organization role is included, an error is returned.
|
||||
func (api *api) updateSiteUserRoles(ctx context.Context, args database.UpdateUserRolesParams) (database.User, error) {
|
||||
func (api *API) updateSiteUserRoles(ctx context.Context, args database.UpdateUserRolesParams) (database.User, error) {
|
||||
// Enforce only site wide roles
|
||||
for _, r := range args.GrantedRoles {
|
||||
if _, ok := rbac.IsOrgRole(r); ok {
|
||||
|
@ -489,7 +489,7 @@ func (api *api) updateSiteUserRoles(ctx context.Context, args database.UpdateUse
|
|||
}
|
||||
|
||||
// Returns organizations the parameterized user has access to.
|
||||
func (api *api) organizationsByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) organizationsByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
organizations, err := api.Database.GetOrganizationsByUserID(r.Context(), user.ID)
|
||||
|
@ -515,7 +515,7 @@ func (api *api) organizationsByUser(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, publicOrganizations)
|
||||
}
|
||||
|
||||
func (api *api) organizationByUserAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
organizationName := chi.URLParam(r, "organizationname")
|
||||
organization, err := api.Database.GetOrganizationByName(r.Context(), organizationName)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
|
@ -539,7 +539,7 @@ func (api *api) organizationByUserAndName(rw http.ResponseWriter, r *http.Reques
|
|||
httpapi.Write(rw, http.StatusOK, convertOrganization(organization))
|
||||
}
|
||||
|
||||
func (api *api) postOrganizationsByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postOrganizationsByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
var req codersdk.CreateOrganizationRequest
|
||||
if !httpapi.Read(rw, r, &req) {
|
||||
|
@ -605,7 +605,7 @@ func (api *api) postOrganizationsByUser(rw http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
// Authenticates the user with an email and password.
|
||||
func (api *api) postLogin(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) {
|
||||
var loginWithPassword codersdk.LoginWithPasswordRequest
|
||||
if !httpapi.Read(rw, r, &loginWithPassword) {
|
||||
return
|
||||
|
@ -651,7 +651,7 @@ func (api *api) postLogin(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// Creates a new session key, used for logging in via the CLI
|
||||
func (api *api) postAPIKey(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionCreate, rbac.ResourceAPIKey.WithOwner(user.ID.String())) {
|
||||
|
@ -670,7 +670,7 @@ func (api *api) postAPIKey(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// Clear the user's session cookie
|
||||
func (*api) postLogout(rw http.ResponseWriter, _ *http.Request) {
|
||||
func (*API) postLogout(rw http.ResponseWriter, _ *http.Request) {
|
||||
// Get a blank token cookie
|
||||
cookie := &http.Cookie{
|
||||
// MaxAge < 0 means to delete the cookie now
|
||||
|
@ -700,7 +700,7 @@ func generateAPIKeyIDSecret() (id string, secret string, err error) {
|
|||
return id, secret, nil
|
||||
}
|
||||
|
||||
func (api *api) createAPIKey(rw http.ResponseWriter, r *http.Request, params database.InsertAPIKeyParams) (string, bool) {
|
||||
func (api *API) createAPIKey(rw http.ResponseWriter, r *http.Request, params database.InsertAPIKeyParams) (string, bool) {
|
||||
keyID, keySecret, err := generateAPIKeyIDSecret()
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
|
@ -743,7 +743,7 @@ func (api *api) createAPIKey(rw http.ResponseWriter, r *http.Request, params dat
|
|||
return sessionToken, true
|
||||
}
|
||||
|
||||
func (api *api) createUser(ctx context.Context, req codersdk.CreateUserRequest) (database.User, uuid.UUID, error) {
|
||||
func (api *API) createUser(ctx context.Context, req codersdk.CreateUserRequest) (database.User, uuid.UUID, error) {
|
||||
var user database.User
|
||||
return user, req.OrganizationID, api.Database.InTx(func(db database.Store) error {
|
||||
var orgRoles []string
|
||||
|
@ -845,7 +845,7 @@ func convertUsers(users []database.User, organizationIDsByUserID map[uuid.UUID][
|
|||
return converted
|
||||
}
|
||||
|
||||
func userOrganizationIDs(ctx context.Context, api *api, user database.User) ([]uuid.UUID, error) {
|
||||
func userOrganizationIDs(ctx context.Context, api *API, user database.User) ([]uuid.UUID, error) {
|
||||
organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(ctx, []uuid.UUID{user.ID})
|
||||
if errors.Is(err, sql.ErrNoRows) || len(organizationIDsByMemberIDsRows) == 0 {
|
||||
return []uuid.UUID{}, nil
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
"github.com/coder/coder/provisionersdk"
|
||||
)
|
||||
|
||||
func (api *api) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceAgent := httpmw.WorkspaceAgentParam(r)
|
||||
apiAgent, err := convertWorkspaceAgent(workspaceAgent, api.AgentConnectionUpdateFrequency)
|
||||
if err != nil {
|
||||
|
@ -41,7 +41,7 @@ func (api *api) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, apiAgent)
|
||||
}
|
||||
|
||||
func (api *api) workspaceAgentDial(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceAgentDial(rw http.ResponseWriter, r *http.Request) {
|
||||
api.websocketWaitMutex.Lock()
|
||||
api.websocketWaitGroup.Add(1)
|
||||
api.websocketWaitMutex.Unlock()
|
||||
|
@ -90,7 +90,7 @@ func (api *api) workspaceAgentDial(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (api *api) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
apiAgent, err := convertWorkspaceAgent(workspaceAgent, api.AgentConnectionUpdateFrequency)
|
||||
if err != nil {
|
||||
|
@ -136,7 +136,7 @@ func (api *api) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
|
|||
})
|
||||
}
|
||||
|
||||
func (api *api) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) {
|
||||
api.websocketWaitMutex.Lock()
|
||||
api.websocketWaitGroup.Add(1)
|
||||
api.websocketWaitMutex.Unlock()
|
||||
|
@ -269,12 +269,12 @@ func (api *api) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (api *api) workspaceAgentICEServers(rw http.ResponseWriter, _ *http.Request) {
|
||||
func (api *API) workspaceAgentICEServers(rw http.ResponseWriter, _ *http.Request) {
|
||||
httpapi.Write(rw, http.StatusOK, api.ICEServers)
|
||||
}
|
||||
|
||||
// workspaceAgentTurn proxies a WebSocket connection to the TURN server.
|
||||
func (api *api) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) {
|
||||
api.websocketWaitMutex.Lock()
|
||||
api.websocketWaitGroup.Add(1)
|
||||
api.websocketWaitMutex.Unlock()
|
||||
|
@ -324,7 +324,7 @@ func (api *api) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// workspaceAgentPTY spawns a PTY and pipes it over a WebSocket.
|
||||
// This is used for the web terminal.
|
||||
func (api *api) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
|
||||
api.websocketWaitMutex.Lock()
|
||||
api.websocketWaitGroup.Add(1)
|
||||
api.websocketWaitMutex.Unlock()
|
||||
|
@ -395,7 +395,7 @@ func (api *api) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// dialWorkspaceAgent connects to a workspace agent by ID.
|
||||
func (api *api) dialWorkspaceAgent(r *http.Request, agentID uuid.UUID) (*agent.Conn, error) {
|
||||
func (api *API) dialWorkspaceAgent(r *http.Request, agentID uuid.UUID) (*agent.Conn, error) {
|
||||
client, server := provisionersdk.TransportPipe()
|
||||
go func() {
|
||||
_ = peerbroker.ProxyListen(r.Context(), server, peerbroker.ProxyOptions{
|
||||
|
|
|
@ -27,9 +27,9 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
t.Parallel()
|
||||
t.Run("Connect", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
|
@ -68,9 +68,9 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
|
||||
func TestWorkspaceAgentListen(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
|
@ -118,9 +118,9 @@ func TestWorkspaceAgentListen(t *testing.T) {
|
|||
|
||||
func TestWorkspaceAgentTURN(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
|
@ -179,9 +179,9 @@ func TestWorkspaceAgentPTY(t *testing.T) {
|
|||
// it seems like it could be either.
|
||||
t.Skip("ConPTY appears to be inconsistent on Windows.")
|
||||
}
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceBuild := httpmw.WorkspaceBuildParam(r)
|
||||
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
|
||||
if err != nil {
|
||||
|
@ -45,7 +45,7 @@ func (api *api) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(workspaceBuild, convertProvisionerJob(job)))
|
||||
}
|
||||
|
||||
func (api *api) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceWorkspace.
|
||||
|
@ -107,7 +107,7 @@ func (api *api) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, apiBuilds)
|
||||
}
|
||||
|
||||
func (api *api) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
|
@ -142,7 +142,7 @@ func (api *api) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(workspaceBuild, convertProvisionerJob(job)))
|
||||
}
|
||||
|
||||
func (api *api) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
var createBuild codersdk.CreateWorkspaceBuildRequest
|
||||
|
@ -310,7 +310,7 @@ func (api *api) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusCreated, convertWorkspaceBuild(workspaceBuild, convertProvisionerJob(provisionerJob)))
|
||||
}
|
||||
|
||||
func (api *api) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceBuild := httpmw.WorkspaceBuildParam(r)
|
||||
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
|
||||
if err != nil {
|
||||
|
@ -362,7 +362,7 @@ func (api *api) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Reques
|
|||
})
|
||||
}
|
||||
|
||||
func (api *api) workspaceBuildResources(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceBuildResources(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceBuild := httpmw.WorkspaceBuildParam(r)
|
||||
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
|
||||
if err != nil {
|
||||
|
@ -387,7 +387,7 @@ func (api *api) workspaceBuildResources(rw http.ResponseWriter, r *http.Request)
|
|||
api.provisionerJobResources(rw, r, job)
|
||||
}
|
||||
|
||||
func (api *api) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceBuild := httpmw.WorkspaceBuildParam(r)
|
||||
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
|
||||
if err != nil {
|
||||
|
@ -412,7 +412,7 @@ func (api *api) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) {
|
|||
api.provisionerJobLogs(rw, r, job)
|
||||
}
|
||||
|
||||
func (api *api) workspaceBuildState(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceBuild := httpmw.WorkspaceBuildParam(r)
|
||||
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
|
||||
if err != nil {
|
||||
|
|
|
@ -119,9 +119,9 @@ func TestWorkspaceBuildResources(t *testing.T) {
|
|||
t.Parallel()
|
||||
t.Run("ListRunning", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
closeDaemon := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
closeDaemon := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
closeDaemon.Close()
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
// Azure supports instance identity verification:
|
||||
// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#tabgroup_14
|
||||
func (api *api) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
var req codersdk.AzureInstanceIdentityToken
|
||||
if !httpapi.Read(rw, r, &req) {
|
||||
return
|
||||
|
@ -36,7 +36,7 @@ func (api *api) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r
|
|||
// AWS supports instance identity verification:
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
|
||||
// Using this, we can exchange a signed instance payload for an agent token.
|
||||
func (api *api) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
var req codersdk.AWSInstanceIdentityToken
|
||||
if !httpapi.Read(rw, r, &req) {
|
||||
return
|
||||
|
@ -54,7 +54,7 @@ func (api *api) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *
|
|||
// Google Compute Engine supports instance identity verification:
|
||||
// https://cloud.google.com/compute/docs/instances/verifying-instance-identity
|
||||
// Using this, we can exchange a signed instance payload for an agent token.
|
||||
func (api *api) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
var req codersdk.GoogleInstanceIdentityToken
|
||||
if !httpapi.Read(rw, r, &req) {
|
||||
return
|
||||
|
@ -85,7 +85,7 @@ func (api *api) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter,
|
|||
api.handleAuthInstanceID(rw, r, claims.Google.ComputeEngine.InstanceID)
|
||||
}
|
||||
|
||||
func (api *api) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, instanceID string) {
|
||||
func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, instanceID string) {
|
||||
agent, err := api.Database.GetWorkspaceAgentByInstanceID(r.Context(), instanceID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) workspaceResource(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceResource(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceBuild := httpmw.WorkspaceBuildParam(r)
|
||||
workspaceResource := httpmw.WorkspaceResourceParam(r)
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func (api *api) workspace(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionRead, workspace) {
|
||||
return
|
||||
|
@ -100,7 +100,7 @@ func (api *api) workspace(rw http.ResponseWriter, r *http.Request) {
|
|||
convertWorkspace(workspace, convertWorkspaceBuild(build, convertProvisionerJob(job)), template, owner))
|
||||
}
|
||||
|
||||
func (api *api) workspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
workspaces, err := api.Database.GetWorkspacesWithFilter(r.Context(), database.GetWorkspacesWithFilterParams{
|
||||
OrganizationID: organization.ID,
|
||||
|
@ -131,7 +131,7 @@ func (api *api) workspacesByOrganization(rw http.ResponseWriter, r *http.Request
|
|||
|
||||
// workspaces returns all workspaces a user can read.
|
||||
// Optional filters with query params
|
||||
func (api *api) workspaces(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
|
||||
// Empty strings mean no filter
|
||||
|
@ -192,7 +192,7 @@ func (api *api) workspaces(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, apiWorkspaces)
|
||||
}
|
||||
|
||||
func (api *api) workspacesByOwner(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspacesByOwner(rw http.ResponseWriter, r *http.Request) {
|
||||
owner := httpmw.UserParam(r)
|
||||
workspaces, err := api.Database.GetWorkspacesWithFilter(r.Context(), database.GetWorkspacesWithFilterParams{
|
||||
OwnerID: owner.ID,
|
||||
|
@ -221,7 +221,7 @@ func (api *api) workspacesByOwner(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(rw, http.StatusOK, apiWorkspaces)
|
||||
}
|
||||
|
||||
func (api *api) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
owner := httpmw.UserParam(r)
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
workspaceName := chi.URLParam(r, "workspacename")
|
||||
|
@ -280,7 +280,7 @@ func (api *api) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
// Create a new workspace for the currently authenticated user.
|
||||
func (api *api) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
var createWorkspace codersdk.CreateWorkspaceRequest
|
||||
if !httpapi.Read(rw, r, &createWorkspace) {
|
||||
return
|
||||
|
@ -502,7 +502,7 @@ func (api *api) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
|
|||
convertWorkspaceBuild(workspaceBuild, convertProvisionerJob(templateVersionJob)), template, user))
|
||||
}
|
||||
|
||||
func (api *api) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionUpdate, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
|
@ -539,7 +539,7 @@ func (api *api) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (api *api) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionUpdate, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
|
@ -570,7 +570,7 @@ func (api *api) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (api *api) watchWorkspace(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
|
||||
c, err := websocket.Accept(rw, r, &websocket.AcceptOptions{
|
||||
|
|
|
@ -290,9 +290,9 @@ func TestPostWorkspaceBuild(t *testing.T) {
|
|||
|
||||
t.Run("AlreadyActive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
closeDaemon := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
closeDaemon := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
@ -328,9 +328,9 @@ func TestPostWorkspaceBuild(t *testing.T) {
|
|||
|
||||
t.Run("WithState", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, client, coderDaemon := coderdtest.NewWithServer(t, nil)
|
||||
client, coderAPI := coderdtest.NewWithAPI(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
closeDaemon := coderdtest.NewProvisionerDaemon(t, coderDaemon)
|
||||
closeDaemon := coderdtest.NewProvisionerDaemon(t, coderAPI)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
|
Loading…
Reference in New Issue