Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit013f028

Browse files
authored
feat: Add workspace application support (#1773)
* feat: Add app supportThis adds apps as a property to a workspace agent.The resource is added to the Terraform provider here:coder/terraform-provider-coder#17Apps will be opened in the dashboard or via the CLIwith `coder open <name>`. If `command` is specified, aterminal will appear locally and in the web. If `target`is specified, the browser will open to an exposed instanceof that target.* Compare fields in apps test* Update Terraform provider to use relative path* Add some basic structure for routing* chore: Remove interface from coderd and lift API surfaceAbstracting coderd into an interface added misdirection becausethe interface was never intended to be fulfilled outside of a singleimplementation.This lifts the abstraction, and attaches all handlers to a root structnamed `*coderd.API`.* Add basic proxy logic* Add proxying based on path* Add app proxying for wildcards* Add wsconncache* fix: Race when writing to a closed pipeThis is such an intermittent race it's difficult to track,but regardless this is an improvement to the code.* fix: Race when writing to a closed pipeThis is such an intermittent race it's difficult to track,but regardless this is an improvement to the code.* fix: Race when writing to a closed pipeThis is such an intermittent race it's difficult to track,but regardless this is an improvement to the code.* fix: Race when writing to a closed pipeThis is such an intermittent race it's difficult to track,but regardless this is an improvement to the code.* Add workspace route proxying endpoint- Makes the workspace conn cache concurrency-safe- Reduces unnecessary open checks in `peer.Channel`- Fixes the use of a temporary context when dialing a workspace agent* Add embed errors* chore: Refactor site to improve testingIt was difficult to develop this package due to theembed build tag being mandatory on the tests. The logicto test doesn't require any embedded files.* Add test for error handler* Remove unused access url* Add RBAC tests* Fix dial agent syntax* Fix linting errors* Fix gen* Fix icon required* Adjust migration number* Fix proxy error status code* Fix empty db lookup
1 parent2c089d5 commit013f028

File tree

42 files changed

+1710
-268
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1710
-268
lines changed

‎.vscode/settings.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"cSpell.words": [
3+
"apps",
4+
"awsidentity",
5+
"buildinfo",
36
"buildname",
47
"circbuf",
58
"cliflag",
@@ -16,6 +19,7 @@
1619
"Dsts",
1720
"fatih",
1821
"Formik",
22+
"gitsshkey",
1923
"goarch",
2024
"gographviz",
2125
"goleak",
@@ -30,6 +34,7 @@
3034
"incpatch",
3135
"isatty",
3236
"Jobf",
37+
"Keygen",
3338
"kirsle",
3439
"ldflags",
3540
"manifoldco",
@@ -54,6 +59,7 @@
5459
"retrier",
5560
"rpty",
5661
"sdkproto",
62+
"sdktrace",
5763
"Signup",
5864
"sourcemapped",
5965
"stretchr",
@@ -66,13 +72,18 @@
6672
"tfjson",
6773
"tfstate",
6874
"trimprefix",
75+
"turnconn",
6976
"typegen",
7077
"unconvert",
7178
"Untar",
7279
"VMID",
7380
"weblinks",
7481
"webrtc",
82+
"workspaceagent",
83+
"workspaceapp",
84+
"workspaceapps",
7585
"workspacebuilds",
86+
"wsconncache",
7687
"xerrors",
7788
"xstate",
7889
"yamux"

‎agent/conn.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (c *Conn) DialContext(ctx context.Context, network string, addr string) (ne
102102
varresdialResponse
103103
err=dec.Decode(&res)
104104
iferr!=nil {
105-
returnnil,xerrors.Errorf("failed todecodeinitial packet: %w",err)
105+
returnnil,xerrors.Errorf("decodeagent dial response: %w",err)
106106
}
107107
ifres.Error!="" {
108108
_=channel.Close()

‎coderd/coderd.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/coder/coder/coderd/rbac"
2828
"github.com/coder/coder/coderd/tracing"
2929
"github.com/coder/coder/coderd/turnconn"
30+
"github.com/coder/coder/coderd/wsconncache"
3031
"github.com/coder/coder/codersdk"
3132
"github.com/coder/coder/site"
3233
)
@@ -44,14 +45,14 @@ type Options struct {
4445
// app. Specific routes may have their own limiters.
4546
APIRateLimitint
4647
AWSCertificates awsidentity.Certificates
48+
Authorizer rbac.Authorizer
4749
AzureCertificates x509.VerifyOptions
4850
GoogleTokenValidator*idtoken.Validator
4951
GithubOAuth2Config*GithubOAuth2Config
5052
ICEServers []webrtc.ICEServer
5153
SecureAuthCookiebool
5254
SSHKeygenAlgorithm gitsshkey.Algorithm
5355
TURNServer*turnconn.Server
54-
Authorizer rbac.Authorizer
5556
TracerProvider*sdktrace.TracerProvider
5657
}
5758

@@ -75,9 +76,11 @@ func New(options *Options) *API {
7576

7677
r:=chi.NewRouter()
7778
api:=&API{
78-
Options:options,
79-
Handler:r,
79+
Options:options,
80+
Handler:r,
81+
siteHandler:site.Handler(site.FS()),
8082
}
83+
api.workspaceAgentCache=wsconncache.New(api.dialWorkspaceAgent,0)
8184

8285
apiKeyMiddleware:=httpmw.ExtractAPIKey(options.Database,&httpmw.OAuth2Configs{
8386
Github:options.GithubOAuth2Config,
@@ -93,6 +96,20 @@ func New(options *Options) *API {
9396
tracing.HTTPMW(api.TracerProvider,"coderd.http"),
9497
)
9598

99+
apps:=func(r chi.Router) {
100+
r.Use(
101+
httpmw.RateLimitPerMinute(options.APIRateLimit),
102+
apiKeyMiddleware,
103+
httpmw.ExtractUserParam(api.Database),
104+
)
105+
r.Get("/*",api.workspaceAppsProxyPath)
106+
}
107+
// %40 is the encoded character of the @ symbol. VS Code Web does
108+
// not handle character encoding properly, so it's safe to assume
109+
// other applications might not as well.
110+
r.Route("/%40{user}/{workspacename}/apps/{workspaceapp}",apps)
111+
r.Route("/@{user}/{workspacename}/apps/{workspaceapp}",apps)
112+
96113
r.Route("/api/v2",func(r chi.Router) {
97114
r.NotFound(func(rw http.ResponseWriter,r*http.Request) {
98115
httpapi.Write(rw,http.StatusNotFound, httpapi.Response{
@@ -327,24 +344,27 @@ func New(options *Options) *API {
327344
r.Get("/state",api.workspaceBuildState)
328345
})
329346
})
330-
r.NotFound(site.Handler(site.FS()).ServeHTTP)
331-
347+
r.NotFound(api.siteHandler.ServeHTTP)
332348
returnapi
333349
}
334350

335351
typeAPIstruct {
336352
*Options
337353

338-
Handler chi.Router
339-
websocketWaitMutex sync.Mutex
340-
websocketWaitGroup sync.WaitGroup
354+
Handler chi.Router
355+
siteHandler http.Handler
356+
websocketWaitMutex sync.Mutex
357+
websocketWaitGroup sync.WaitGroup
358+
workspaceAgentCache*wsconncache.Cache
341359
}
342360

343361
// Close waits for all WebSocket connections to drain before returning.
344-
func (api*API)Close() {
362+
func (api*API)Close()error{
345363
api.websocketWaitMutex.Lock()
346364
api.websocketWaitGroup.Wait()
347365
api.websocketWaitMutex.Unlock()
366+
367+
returnapi.workspaceAgentCache.Close()
348368
}
349369

350370
funcdebugLogRequest(log slog.Logger)func(http.Handler) http.Handler {

‎coderd/coderd_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
7474
Agents: []*proto.Agent{{
7575
Id:"something",
7676
Auth:&proto.Agent_Token{},
77+
Apps: []*proto.App{{
78+
Name:"app",
79+
Url:"http://localhost:3000",
80+
}},
7781
}},
7882
}},
7983
},
@@ -128,6 +132,15 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
128132
"GET:/api/v2/users/authmethods": {NoAuthorize:true},
129133
"POST:/api/v2/csp/reports": {NoAuthorize:true},
130134

135+
"GET:/%40{user}/{workspacename}/apps/{application}/*": {
136+
AssertAction:rbac.ActionRead,
137+
AssertObject:workspaceRBACObj,
138+
},
139+
"GET:/@{user}/{workspacename}/apps/{application}/*": {
140+
AssertAction:rbac.ActionRead,
141+
AssertObject:workspaceRBACObj,
142+
},
143+
131144
// Has it's own auth
132145
"GET:/api/v2/users/oauth2/github/callback": {NoAuthorize:true},
133146

@@ -368,6 +381,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
368381
route=strings.ReplaceAll(route,"{template}",template.ID.String())
369382
route=strings.ReplaceAll(route,"{hash}",file.Hash)
370383
route=strings.ReplaceAll(route,"{workspaceresource}",workspaceResources[0].ID.String())
384+
route=strings.ReplaceAll(route,"{workspaceapp}",workspaceResources[0].Agents[0].Apps[0].Name)
371385
route=strings.ReplaceAll(route,"{templateversion}",version.ID.String())
372386
route=strings.ReplaceAll(route,"{templateversiondryrun}",templateVersionDryRun.ID.String())
373387
route=strings.ReplaceAll(route,"{templatename}",template.Name)

‎coderd/coderdtest/coderdtest.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, *coderd.API)
173173
cancelFunc()
174174
_=turnServer.Close()
175175
srv.Close()
176-
coderAPI.Close()
176+
_=coderAPI.Close()
177177
})
178178

179179
returncodersdk.New(serverURL),coderAPI

‎coderd/database/databasefake/databasefake.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func New() database.Store {
3535
templateVersions:make([]database.TemplateVersion,0),
3636
templates:make([]database.Template,0),
3737
workspaceBuilds:make([]database.WorkspaceBuild,0),
38+
workspaceApps:make([]database.WorkspaceApp,0),
3839
workspaces:make([]database.Workspace,0),
3940
}
4041
}
@@ -63,6 +64,7 @@ type fakeQuerier struct {
6364
templateVersions []database.TemplateVersion
6465
templates []database.Template
6566
workspaceBuilds []database.WorkspaceBuild
67+
workspaceApps []database.WorkspaceApp
6668
workspaces []database.Workspace
6769
}
6870

@@ -388,6 +390,38 @@ func (q *fakeQuerier) GetWorkspaceByOwnerIDAndName(_ context.Context, arg databa
388390
return database.Workspace{},sql.ErrNoRows
389391
}
390392

393+
func (q*fakeQuerier)GetWorkspaceAppsByAgentID(_ context.Context,id uuid.UUID) ([]database.WorkspaceApp,error) {
394+
q.mutex.RLock()
395+
deferq.mutex.RUnlock()
396+
397+
apps:=make([]database.WorkspaceApp,0)
398+
for_,app:=rangeq.workspaceApps {
399+
ifapp.AgentID==id {
400+
apps=append(apps,app)
401+
}
402+
}
403+
iflen(apps)==0 {
404+
returnnil,sql.ErrNoRows
405+
}
406+
returnapps,nil
407+
}
408+
409+
func (q*fakeQuerier)GetWorkspaceAppsByAgentIDs(_ context.Context,ids []uuid.UUID) ([]database.WorkspaceApp,error) {
410+
q.mutex.RLock()
411+
deferq.mutex.RUnlock()
412+
413+
apps:=make([]database.WorkspaceApp,0)
414+
for_,app:=rangeq.workspaceApps {
415+
for_,id:=rangeids {
416+
ifapp.AgentID.String()==id.String() {
417+
apps=append(apps,app)
418+
break
419+
}
420+
}
421+
}
422+
returnapps,nil
423+
}
424+
391425
func (q*fakeQuerier)GetWorkspacesAutostart(_ context.Context) ([]database.Workspace,error) {
392426
q.mutex.RLock()
393427
deferq.mutex.RUnlock()
@@ -1031,6 +1065,22 @@ func (q *fakeQuerier) GetWorkspaceAgentsByResourceIDs(_ context.Context, resourc
10311065
returnworkspaceAgents,nil
10321066
}
10331067

1068+
func (q*fakeQuerier)GetWorkspaceAppByAgentIDAndName(_ context.Context,arg database.GetWorkspaceAppByAgentIDAndNameParams) (database.WorkspaceApp,error) {
1069+
q.mutex.RLock()
1070+
deferq.mutex.RUnlock()
1071+
1072+
for_,app:=rangeq.workspaceApps {
1073+
ifapp.AgentID!=arg.AgentID {
1074+
continue
1075+
}
1076+
ifapp.Name!=arg.Name {
1077+
continue
1078+
}
1079+
returnapp,nil
1080+
}
1081+
return database.WorkspaceApp{},sql.ErrNoRows
1082+
}
1083+
10341084
func (q*fakeQuerier)GetProvisionerDaemonByID(_ context.Context,id uuid.UUID) (database.ProvisionerDaemon,error) {
10351085
q.mutex.RLock()
10361086
deferq.mutex.RUnlock()
@@ -1521,6 +1571,25 @@ func (q *fakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.Inser
15211571
returnworkspaceBuild,nil
15221572
}
15231573

1574+
func (q*fakeQuerier)InsertWorkspaceApp(_ context.Context,arg database.InsertWorkspaceAppParams) (database.WorkspaceApp,error) {
1575+
q.mutex.Lock()
1576+
deferq.mutex.Unlock()
1577+
1578+
// nolint:gosimple
1579+
workspaceApp:= database.WorkspaceApp{
1580+
ID:arg.ID,
1581+
AgentID:arg.AgentID,
1582+
CreatedAt:arg.CreatedAt,
1583+
Name:arg.Name,
1584+
Icon:arg.Icon,
1585+
Command:arg.Command,
1586+
Url:arg.Url,
1587+
RelativePath:arg.RelativePath,
1588+
}
1589+
q.workspaceApps=append(q.workspaceApps,workspaceApp)
1590+
returnworkspaceApp,nil
1591+
}
1592+
15241593
func (q*fakeQuerier)UpdateAPIKeyByID(_ context.Context,arg database.UpdateAPIKeyByIDParams)error {
15251594
q.mutex.Lock()
15261595
deferq.mutex.Unlock()

‎coderd/database/dump.sql

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROPTABLE workspace_apps;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CREATETABLEworkspace_apps (
2+
id uuidNOT NULL,
3+
created_attimestamp with time zoneNOT NULL,
4+
agent_id uuidNOT NULLREFERENCES workspace_agents (id)ON DELETE CASCADE,
5+
namevarchar(64)NOT NULL,
6+
iconvarchar(256)NOT NULL,
7+
commandvarchar(65534),
8+
urlvarchar(65534),
9+
relative_pathbooleanNOT NULL DEFAULT false,
10+
PRIMARY KEY (id),
11+
UNIQUE(agent_id, name)
12+
);

‎coderd/database/models.go

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/querier.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp