Skip to content

Commit 237be67

Browse files
committed
Merge branch 'main' into lilac/dynamic-parameters-endpoint
2 parents 86eadcd + b000a7a commit 237be67

File tree

72 files changed

+1555
-967
lines changed

Some content is hidden

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

72 files changed

+1555
-967
lines changed

.github/actions/setup-tf/action.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ runs:
77
- name: Install Terraform
88
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
99
with:
10-
terraform_version: 1.11.2
10+
terraform_version: 1.11.3
1111
terraform_wrapper: false

agent/agent.go

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,9 +1186,9 @@ func (a *agent) createOrUpdateNetwork(manifestOK, networkOK *checkpoint) func(co
11861186
network := a.network
11871187
a.closeMutex.Unlock()
11881188
if network == nil {
1189-
keySeed, err := WorkspaceKeySeed(manifest.WorkspaceID, manifest.AgentName)
1189+
keySeed, err := SSHKeySeed(manifest.OwnerName, manifest.WorkspaceName, manifest.AgentName)
11901190
if err != nil {
1191-
return xerrors.Errorf("generate seed from workspace id: %w", err)
1191+
return xerrors.Errorf("generate SSH key seed: %w", err)
11921192
}
11931193
// use the graceful context here, because creating the tailnet is not itself tied to the
11941194
// agent API.
@@ -1518,14 +1518,11 @@ func (a *agent) runCoordinator(ctx context.Context, tClient tailnetproto.DRPCTai
15181518
a.logger.Info(ctx, "connected to coordination RPC")
15191519

15201520
// This allows the Close() routine to wait for the coordinator to gracefully disconnect.
1521-
a.closeMutex.Lock()
1522-
if a.isClosed() {
1523-
return nil
1521+
disconnected := a.setCoordDisconnected()
1522+
if disconnected == nil {
1523+
return nil // already closed by something else
15241524
}
1525-
disconnected := make(chan struct{})
1526-
a.coordDisconnected = disconnected
15271525
defer close(disconnected)
1528-
a.closeMutex.Unlock()
15291526

15301527
ctrl := tailnet.NewAgentCoordinationController(a.logger, network)
15311528
coordination := ctrl.New(coordinate)
@@ -1547,6 +1544,17 @@ func (a *agent) runCoordinator(ctx context.Context, tClient tailnetproto.DRPCTai
15471544
return <-errCh
15481545
}
15491546

1547+
func (a *agent) setCoordDisconnected() chan struct{} {
1548+
a.closeMutex.Lock()
1549+
defer a.closeMutex.Unlock()
1550+
if a.isClosed() {
1551+
return nil
1552+
}
1553+
disconnected := make(chan struct{})
1554+
a.coordDisconnected = disconnected
1555+
return disconnected
1556+
}
1557+
15501558
// runDERPMapSubscriber runs a coordinator and returns if a reconnect should occur.
15511559
func (a *agent) runDERPMapSubscriber(ctx context.Context, tClient tailnetproto.DRPCTailnetClient24, network *tailnet.Conn) error {
15521560
defer a.logger.Debug(ctx, "disconnected from derp map RPC")
@@ -2068,12 +2076,31 @@ func PrometheusMetricsHandler(prometheusRegistry *prometheus.Registry, logger sl
20682076
})
20692077
}
20702078

2071-
// WorkspaceKeySeed converts a WorkspaceID UUID and agent name to an int64 hash.
2079+
// SSHKeySeed converts an owner userName, workspaceName and agentName to an int64 hash.
20722080
// This uses the FNV-1a hash algorithm which provides decent distribution and collision
20732081
// resistance for string inputs.
2074-
func WorkspaceKeySeed(workspaceID uuid.UUID, agentName string) (int64, error) {
2082+
//
2083+
// Why owner username, workspace name, and agent name? These are the components that are used in hostnames for the
2084+
// workspace over SSH, and so we want the workspace to have a stable key with respect to these. We don't use the
2085+
// respective UUIDs. The workspace UUID would be different if you delete and recreate a workspace with the same name.
2086+
// The agent UUID is regenerated on each build. Since Coder's Tailnet networking is handling the authentication, we
2087+
// should not be showing users warnings about host SSH keys.
2088+
func SSHKeySeed(userName, workspaceName, agentName string) (int64, error) {
20752089
h := fnv.New64a()
2076-
_, err := h.Write(workspaceID[:])
2090+
_, err := h.Write([]byte(userName))
2091+
if err != nil {
2092+
return 42, err
2093+
}
2094+
// null separators between strings so that (dog, foodstuff) is distinct from (dogfood, stuff)
2095+
_, err = h.Write([]byte{0})
2096+
if err != nil {
2097+
return 42, err
2098+
}
2099+
_, err = h.Write([]byte(workspaceName))
2100+
if err != nil {
2101+
return 42, err
2102+
}
2103+
_, err = h.Write([]byte{0})
20772104
if err != nil {
20782105
return 42, err
20792106
}

cli/ssh_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ func TestSSH(t *testing.T) {
479479
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
480480
defer cancel()
481481

482+
user, err := client.User(ctx, codersdk.Me)
483+
require.NoError(t, err)
484+
482485
inv, root := clitest.New(t, "ssh", "--stdio", workspace.Name)
483486
clitest.SetupConfig(t, client, root)
484487
inv.Stdin = clientOutput
@@ -490,7 +493,7 @@ func TestSSH(t *testing.T) {
490493
assert.NoError(t, err)
491494
})
492495

493-
keySeed, err := agent.WorkspaceKeySeed(workspace.ID, "dev")
496+
keySeed, err := agent.SSHKeySeed(user.Username, workspace.Name, "dev")
494497
assert.NoError(t, err)
495498

496499
signer, err := agentssh.CoderSigner(keySeed)

docs/admin/networking/index.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,64 @@ low-latency browser experiences for geo-distributed teams.
197197

198198
To learn more, see [Workspace Proxies](./workspace-proxies.md).
199199

200+
## Latency
201+
202+
Coder measures and reports several types of latency, providing insights into the performance of your deployment. Understanding these metrics can help you diagnose issues and optimize the user experience.
203+
204+
There are three main types of latency metrics for your Coder deployment:
205+
206+
- Dashboard-to-server latency:
207+
208+
The Coder UI measures round-trip time to the Coder server or workspace proxy using built-in browser timing capabilities.
209+
210+
This appears in the user interface next to your username, showing how responsive the dashboard is.
211+
212+
- Workspace connection latency:
213+
214+
The latency shown on the workspace dashboard measures the round-trip time between the workspace agent and its DERP relay server.
215+
216+
This metric is displayed in milliseconds on the workspace dashboard and specifically shows the agent-to-relay latency, not direct P2P connections.
217+
218+
To estimate the total end-to-end latency experienced by a user, add the dashboard-to-server latency to this agent-to-relay latency.
219+
220+
- Database latency:
221+
222+
For administrators, Coder monitors and reports database query performance in the health dashboard.
223+
224+
### How latency is classified
225+
226+
Latency measurements are color-coded in the dashboard:
227+
228+
- **Green** (<150ms): Good performance.
229+
- **Yellow** (150-300ms): Moderate latency that might affect user experience.
230+
- **Red** (>300ms): High latency that will noticeably affect user experience.
231+
232+
### View latency information
233+
234+
- **Dashboard**: The global latency indicator appears in the top navigation bar.
235+
- **Workspace list**: Each workspace shows its connection latency.
236+
- **Health dashboard**: Administrators can view advanced metrics including database latency.
237+
- **CLI**: Use `coder ping <workspace>` to measure and analyze latency from the command line.
238+
239+
### Factors that affect latency
240+
241+
- **Geographic distance**: Physical distance between users, Coder server, and workspaces.
242+
- **Network connectivity**: Quality of internet connections and routing.
243+
- **Infrastructure**: Cloud provider regions and network optimization.
244+
- **P2P connectivity**: Whether direct connections can be established or relays are needed.
245+
246+
### How to optimize latency
247+
248+
To improve latency and user experience:
249+
250+
- **Deploy workspace proxies**: Place [proxies](./workspace-proxies.md) in regions closer to users, connecting back to your single Coder server deployment.
251+
- **Use P2P connections**: Ensure network configurations permit direct connections.
252+
- **Strategic placement**: Deploy your Coder server in a region where most users work.
253+
- **Network configuration**: Optimize routing between users and workspaces.
254+
- **Check firewall rules**: Ensure they don't block necessary Coder connections.
255+
256+
For help troubleshooting connection issues, including latency problems, refer to the [networking troubleshooting guide](./troubleshooting.md).
257+
200258
## Up next
201259

202260
- Learn about [Port Forwarding](./port-forwarding.md)

docs/admin/networking/workspace-proxies.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,15 @@ up to 60 seconds.
208208

209209
![Workspace proxy picker](../../images/admin/networking/workspace-proxies/ws-proxy-picker.png)
210210

211+
## Multiple workspace proxies
212+
213+
When multiple workspace proxies are deployed:
214+
215+
- The browser measures latency to each available proxy independently.
216+
- Users can select their preferred proxy from the dashboard.
217+
- The system can automatically select the lowest-latency proxy.
218+
- The dashboard latency indicator shows latency to the currently selected proxy.
219+
211220
## Observability
212221

213222
Coder workspace proxy exports metrics via the HTTP endpoint, which can be

docs/tutorials/best-practices/scale-coder.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ Although Coder Server persists no internal state, it operates as a proxy for end
126126
users to their workspaces in two capacities:
127127

128128
1. As an HTTP proxy when they access workspace applications in their browser via
129-
the Coder Dashboard.
129+
the Coder Dashboard.
130130

131131
1. As a DERP proxy when establishing tunneled connections with CLI tools like
132-
`coder ssh`, `coder port-forward`, and others, and with desktop IDEs.
132+
`coder ssh`, `coder port-forward`, and others, and with desktop IDEs.
133133

134134
Stopping a Coder Server instance will (momentarily) disconnect any users
135135
currently connecting through that instance. Adding a new instance is not

dogfood/coder/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,9 @@ RUN apt-get update --quiet && apt-get install --yes \
198198
# Configure FIPS-compliant policies
199199
update-crypto-policies --set FIPS
200200

201-
# NOTE: In scripts/Dockerfile.base we specifically install Terraform version 1.11.2.
201+
# NOTE: In scripts/Dockerfile.base we specifically install Terraform version 1.11.3.
202202
# Installing the same version here to match.
203-
RUN wget -O /tmp/terraform.zip "https://releases.hashicorp.com/terraform/1.11.2/terraform_1.11.2_linux_amd64.zip" && \
203+
RUN wget -O /tmp/terraform.zip "https://releases.hashicorp.com/terraform/1.11.3/terraform_1.11.3_linux_amd64.zip" && \
204204
unzip /tmp/terraform.zip -d /usr/local/bin && \
205205
rm -f /tmp/terraform.zip && \
206206
chmod +x /usr/local/bin/terraform && \

install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ EOF
273273
main() {
274274
MAINLINE=1
275275
STABLE=0
276-
TERRAFORM_VERSION="1.11.2"
276+
TERRAFORM_VERSION="1.11.3"
277277

278278
if [ "${TRACE-}" ]; then
279279
set -x

provisioner/terraform/install.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var (
2222
// when Terraform is not available on the system.
2323
// NOTE: Keep this in sync with the version in scripts/Dockerfile.base.
2424
// NOTE: Keep this in sync with the version in install.sh.
25-
TerraformVersion = version.Must(version.NewVersion("1.11.2"))
25+
TerraformVersion = version.Must(version.NewVersion("1.11.3"))
2626

2727
minTerraformVersion = version.Must(version.NewVersion("1.1.0"))
2828
maxTerraformVersion = version.Must(version.NewVersion("1.11.9")) // use .9 to automatically allow patch releases

provisioner/terraform/testdata/resources/presets/presets.tfplan.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)