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
This commit is contained in:
Spike Curtis
2025-04-14 09:47:46 +04:00
committed by GitHub
parent 39b9d23d96
commit e5ce3824ca
2 changed files with 137 additions and 1 deletions
+51 -1
View File
@@ -128,12 +128,19 @@ func init() {
}
}
type resolver interface {
LookupIP(ctx context.Context, network, host string) ([]net.IP, error)
}
type Client struct {
client *codersdk.Client
// overridden in tests
resolver resolver
}
func New(c *codersdk.Client) *Client {
return &Client{client: c}
return &Client{client: c, resolver: net.DefaultResolver}
}
// AgentConnectionInfo returns required information for establishing
@@ -384,3 +391,46 @@ func (c *Client) AgentReconnectingPTY(ctx context.Context, opts WorkspaceAgentRe
}
return websocket.NetConn(context.Background(), conn, websocket.MessageBinary), nil
}
type CoderConnectQueryOptions struct {
HostnameSuffix string
}
// IsCoderConnectRunning checks if Coder Connect (OS level tunnel to workspaces) is running on the system. If you
// already know the hostname suffix your deployment uses, you can pass it in the CoderConnectQueryOptions to avoid an
// API call to AgentConnectionInfoGeneric.
func (c *Client) IsCoderConnectRunning(ctx context.Context, o CoderConnectQueryOptions) (bool, error) {
suffix := o.HostnameSuffix
if suffix == "" {
info, err := c.AgentConnectionInfoGeneric(ctx)
if err != nil {
return false, xerrors.Errorf("get agent connection info: %w", err)
}
suffix = info.HostnameSuffix
}
domainName := fmt.Sprintf(tailnet.IsCoderConnectEnabledFmtString, suffix)
var dnsError *net.DNSError
ips, err := c.resolver.LookupIP(ctx, "ip6", domainName)
if xerrors.As(err, &dnsError) {
if dnsError.IsNotFound {
return false, nil
}
}
if err != nil {
return false, xerrors.Errorf("lookup DNS %s: %w", domainName, err)
}
// The returned IP addresses are probably from the Coder Connect DNS server, but there are sometimes weird captive
// internet setups where the DNS server is configured to return an address for any IP query. So, to avoid false
// positives, check if we can find an address from our service prefix.
for _, ip := range ips {
addr, ok := netip.AddrFromSlice(ip)
if !ok {
continue
}
if tailnet.CoderServicePrefix.AsNetip().Contains(addr) {
return true, nil
}
}
return false, nil
}