-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathdisconnect.go
More file actions
228 lines (194 loc) · 8.23 KB
/
Copy pathdisconnect.go
File metadata and controls
228 lines (194 loc) · 8.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package codersdk
import "cdr.dev/slog/v3"
// SlogDisconnectDetail is the slog field for the free-form, human-readable
// detail string that supplements the structured reason. Use it for
// "exited with code 137" style information that does not fit a category.
func SlogDisconnectDetail(detail string) slog.Field {
return slog.F("disconnect_detail", detail)
}
// DisconnectReason categorizes why a workspace connection ended. It is
// emitted as a slog field at every disconnect log site so operators can
// filter and aggregate disconnects without parsing free-form reason
// strings.
//
// The set of values intentionally stays small. Use DisconnectReasonUnknown
// when no other value applies; do not invent ad-hoc strings. Add a new
// constant here (and update its godoc) when a new disconnect class is
// genuinely distinct from the existing ones.
type DisconnectReason string
func (r DisconnectReason) SlogField() slog.Field {
if r == "" {
return slog.F("disconnect_reason", "unknown")
}
return slog.F("disconnect_reason", r)
}
func (r DisconnectReason) SlogExpectedField() slog.Field {
return slog.F("disconnect_expected", r.Expected())
}
const (
// DisconnectReasonUnknown is the zero value. Use it when the disconnect
// path cannot determine a more specific reason. Treat any disconnect
// logged with this value as a bug to investigate, not as a normal
// outcome.
DisconnectReasonUnknown DisconnectReason = ""
// DisconnectReasonGraceful indicates the connection ended cleanly: the
// remote side acknowledged a Disconnect message, an SSH session exited
// with status 0, or a PTY closed without error. This is the expected
// "happy path" outcome.
DisconnectReasonGraceful DisconnectReason = "graceful"
// DisconnectReasonClientClosed indicates the client side closed the
// connection without an error (for example, the user closed their
// terminal or quit their IDE). The session ran to a natural end from
// the client's perspective.
DisconnectReasonClientClosed DisconnectReason = "client_closed"
// DisconnectReasonServerShutdown indicates the workspace agent or
// coderd is shutting down and is closing connections as part of that
// shutdown. The connection itself was healthy; the process is going
// away.
DisconnectReasonServerShutdown DisconnectReason = "server_shutdown"
// DisconnectReasonNetworkError indicates the transport failed: an EOF,
// a read or write error, a context cancellation caused by a timeout,
// or a similar I/O failure. The connection did not end cleanly.
DisconnectReasonNetworkError DisconnectReason = "network_error"
// DisconnectReasonProtocolError indicates a dRPC, SSH, or tailnet
// protocol violation by the peer. Distinct from network errors because
// the bytes flowed but the contents were unparsable or unexpected.
DisconnectReasonProtocolError DisconnectReason = "protocol_error"
// DisconnectReasonWorkspaceStopped indicates the workspace itself was
// stopped or deleted while the connection was open, so coderd closed
// outstanding sessions on its behalf.
DisconnectReasonWorkspaceStopped DisconnectReason = "workspace_stopped"
// DisconnectReasonControlPlaneLost indicates the agent or client lost
// its coordination RPC to coderd. The data plane (peer-to-peer or
// DERP) may still be functional; this records the control plane
// outcome specifically.
DisconnectReasonControlPlaneLost DisconnectReason = "control_plane_lost"
)
// Valid reports whether r is a known DisconnectReason. The zero value
// (DisconnectReasonUnknown) is considered valid since it is the explicit
// "no information" reason.
func (r DisconnectReason) Valid() bool {
switch r {
case DisconnectReasonUnknown,
DisconnectReasonGraceful,
DisconnectReasonClientClosed,
DisconnectReasonServerShutdown,
DisconnectReasonNetworkError,
DisconnectReasonProtocolError,
DisconnectReasonWorkspaceStopped,
DisconnectReasonControlPlaneLost:
return true
default:
return false
}
}
// Expected reports whether a disconnect with this reason is part of
// normal operation. Operators can use this to split dashboards or alerts
// into "expected" and "investigate" buckets without enumerating every
// reason.
func (r DisconnectReason) Expected() bool {
switch r {
case DisconnectReasonGraceful,
DisconnectReasonClientClosed,
DisconnectReasonServerShutdown,
DisconnectReasonWorkspaceStopped:
return true
case DisconnectReasonUnknown,
DisconnectReasonNetworkError,
DisconnectReasonProtocolError,
DisconnectReasonControlPlaneLost:
return false
default:
// Unknown reason values are treated as not expected so that
// new emit sites that forget to classify themselves surface
// in the "investigate" bucket by default.
return false
}
}
// DisconnectInitiator identifies which side caused the disconnect. It
// pairs with DisconnectReason: the reason describes what happened, the
// initiator describes who started it.
type DisconnectInitiator string
const (
// DisconnectInitiatorUnknown means the disconnect site cannot
// attribute the close to a specific side. Avoid this where possible.
DisconnectInitiatorUnknown DisconnectInitiator = ""
// DisconnectInitiatorClient means the user-facing side (CLI, VS Code
// extension, JetBrains plugin, Coder Desktop) closed the connection.
DisconnectInitiatorClient DisconnectInitiator = "client"
// DisconnectInitiatorAgent means the workspace agent closed the
// connection.
DisconnectInitiatorAgent DisconnectInitiator = "agent"
// DisconnectInitiatorServer means coderd (or a workspace proxy)
// closed the connection.
DisconnectInitiatorServer DisconnectInitiator = "server"
// DisconnectInitiatorNetwork means an underlying network or transport
// fault caused the close. Neither end deliberately initiated it.
DisconnectInitiatorNetwork DisconnectInitiator = "network"
)
func (i DisconnectInitiator) SlogField() slog.Field {
return slog.F("disconnect_initiator", i)
}
// Valid reports whether i is a known DisconnectInitiator.
func (i DisconnectInitiator) Valid() bool {
switch i {
case DisconnectInitiatorUnknown,
DisconnectInitiatorClient,
DisconnectInitiatorAgent,
DisconnectInitiatorServer,
DisconnectInitiatorNetwork:
return true
default:
return false
}
}
// ConnectionDirection identifies which layer a disconnect log belongs to.
// It tells operators at a glance whether a log is about the control plane
// (server to agent) or the data plane (agent to client).
type ConnectionDirection string
func (d ConnectionDirection) SlogField() slog.Field {
return slog.F("connect_type", d)
}
const (
// ConnectionDirectionServerToAgent is the control-plane connection
// between coderd and the workspace agent (coordination RPC, DERP map
// subscriber, agent runLoop).
ConnectionDirectionServerToAgent ConnectionDirection = "server_to_agent"
// ConnectionDirectionAgentToClient is a data-plane session between
// the workspace agent and a user's client (SSH, reconnecting PTY,
// JetBrains port-forwarding).
ConnectionDirectionAgentToClient ConnectionDirection = "agent_to_client"
// ConnectionDirectionClientToServer is a connection from a user's
// client to coderd (e.g. the CLI's WebSocket to the coordinator).
// Not yet instrumented.
ConnectionDirectionClientToServer ConnectionDirection = "client_to_server"
)
// ConnectionMethod describes the network path a workspace connection
// took at the moment a disconnect log was emitted. It is intended for
// observability only; do not switch behavior on it.
type ConnectionMethod string
func (m ConnectionMethod) SlogField() slog.Field {
return slog.F("connection_method", m)
}
const (
// ConnectionMethodUnknown means the disconnect site does not have
// the information to determine the connection path.
ConnectionMethodUnknown ConnectionMethod = ""
// ConnectionMethodDirect means the peers were communicating over a
// direct, peer-to-peer connection (NAT-traversed via STUN).
ConnectionMethodDirect ConnectionMethod = "direct"
// ConnectionMethodDERP means the peers were communicating through a
// DERP relay rather than directly.
ConnectionMethodDERP ConnectionMethod = "derp"
)
// Valid reports whether m is a known ConnectionMethod.
func (m ConnectionMethod) Valid() bool {
switch m {
case ConnectionMethodUnknown,
ConnectionMethodDirect,
ConnectionMethodDERP:
return true
default:
return false
}
}