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

Commit8f768f8

Browse files
authored
feat: Workspace Proxy picker show latency to each proxy (#7486)
* chore: Add cors to workspace proxies to allow for latency checks* Add latency check to wsproxyUse performance API timings.- Fix cors and timing headers- Accept custom headers
1 parent640fcf4 commit8f768f8

22 files changed

+347
-28
lines changed

‎coderd/coderd.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,17 @@ func New(options *Options) *API {
806806
return []string{}
807807
})
808808
r.NotFound(cspMW(compressHandler(http.HandlerFunc(api.siteHandler.ServeHTTP))).ServeHTTP)
809+
810+
// This must be before all middleware to improve the response time.
811+
// So make a new router, and mount the old one as the root.
812+
rootRouter:=chi.NewRouter()
813+
// This is the only route we add before all the middleware.
814+
// We want to time the latency of the request, so any middleware will
815+
// interfere with that timing.
816+
rootRouter.Get("/latency-check",LatencyCheck(api.AccessURL))
817+
rootRouter.Mount("/",r)
818+
api.RootHandler=rootRouter
819+
809820
returnapi
810821
}
811822

‎coderd/coderd_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,15 @@ func TestDERPLatencyCheck(t *testing.T) {
124124
require.Equal(t,http.StatusOK,res.StatusCode)
125125
}
126126

127+
funcTestFastLatencyCheck(t*testing.T) {
128+
t.Parallel()
129+
client:=coderdtest.New(t,nil)
130+
res,err:=client.Request(context.Background(),http.MethodGet,"/latency-check",nil)
131+
require.NoError(t,err)
132+
deferres.Body.Close()
133+
require.Equal(t,http.StatusOK,res.StatusCode)
134+
}
135+
127136
funcTestHealthz(t*testing.T) {
128137
t.Parallel()
129138
client:=coderdtest.New(t,nil)

‎coderd/latencycheck.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package coderd
2+
3+
import (
4+
"net/http"
5+
"net/url"
6+
"strings"
7+
)
8+
9+
funcLatencyCheck(allowedOrigins...*url.URL) http.HandlerFunc {
10+
allowed:=make([]string,0,len(allowedOrigins))
11+
for_,origin:=rangeallowedOrigins {
12+
// Allow the origin without a path
13+
tmp:=*origin
14+
tmp.Path=""
15+
allowed=append(allowed,strings.TrimSuffix(origin.String(),"/"))
16+
}
17+
origins:=strings.Join(allowed,",")
18+
returnfunc(rw http.ResponseWriter,r*http.Request) {
19+
// Allowing timing information to be shared. This allows the browser
20+
// to exclude TLS handshake timing.
21+
rw.Header().Set("Timing-Allow-Origin",origins)
22+
rw.WriteHeader(http.StatusOK)
23+
}
24+
}

‎enterprise/coderd/workspaceproxy_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ func TestRegions(t *testing.T) {
175175
})
176176

177177
t.Run("GoingAway",func(t*testing.T) {
178+
t.Skip("This is flakey in CI because it relies on internal go routine timing. Should refactor.")
178179
t.Parallel()
179180

180181
dv:=coderdtest.DeploymentValues(t)

‎enterprise/wsproxy/wsproxy.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919

2020
"cdr.dev/slog"
2121
"github.com/coder/coder/buildinfo"
22+
"github.com/coder/coder/coderd"
2223
"github.com/coder/coder/coderd/httpapi"
2324
"github.com/coder/coder/coderd/httpmw"
2425
"github.com/coder/coder/coderd/tracing"
@@ -186,6 +187,21 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
186187
SecureAuthCookie:opts.SecureAuthCookie,
187188
}
188189

190+
// The primary coderd dashboard needs to make some GET requests to
191+
// the workspace proxies to check latency.
192+
corsMW:=cors.Handler(cors.Options{
193+
AllowedOrigins: []string{
194+
// Allow the dashboard to make requests to the proxy for latency
195+
// checks.
196+
opts.DashboardURL.String(),
197+
},
198+
// Only allow GET requests for latency checks.
199+
AllowedMethods: []string{http.MethodOptions,http.MethodGet},
200+
AllowedHeaders: []string{"Accept","Content-Type","X-LATENCY-CHECK","X-CSRF-TOKEN"},
201+
// Do not send any cookies
202+
AllowCredentials:false,
203+
})
204+
189205
// Routes
190206
apiRateLimiter:=httpmw.RateLimit(opts.APIRateLimit,time.Minute)
191207
// Persistent middlewares to all routes
@@ -198,20 +214,7 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
198214
httpmw.ExtractRealIP(s.Options.RealIPConfig),
199215
httpmw.Logger(s.Logger),
200216
httpmw.Prometheus(s.PrometheusRegistry),
201-
// The primary coderd dashboard needs to make some GET requests to
202-
// the workspace proxies to check latency.
203-
cors.Handler(cors.Options{
204-
AllowedOrigins: []string{
205-
// Allow the dashboard to make requests to the proxy for latency
206-
// checks.
207-
opts.DashboardURL.String(),
208-
},
209-
// Only allow GET requests for latency checks.
210-
AllowedMethods: []string{http.MethodGet},
211-
AllowedHeaders: []string{"Accept","Content-Type"},
212-
// Do not send any cookies
213-
AllowCredentials:false,
214-
}),
217+
corsMW,
215218

216219
// HandleSubdomain is a middleware that handles all requests to the
217220
// subdomain-based workspace apps.
@@ -260,6 +263,13 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
260263
})
261264
})
262265

266+
// See coderd/coderd.go for why we need this.
267+
rootRouter:=chi.NewRouter()
268+
// Make sure to add the cors middleware to the latency check route.
269+
rootRouter.Get("/latency-check",corsMW(coderd.LatencyCheck(s.DashboardURL,s.AppServer.AccessURL)).ServeHTTP)
270+
rootRouter.Mount("/",r)
271+
s.Handler=rootRouter
272+
263273
returns,nil
264274
}
265275

‎site/jest.setup.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,33 @@ import "jest-location-mock"
66
import{TextEncoder,TextDecoder}from"util"
77
import{Blob}from"buffer"
88
importjestFetchMockfrom"jest-fetch-mock"
9+
import{ProxyLatencyReport}from"contexts/useProxyLatency"
10+
import{RegionsResponse}from"api/typesGenerated"
911

1012
jestFetchMock.enableMocks()
1113

14+
// useProxyLatency does some http requests to determine latency.
15+
// This would fail unit testing, or at least make it very slow with
16+
// actual network requests. So just globally mock this hook.
17+
jest.mock("contexts/useProxyLatency",()=>({
18+
useProxyLatency:(proxies?:RegionsResponse)=>{
19+
if(!proxies){
20+
return{}asRecord<string,ProxyLatencyReport>
21+
}
22+
23+
returnproxies.regions.reduce((acc,proxy)=>{
24+
acc[proxy.id]={
25+
accurate:true,
26+
// Return a constant latency of 8ms.
27+
// If you make this random it could break stories.
28+
latencyMS:8,
29+
at:newDate(),
30+
}
31+
returnacc
32+
},{}asRecord<string,ProxyLatencyReport>)
33+
},
34+
}))
35+
1236
global.TextEncoder=TextEncoder
1337
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Polyfill for jsdom
1438
global.TextDecoder=TextDecoderasany

‎site/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"dependencies": {
3131
"@emoji-mart/data":"1.0.5",
3232
"@emoji-mart/react":"1.0.1",
33+
"@fastly/performance-observer-polyfill":"^2.0.0",
3334
"@emotion/react":"^11.10.8",
3435
"@emotion/styled":"^11.10.8",
3536
"@fontsource/ibm-plex-mono":"4.5.10",

‎site/src/components/AppLink/AppLink.stories.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
MockWorkspace,
66
MockWorkspaceAgent,
77
MockWorkspaceApp,
8+
MockProxyLatencies,
89
}from"testHelpers/entities"
910
import{AppLink,AppLinkProps}from"./AppLink"
1011
import{ProxyContext,getPreferredProxy}from"contexts/ProxyContext"
@@ -17,6 +18,7 @@ export default {
1718
constTemplate:Story<AppLinkProps>=(args)=>(
1819
<ProxyContext.Provider
1920
value={{
21+
proxyLatencies:MockProxyLatencies,
2022
proxy:getPreferredProxy(MockWorkspaceProxies,MockPrimaryWorkspaceProxy),
2123
proxies:MockWorkspaceProxies,
2224
isLoading:false,

‎site/src/components/Resources/AgentLatency.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import{useRef,useState,FC}from"react"
22
import{makeStyles,useTheme}from"@mui/styles"
3+
import{Theme}from"@mui/material/styles"
34
import{
45
HelpTooltipText,
56
HelpPopover,
67
HelpTooltipTitle,
78
}from"components/Tooltips/HelpTooltip"
89
import{Stack}from"components/Stack/Stack"
910
import{WorkspaceAgent,DERPRegion}from"api/typesGenerated"
10-
import{Theme}from"@mui/material/styles"
11+
import{getLatencyColor}from"utils/colors"
1112

1213
constgetDisplayLatency=(theme:Theme,agent:WorkspaceAgent)=>{
1314
// Find the right latency to display
@@ -22,17 +23,9 @@ const getDisplayLatency = (theme: Theme, agent: WorkspaceAgent) => {
2223
returnundefined
2324
}
2425

25-
// Get the color
26-
letcolor=theme.palette.success.light
27-
if(latency.latency_ms>=150&&latency.latency_ms<300){
28-
color=theme.palette.warning.light
29-
}elseif(latency.latency_ms>=300){
30-
color=theme.palette.error.light
31-
}
32-
3326
return{
3427
...latency,
35-
color,
28+
color:getLatencyColor(theme,latency.latency_ms),
3629
}
3730
}
3831

‎site/src/components/Resources/AgentRow.stories.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
MockWorkspaceAgentStartTimeout,
1717
MockWorkspaceAgentTimeout,
1818
MockWorkspaceApp,
19+
MockProxyLatencies,
1920
}from"testHelpers/entities"
2021
import{AgentRow,AgentRowProps}from"./AgentRow"
2122
import{ProxyContext,getPreferredProxy}from"contexts/ProxyContext"
@@ -56,6 +57,7 @@ const TemplateFC = (
5657
return(
5758
<ProxyContext.Provider
5859
value={{
60+
proxyLatencies:MockProxyLatencies,
5961
proxy:getPreferredProxy(proxies,selectedProxy),
6062
proxies:proxies,
6163
isLoading:false,

‎site/src/components/Resources/ResourceCard.stories.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import{action}from"@storybook/addon-actions"
22
import{Story}from"@storybook/react"
3-
import{MockWorkspace,MockWorkspaceResource}from"testHelpers/entities"
3+
import{
4+
MockProxyLatencies,
5+
MockWorkspace,
6+
MockWorkspaceResource,
7+
}from"testHelpers/entities"
48
import{AgentRow}from"./AgentRow"
59
import{ResourceCard,ResourceCardProps}from"./ResourceCard"
610
import{ProxyContext,getPreferredProxy}from"contexts/ProxyContext"
@@ -18,6 +22,7 @@ Example.args = {
1822
agentRow:(agent)=>(
1923
<ProxyContext.Provider
2024
value={{
25+
proxyLatencies:MockProxyLatencies,
2126
proxy:getPreferredProxy([],undefined),
2227
proxies:[],
2328
isLoading:false,
@@ -84,6 +89,7 @@ BunchOfMetadata.args = {
8489
agentRow:(agent)=>(
8590
<ProxyContext.Provider
8691
value={{
92+
proxyLatencies:MockProxyLatencies,
8793
proxy:getPreferredProxy([],undefined),
8894
proxies:[],
8995
isLoading:false,

‎site/src/components/Workspace/Workspace.stories.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Workspace, WorkspaceErrors, WorkspaceProps } from "./Workspace"
77
import{withReactContext}from"storybook-react-context"
88
importEventSourcefrom"eventsourcemock"
99
import{ProxyContext,getPreferredProxy}from"contexts/ProxyContext"
10+
import{MockProxyLatencies}from"../../testHelpers/entities"
1011

1112
exportdefault{
1213
title:"components/Workspace",
@@ -26,6 +27,7 @@ export default {
2627
constTemplate:Story<WorkspaceProps>=(args)=>(
2728
<ProxyContext.Provider
2829
value={{
30+
proxyLatencies:MockProxyLatencies,
2931
proxy:getPreferredProxy([],undefined),
3032
proxies:[],
3133
isLoading:false,

‎site/src/contexts/ProxyContext.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import {
99
useContext,
1010
useState,
1111
}from"react"
12+
import{ProxyLatencyReport,useProxyLatency}from"./useProxyLatency"
1213

1314
interfaceProxyContextValue{
1415
proxy:PreferredProxy
1516
proxies?:Region[]
17+
proxyLatencies?:Record<string,ProxyLatencyReport>
1618
// isfetched is true when the proxy api call is complete.
1719
isFetched:boolean
1820
// isLoading is true if the proxy is in the process of being fetched.
@@ -72,6 +74,10 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
7274
},
7375
})
7476

77+
// Everytime we get a new proxiesResponse, update the latency check
78+
// to each workspace proxy.
79+
constproxyLatencies=useProxyLatency(proxiesResp)
80+
7581
constsetAndSaveProxy=(
7682
selectedProxy?:Region,
7783
// By default the proxies come from the api call above.
@@ -95,6 +101,7 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
95101
return(
96102
<ProxyContext.Provider
97103
value={{
104+
proxyLatencies:proxyLatencies,
98105
proxy:experimentEnabled
99106
?proxy
100107
:{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp