Skip to content

Commit 991db6d

Browse files
Merge remote-tracking branch 'origin/main' into yevhenii/secrets-prototype
2 parents 887f914 + dad033e commit 991db6d

File tree

121 files changed

+6943
-868
lines changed

Some content is hidden

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

121 files changed

+6943
-868
lines changed

.github/workflows/release.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,29 @@ jobs:
634634
- name: ls build
635635
run: ls -lh build
636636

637+
- name: Publish Coder CLI binaries and detached signatures to GCS
638+
if: ${{ !inputs.dry_run && github.ref == 'refs/heads/main' && github.repository_owner == 'coder'}}
639+
run: |
640+
set -euxo pipefail
641+
642+
version="$(./scripts/version.sh)"
643+
644+
binaries=(
645+
"coder-darwin-amd64"
646+
"coder-darwin-arm64"
647+
"coder-linux-amd64"
648+
"coder-linux-arm64"
649+
"coder-linux-armv7"
650+
"coder-windows-amd64.exe"
651+
"coder-windows-arm64.exe"
652+
)
653+
654+
for binary in "${binaries[@]}"; do
655+
detached_signature="${binary}.asc"
656+
gcloud storage cp "./site/out/bin/${binary}" "gs://releases.coder.com/coder-cli/${version}/${binary}"
657+
gcloud storage cp "./site/out/bin/${detached_signature}" "gs://releases.coder.com/coder-cli/${version}/${detached_signature}"
658+
done
659+
637660
- name: Publish release
638661
run: |
639662
set -euo pipefail

agent/agentcontainers/api_test.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2883,8 +2883,12 @@ func TestAPI(t *testing.T) {
28832883
Op: fsnotify.Write,
28842884
})
28852885

2886-
err = api.RefreshContainers(ctx)
2887-
require.NoError(t, err)
2886+
require.Eventuallyf(t, func() bool {
2887+
err = api.RefreshContainers(ctx)
2888+
require.NoError(t, err)
2889+
2890+
return len(fakeSAC.agents) == 1
2891+
}, testutil.WaitShort, testutil.IntervalFast, "subagent should be created after config change")
28882892

28892893
t.Log("Phase 2: Cont, waiting for sub agent to exit")
28902894
exitSubAgentOnce.Do(func() {
@@ -2919,8 +2923,12 @@ func TestAPI(t *testing.T) {
29192923
Op: fsnotify.Write,
29202924
})
29212925

2922-
err = api.RefreshContainers(ctx)
2923-
require.NoError(t, err)
2926+
require.Eventuallyf(t, func() bool {
2927+
err = api.RefreshContainers(ctx)
2928+
require.NoError(t, err)
2929+
2930+
return len(fakeSAC.agents) == 0
2931+
}, testutil.WaitShort, testutil.IntervalFast, "subagent should be deleted after config change")
29242932

29252933
req = httptest.NewRequest(http.MethodGet, "/", nil).WithContext(ctx)
29262934
rec = httptest.NewRecorder()

cli/delete_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,6 @@ func TestDelete(t *testing.T) {
233233
t.Skip("this test requires postgres")
234234
}
235235

236-
clock := quartz.NewMock(t)
237-
ctx := testutil.Context(t, testutil.WaitSuperLong)
238-
239236
// Setup
240237
db, pb := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
241238
client, _ := coderdtest.NewWithProvisionerCloser(t, &coderdtest.Options{
@@ -301,6 +298,9 @@ func TestDelete(t *testing.T) {
301298
t.Run(tc.name, func(t *testing.T) {
302299
t.Parallel()
303300

301+
clock := quartz.NewMock(t)
302+
ctx := testutil.Context(t, testutil.WaitSuperLong)
303+
304304
// Create one prebuilt workspace (owned by system user) and one normal workspace (owned by a user)
305305
// Each workspace is persisted in the DB along with associated workspace jobs and builds.
306306
dbPrebuiltWorkspace := setupTestDBWorkspace(t, clock, db, pb, orgID, database.PrebuildsSystemUserID, template.ID, version.ID, preset.ID)

cli/testdata/coder_list_--output_json.golden

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"automatic_updates": "never",
8787
"allow_renames": false,
8888
"favorite": false,
89-
"next_start_at": "====[timestamp]====="
89+
"next_start_at": "====[timestamp]=====",
90+
"is_prebuild": false
9091
}
9192
]

coderd/agentapi/api.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
agentproto "github.com/coder/coder/v2/agent/proto"
2020
"github.com/coder/coder/v2/coderd/agentapi/resourcesmonitor"
2121
"github.com/coder/coder/v2/coderd/appearance"
22-
"github.com/coder/coder/v2/coderd/audit"
22+
"github.com/coder/coder/v2/coderd/connectionlog"
2323
"github.com/coder/coder/v2/coderd/database"
2424
"github.com/coder/coder/v2/coderd/database/pubsub"
2525
"github.com/coder/coder/v2/coderd/externalauth"
@@ -50,7 +50,7 @@ type API struct {
5050
*ResourcesMonitoringAPI
5151
*LogsAPI
5252
*ScriptsAPI
53-
*AuditAPI
53+
*ConnLogAPI
5454
*SubAgentAPI
5555
*tailnet.DRPCService
5656

@@ -71,7 +71,7 @@ type Options struct {
7171
Database database.Store
7272
NotificationsEnqueuer notifications.Enqueuer
7373
Pubsub pubsub.Pubsub
74-
Auditor *atomic.Pointer[audit.Auditor]
74+
ConnectionLogger *atomic.Pointer[connectionlog.ConnectionLogger]
7575
DerpMapFn func() *tailcfg.DERPMap
7676
TailnetCoordinator *atomic.Pointer[tailnet.Coordinator]
7777
StatsReporter *workspacestats.Reporter
@@ -180,11 +180,11 @@ func New(opts Options) *API {
180180
Database: opts.Database,
181181
}
182182

183-
api.AuditAPI = &AuditAPI{
184-
AgentFn: api.agent,
185-
Auditor: opts.Auditor,
186-
Database: opts.Database,
187-
Log: opts.Log,
183+
api.ConnLogAPI = &ConnLogAPI{
184+
AgentFn: api.agent,
185+
ConnectionLogger: opts.ConnectionLogger,
186+
Database: opts.Database,
187+
Log: opts.Log,
188188
}
189189

190190
api.DRPCService = &tailnet.DRPCService{

coderd/agentapi/audit.go

Lines changed: 0 additions & 105 deletions
This file was deleted.

coderd/agentapi/connectionlog.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package agentapi
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"sync/atomic"
7+
8+
"github.com/google/uuid"
9+
"golang.org/x/xerrors"
10+
"google.golang.org/protobuf/types/known/emptypb"
11+
12+
"cdr.dev/slog"
13+
agentproto "github.com/coder/coder/v2/agent/proto"
14+
"github.com/coder/coder/v2/coderd/connectionlog"
15+
"github.com/coder/coder/v2/coderd/database"
16+
"github.com/coder/coder/v2/coderd/database/db2sdk"
17+
)
18+
19+
type ConnLogAPI struct {
20+
AgentFn func(context.Context) (database.WorkspaceAgent, error)
21+
ConnectionLogger *atomic.Pointer[connectionlog.ConnectionLogger]
22+
Database database.Store
23+
Log slog.Logger
24+
}
25+
26+
func (a *ConnLogAPI) ReportConnection(ctx context.Context, req *agentproto.ReportConnectionRequest) (*emptypb.Empty, error) {
27+
// We use the connection ID to identify which connection log event to mark
28+
// as closed, when we receive a close action for that ID.
29+
connectionID, err := uuid.FromBytes(req.GetConnection().GetId())
30+
if err != nil {
31+
return nil, xerrors.Errorf("connection id from bytes: %w", err)
32+
}
33+
34+
if connectionID == uuid.Nil {
35+
return nil, xerrors.New("connection ID cannot be nil")
36+
}
37+
action, err := db2sdk.ConnectionLogStatusFromAgentProtoConnectionAction(req.GetConnection().GetAction())
38+
if err != nil {
39+
return nil, err
40+
}
41+
connectionType, err := db2sdk.ConnectionLogConnectionTypeFromAgentProtoConnectionType(req.GetConnection().GetType())
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
var code sql.NullInt32
47+
if action == database.ConnectionStatusDisconnected {
48+
code = sql.NullInt32{
49+
Int32: req.GetConnection().GetStatusCode(),
50+
Valid: true,
51+
}
52+
}
53+
54+
// Fetch contextual data for this connection log event.
55+
workspaceAgent, err := a.AgentFn(ctx)
56+
if err != nil {
57+
return nil, xerrors.Errorf("get agent: %w", err)
58+
}
59+
workspace, err := a.Database.GetWorkspaceByAgentID(ctx, workspaceAgent.ID)
60+
if err != nil {
61+
return nil, xerrors.Errorf("get workspace by agent id: %w", err)
62+
}
63+
64+
reason := req.GetConnection().GetReason()
65+
connLogger := *a.ConnectionLogger.Load()
66+
err = connLogger.Upsert(ctx, database.UpsertConnectionLogParams{
67+
ID: uuid.New(),
68+
Time: req.GetConnection().GetTimestamp().AsTime(),
69+
OrganizationID: workspace.OrganizationID,
70+
WorkspaceOwnerID: workspace.OwnerID,
71+
WorkspaceID: workspace.ID,
72+
WorkspaceName: workspace.Name,
73+
AgentName: workspaceAgent.Name,
74+
Type: connectionType,
75+
Code: code,
76+
Ip: database.ParseIP(req.GetConnection().GetIp()),
77+
ConnectionID: uuid.NullUUID{
78+
UUID: connectionID,
79+
Valid: true,
80+
},
81+
DisconnectReason: sql.NullString{
82+
String: reason,
83+
Valid: reason != "",
84+
},
85+
// We supply the action:
86+
// - So the DB can handle duplicate connections or disconnections properly.
87+
// - To make it clear whether this is a connection or disconnection
88+
// prior to it's insertion into the DB (logs)
89+
ConnectionStatus: action,
90+
91+
// It's not possible to tell which user connected. Once we have
92+
// the capability, this may be reported by the agent.
93+
UserID: uuid.NullUUID{
94+
Valid: false,
95+
},
96+
// N/A
97+
UserAgent: sql.NullString{},
98+
// N/A
99+
SlugOrPort: sql.NullString{},
100+
})
101+
if err != nil {
102+
return nil, xerrors.Errorf("export connection log: %w", err)
103+
}
104+
105+
return &emptypb.Empty{}, nil
106+
}

0 commit comments

Comments
 (0)