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

Commitfed668b

Browse files
authored
chore: switch ssh session stats based on experiment (#13637)
1 parentd7eadee commitfed668b

File tree

14 files changed

+455
-45
lines changed

14 files changed

+455
-45
lines changed

‎cli/ssh.go‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"os"
1313
"os/exec"
1414
"path/filepath"
15+
"slices"
1516
"strings"
1617
"sync"
1718
"time"
@@ -40,6 +41,10 @@ import (
4041
"github.com/coder/serpent"
4142
)
4243

44+
const (
45+
disableUsageApp="disable"
46+
)
47+
4348
var (
4449
workspacePollInterval=time.Minute
4550
autostopNotifyCountdown= []time.Duration{30*time.Minute}
@@ -57,6 +62,7 @@ func (r *RootCmd) ssh() *serpent.Command {
5762
logDirPathstring
5863
remoteForwards []string
5964
env []string
65+
usageAppstring
6066
disableAutostartbool
6167
)
6268
client:=new(codersdk.Client)
@@ -251,6 +257,15 @@ func (r *RootCmd) ssh() *serpent.Command {
251257
stopPolling:=tryPollWorkspaceAutostop(ctx,client,workspace)
252258
deferstopPolling()
253259

260+
usageAppName:=getUsageAppName(usageApp)
261+
ifusageAppName!="" {
262+
closeUsage:=client.UpdateWorkspaceUsageWithBodyContext(ctx,workspace.ID, codersdk.PostWorkspaceUsageRequest{
263+
AgentID:workspaceAgent.ID,
264+
AppName:usageAppName,
265+
})
266+
defercloseUsage()
267+
}
268+
254269
ifstdio {
255270
rawSSH,err:=conn.SSH(ctx)
256271
iferr!=nil {
@@ -509,6 +524,13 @@ func (r *RootCmd) ssh() *serpent.Command {
509524
FlagShorthand:"e",
510525
Value:serpent.StringArrayOf(&env),
511526
},
527+
{
528+
Flag:"usage-app",
529+
Description:"Specifies the usage app to use for workspace activity tracking.",
530+
Env:"CODER_SSH_USAGE_APP",
531+
Value:serpent.StringOf(&usageApp),
532+
Hidden:true,
533+
},
512534
sshDisableAutostartOption(serpent.BoolOf(&disableAutostart)),
513535
}
514536
returncmd
@@ -1044,3 +1066,20 @@ func (r stdioErrLogReader) Read(_ []byte) (int, error) {
10441066
r.l.Error(context.Background(),"reading from stdin in stdio mode is not allowed")
10451067
return0,io.EOF
10461068
}
1069+
1070+
funcgetUsageAppName(usageAppstring) codersdk.UsageAppName {
1071+
ifusageApp==disableUsageApp {
1072+
return""
1073+
}
1074+
1075+
allowedUsageApps:= []string{
1076+
string(codersdk.UsageAppNameSSH),
1077+
string(codersdk.UsageAppNameVscode),
1078+
string(codersdk.UsageAppNameJetbrains),
1079+
}
1080+
ifslices.Contains(allowedUsageApps,usageApp) {
1081+
returncodersdk.UsageAppName(usageApp)
1082+
}
1083+
1084+
returncodersdk.UsageAppNameSSH
1085+
}

‎cli/ssh_test.go‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ import (
3636
"github.com/coder/coder/v2/agent"
3737
"github.com/coder/coder/v2/agent/agentssh"
3838
"github.com/coder/coder/v2/agent/agenttest"
39+
agentproto"github.com/coder/coder/v2/agent/proto"
3940
"github.com/coder/coder/v2/cli/clitest"
4041
"github.com/coder/coder/v2/cli/cliui"
4142
"github.com/coder/coder/v2/coderd/coderdtest"
4243
"github.com/coder/coder/v2/coderd/database"
4344
"github.com/coder/coder/v2/coderd/database/dbfake"
4445
"github.com/coder/coder/v2/coderd/database/dbtestutil"
4546
"github.com/coder/coder/v2/coderd/rbac"
47+
"github.com/coder/coder/v2/coderd/workspacestats/workspacestatstest"
4648
"github.com/coder/coder/v2/codersdk"
4749
"github.com/coder/coder/v2/provisioner/echo"
4850
"github.com/coder/coder/v2/provisionersdk/proto"
@@ -1292,6 +1294,115 @@ func TestSSH(t *testing.T) {
12921294
require.NoError(t,err)
12931295
require.Len(t,ents,1,"expected one file in logdir %s",logDir)
12941296
})
1297+
t.Run("UpdateUsage",func(t*testing.T) {
1298+
t.Parallel()
1299+
1300+
typetestCasestruct {
1301+
namestring
1302+
experimentbool
1303+
usageAppNamestring
1304+
expectedCallsint
1305+
expectedCountSSHint
1306+
expectedCountJetbrainsint
1307+
expectedCountVscodeint
1308+
}
1309+
tcs:= []testCase{
1310+
{
1311+
name:"NoExperiment",
1312+
},
1313+
{
1314+
name:"Empty",
1315+
experiment:true,
1316+
expectedCalls:1,
1317+
expectedCountSSH:1,
1318+
},
1319+
{
1320+
name:"SSH",
1321+
experiment:true,
1322+
usageAppName:"ssh",
1323+
expectedCalls:1,
1324+
expectedCountSSH:1,
1325+
},
1326+
{
1327+
name:"Jetbrains",
1328+
experiment:true,
1329+
usageAppName:"jetbrains",
1330+
expectedCalls:1,
1331+
expectedCountJetbrains:1,
1332+
},
1333+
{
1334+
name:"Vscode",
1335+
experiment:true,
1336+
usageAppName:"vscode",
1337+
expectedCalls:1,
1338+
expectedCountVscode:1,
1339+
},
1340+
{
1341+
name:"InvalidDefaultsToSSH",
1342+
experiment:true,
1343+
usageAppName:"invalid",
1344+
expectedCalls:1,
1345+
expectedCountSSH:1,
1346+
},
1347+
{
1348+
name:"Disable",
1349+
experiment:true,
1350+
usageAppName:"disable",
1351+
},
1352+
}
1353+
1354+
for_,tc:=rangetcs {
1355+
tc:=tc
1356+
t.Run(tc.name,func(t*testing.T) {
1357+
t.Parallel()
1358+
1359+
dv:=coderdtest.DeploymentValues(t)
1360+
iftc.experiment {
1361+
dv.Experiments= []string{string(codersdk.ExperimentWorkspaceUsage)}
1362+
}
1363+
batcher:=&workspacestatstest.StatsBatcher{
1364+
LastStats:&agentproto.Stats{},
1365+
}
1366+
admin,store:=coderdtest.NewWithDatabase(t,&coderdtest.Options{
1367+
DeploymentValues:dv,
1368+
StatsBatcher:batcher,
1369+
})
1370+
admin.SetLogger(slogtest.Make(t,nil).Named("client").Leveled(slog.LevelDebug))
1371+
first:=coderdtest.CreateFirstUser(t,admin)
1372+
client,user:=coderdtest.CreateAnotherUser(t,admin,first.OrganizationID)
1373+
r:=dbfake.WorkspaceBuild(t,store, database.Workspace{
1374+
OrganizationID:first.OrganizationID,
1375+
OwnerID:user.ID,
1376+
}).WithAgent().Do()
1377+
workspace:=r.Workspace
1378+
agentToken:=r.AgentToken
1379+
inv,root:=clitest.New(t,"ssh",workspace.Name,fmt.Sprintf("--usage-app=%s",tc.usageAppName))
1380+
clitest.SetupConfig(t,client,root)
1381+
pty:=ptytest.New(t).Attach(inv)
1382+
1383+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
1384+
defercancel()
1385+
1386+
cmdDone:=tGo(t,func() {
1387+
err:=inv.WithContext(ctx).Run()
1388+
assert.NoError(t,err)
1389+
})
1390+
pty.ExpectMatch("Waiting")
1391+
1392+
_=agenttest.New(t,client.URL,agentToken)
1393+
coderdtest.AwaitWorkspaceAgents(t,client,workspace.ID)
1394+
1395+
// Shells on Mac, Windows, and Linux all exit shells with the "exit" command.
1396+
pty.WriteLine("exit")
1397+
<-cmdDone
1398+
1399+
require.EqualValues(t,tc.expectedCalls,batcher.Called)
1400+
require.EqualValues(t,tc.expectedCountSSH,batcher.LastStats.SessionCountSsh)
1401+
require.EqualValues(t,tc.expectedCountJetbrains,batcher.LastStats.SessionCountJetbrains)
1402+
require.EqualValues(t,tc.expectedCountVscode,batcher.LastStats.SessionCountVscode)
1403+
})
1404+
}
1405+
})
12951406
}
12961407

12971408
//nolint:paralleltest // This test uses t.Setenv, parent test MUST NOT be parallel.

‎cli/vscodessh.go‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
110110
// will call this command after the workspace is started.
111111
autostart:=false
112112

113-
_,workspaceAgent,err:=getWorkspaceAndAgent(ctx,inv,client,autostart,fmt.Sprintf("%s/%s",owner,name))
113+
workspace,workspaceAgent,err:=getWorkspaceAndAgent(ctx,inv,client,autostart,fmt.Sprintf("%s/%s",owner,name))
114114
iferr!=nil {
115115
returnxerrors.Errorf("find workspace and agent: %w",err)
116116
}
@@ -176,6 +176,13 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
176176
deferagentConn.Close()
177177

178178
agentConn.AwaitReachable(ctx)
179+
180+
closeUsage:=client.UpdateWorkspaceUsageWithBodyContext(ctx,workspace.ID, codersdk.PostWorkspaceUsageRequest{
181+
AgentID:workspaceAgent.ID,
182+
AppName:codersdk.UsageAppNameVscode,
183+
})
184+
defercloseUsage()
185+
179186
rawSSH,err:=agentConn.SSH(ctx)
180187
iferr!=nil {
181188
returnerr

‎cli/vscodessh_test.go‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,16 @@ import (
99
"github.com/stretchr/testify/assert"
1010
"github.com/stretchr/testify/require"
1111

12+
"cdr.dev/slog"
13+
"cdr.dev/slog/sloggers/slogtest"
14+
1215
"github.com/coder/coder/v2/agent/agenttest"
16+
agentproto"github.com/coder/coder/v2/agent/proto"
1317
"github.com/coder/coder/v2/cli/clitest"
1418
"github.com/coder/coder/v2/coderd/coderdtest"
19+
"github.com/coder/coder/v2/coderd/database"
20+
"github.com/coder/coder/v2/coderd/database/dbfake"
21+
"github.com/coder/coder/v2/coderd/workspacestats/workspacestatstest"
1522
"github.com/coder/coder/v2/codersdk"
1623
"github.com/coder/coder/v2/pty/ptytest"
1724
"github.com/coder/coder/v2/testutil"
@@ -22,7 +29,25 @@ import (
2229
funcTestVSCodeSSH(t*testing.T) {
2330
t.Parallel()
2431
ctx:=testutil.Context(t,testutil.WaitLong)
25-
client,workspace,agentToken:=setupWorkspaceForAgent(t)
32+
dv:=coderdtest.DeploymentValues(t)
33+
dv.Experiments= []string{string(codersdk.ExperimentWorkspaceUsage)}
34+
batcher:=&workspacestatstest.StatsBatcher{
35+
LastStats:&agentproto.Stats{},
36+
}
37+
admin,store:=coderdtest.NewWithDatabase(t,&coderdtest.Options{
38+
DeploymentValues:dv,
39+
StatsBatcher:batcher,
40+
})
41+
admin.SetLogger(slogtest.Make(t,nil).Named("client").Leveled(slog.LevelDebug))
42+
first:=coderdtest.CreateFirstUser(t,admin)
43+
client,user:=coderdtest.CreateAnotherUser(t,admin,first.OrganizationID)
44+
r:=dbfake.WorkspaceBuild(t,store, database.Workspace{
45+
OrganizationID:first.OrganizationID,
46+
OwnerID:user.ID,
47+
}).WithAgent().Do()
48+
workspace:=r.Workspace
49+
agentToken:=r.AgentToken
50+
2651
user,err:=client.User(ctx,codersdk.Me)
2752
require.NoError(t,err)
2853

@@ -65,4 +90,7 @@ func TestVSCodeSSH(t *testing.T) {
6590
iferr:=waiter.Wait();err!=nil {
6691
waiter.RequireIs(context.Canceled)
6792
}
93+
94+
require.EqualValues(t,1,batcher.Called)
95+
require.EqualValues(t,1,batcher.LastStats.SessionCountVscode)
6896
}

‎coderd/agentapi/api.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/coder/coder/v2/coderd/schedule"
2626
"github.com/coder/coder/v2/coderd/tracing"
2727
"github.com/coder/coder/v2/coderd/workspacestats"
28+
"github.com/coder/coder/v2/codersdk"
2829
"github.com/coder/coder/v2/codersdk/agentsdk"
2930
"github.com/coder/coder/v2/tailnet"
3031
tailnetproto"github.com/coder/coder/v2/tailnet/proto"
@@ -72,6 +73,7 @@ type Options struct {
7273
DerpForceWebSocketsbool
7374
DerpMapUpdateFrequency time.Duration
7475
ExternalAuthConfigs []*externalauth.Config
76+
Experiments codersdk.Experiments
7577

7678
// Optional:
7779
// WorkspaceID avoids a future lookup to find the workspace ID by setting
@@ -118,6 +120,7 @@ func New(opts Options) *API {
118120
Log:opts.Log,
119121
StatsReporter:opts.StatsReporter,
120122
AgentStatsRefreshInterval:opts.AgentStatsRefreshInterval,
123+
Experiments:opts.Experiments,
121124
}
122125

123126
api.LifecycleAPI=&LifecycleAPI{

‎coderd/agentapi/stats.go‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/coder/coder/v2/coderd/database"
1313
"github.com/coder/coder/v2/coderd/database/dbtime"
1414
"github.com/coder/coder/v2/coderd/workspacestats"
15+
"github.com/coder/coder/v2/codersdk"
1516
)
1617

1718
typeStatsAPIstruct {
@@ -20,6 +21,7 @@ type StatsAPI struct {
2021
Log slog.Logger
2122
StatsReporter*workspacestats.Reporter
2223
AgentStatsRefreshInterval time.Duration
24+
Experiments codersdk.Experiments
2325

2426
TimeNowFnfunc() time.Time// defaults to dbtime.Now()
2527
}
@@ -55,6 +57,16 @@ func (a *StatsAPI) UpdateStats(ctx context.Context, req *agentproto.UpdateStatsR
5557
slog.F("payload",req),
5658
)
5759

60+
ifa.Experiments.Enabled(codersdk.ExperimentWorkspaceUsage) {
61+
// while the experiment is enabled we will not report
62+
// session stats from the agent. This is because it is
63+
// being handled by the CLI and the postWorkspaceUsage route.
64+
req.Stats.SessionCountSsh=0
65+
req.Stats.SessionCountJetbrains=0
66+
req.Stats.SessionCountVscode=0
67+
req.Stats.SessionCountReconnectingPty=0
68+
}
69+
5870
err=a.StatsReporter.ReportAgentStats(
5971
ctx,
6072
a.now(),

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp