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

Commite5ce382

Browse files
authored
feat: add IsCoderConnectRunning to workspacesdk (#17361)
Adds `IsCoderConnectRunning()` to the workspacesdk. This will support the `coder` CLI being able to use CoderConnect when it's running.part of#16828
1 parent39b9d23 commite5ce382

File tree

2 files changed

+137
-1
lines changed

2 files changed

+137
-1
lines changed

‎codersdk/workspacesdk/workspacesdk.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,19 @@ func init() {
128128
}
129129
}
130130

131+
typeresolverinterface {
132+
LookupIP(ctx context.Context,network,hoststring) ([]net.IP,error)
133+
}
134+
131135
typeClientstruct {
132136
client*codersdk.Client
137+
138+
// overridden in tests
139+
resolverresolver
133140
}
134141

135142
funcNew(c*codersdk.Client)*Client {
136-
return&Client{client:c}
143+
return&Client{client:c,resolver:net.DefaultResolver}
137144
}
138145

139146
// AgentConnectionInfo returns required information for establishing
@@ -384,3 +391,46 @@ func (c *Client) AgentReconnectingPTY(ctx context.Context, opts WorkspaceAgentRe
384391
}
385392
returnwebsocket.NetConn(context.Background(),conn,websocket.MessageBinary),nil
386393
}
394+
395+
typeCoderConnectQueryOptionsstruct {
396+
HostnameSuffixstring
397+
}
398+
399+
// IsCoderConnectRunning checks if Coder Connect (OS level tunnel to workspaces) is running on the system. If you
400+
// already know the hostname suffix your deployment uses, you can pass it in the CoderConnectQueryOptions to avoid an
401+
// API call to AgentConnectionInfoGeneric.
402+
func (c*Client)IsCoderConnectRunning(ctx context.Context,oCoderConnectQueryOptions) (bool,error) {
403+
suffix:=o.HostnameSuffix
404+
ifsuffix=="" {
405+
info,err:=c.AgentConnectionInfoGeneric(ctx)
406+
iferr!=nil {
407+
returnfalse,xerrors.Errorf("get agent connection info: %w",err)
408+
}
409+
suffix=info.HostnameSuffix
410+
}
411+
domainName:=fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString,suffix)
412+
vardnsError*net.DNSError
413+
ips,err:=c.resolver.LookupIP(ctx,"ip6",domainName)
414+
ifxerrors.As(err,&dnsError) {
415+
ifdnsError.IsNotFound {
416+
returnfalse,nil
417+
}
418+
}
419+
iferr!=nil {
420+
returnfalse,xerrors.Errorf("lookup DNS %s: %w",domainName,err)
421+
}
422+
423+
// The returned IP addresses are probably from the Coder Connect DNS server, but there are sometimes weird captive
424+
// internet setups where the DNS server is configured to return an address for any IP query. So, to avoid false
425+
// positives, check if we can find an address from our service prefix.
426+
for_,ip:=rangeips {
427+
addr,ok:=netip.AddrFromSlice(ip)
428+
if!ok {
429+
continue
430+
}
431+
iftailnet.CoderServicePrefix.AsNetip().Contains(addr) {
432+
returntrue,nil
433+
}
434+
}
435+
returnfalse,nil
436+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package workspacesdk
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"net/http"
8+
"net/http/httptest"
9+
"net/url"
10+
"testing"
11+
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
"golang.org/x/xerrors"
15+
16+
"github.com/coder/coder/v2/coderd/httpapi"
17+
"github.com/coder/coder/v2/codersdk"
18+
"github.com/coder/coder/v2/testutil"
19+
20+
"tailscale.com/net/tsaddr"
21+
22+
"github.com/coder/coder/v2/tailnet"
23+
)
24+
25+
funcTestClient_IsCoderConnectRunning(t*testing.T) {
26+
t.Parallel()
27+
ctx:=testutil.Context(t,testutil.WaitShort)
28+
29+
srv:=httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter,r*http.Request) {
30+
assert.Equal(t,"/api/v2/workspaceagents/connection",r.URL.Path)
31+
httpapi.Write(ctx,rw,http.StatusOK,AgentConnectionInfo{
32+
HostnameSuffix:"test",
33+
})
34+
}))
35+
defersrv.Close()
36+
37+
apiURL,err:=url.Parse(srv.URL)
38+
require.NoError(t,err)
39+
sdkClient:=codersdk.New(apiURL)
40+
client:=New(sdkClient)
41+
42+
// Right name, right IP
43+
expectedName:=fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString,"test")
44+
client.resolver=&fakeResolver{t:t,hostMap:map[string][]net.IP{
45+
expectedName: {net.ParseIP(tsaddr.CoderServiceIPv6().String())},
46+
}}
47+
48+
result,err:=client.IsCoderConnectRunning(ctx,CoderConnectQueryOptions{})
49+
require.NoError(t,err)
50+
require.True(t,result)
51+
52+
// Wrong name
53+
result,err=client.IsCoderConnectRunning(ctx,CoderConnectQueryOptions{HostnameSuffix:"coder"})
54+
require.NoError(t,err)
55+
require.False(t,result)
56+
57+
// Not found
58+
client.resolver=&fakeResolver{t:t,err:&net.DNSError{IsNotFound:true}}
59+
result,err=client.IsCoderConnectRunning(ctx,CoderConnectQueryOptions{})
60+
require.NoError(t,err)
61+
require.False(t,result)
62+
63+
// Some other error
64+
client.resolver=&fakeResolver{t:t,err:xerrors.New("a bad thing happened")}
65+
_,err=client.IsCoderConnectRunning(ctx,CoderConnectQueryOptions{})
66+
require.Error(t,err)
67+
68+
// Right name, wrong IP
69+
client.resolver=&fakeResolver{t:t,hostMap:map[string][]net.IP{
70+
expectedName: {net.ParseIP("2001::34")},
71+
}}
72+
result,err=client.IsCoderConnectRunning(ctx,CoderConnectQueryOptions{})
73+
require.NoError(t,err)
74+
require.False(t,result)
75+
}
76+
77+
typefakeResolverstruct {
78+
t testing.TB
79+
hostMapmap[string][]net.IP
80+
errerror
81+
}
82+
83+
func (f*fakeResolver)LookupIP(_ context.Context,network,hoststring) ([]net.IP,error) {
84+
assert.Equal(f.t,"ip6",network)
85+
returnf.hostMap[host],f.err
86+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp