Skip to content

feat: Add vscodeipc subcommand for VS Code Extension #5326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Dec 18, 2022
Merged
1 change: 1 addition & 0 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func Core() []*cobra.Command {
users(),
versionCmd(),
workspaceAgent(),
vscodeipcCmd(),
}
}

Expand Down
4 changes: 2 additions & 2 deletions cli/speedtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func speedtest() *cobra.Command {
return ctx.Err()
case <-ticker.C:
}
dur, err := conn.Ping(ctx)
dur, p2p, err := conn.Ping(ctx)
if err != nil {
continue
}
Expand All @@ -80,7 +80,7 @@ func speedtest() *cobra.Command {
continue
}
peer := status.Peer[status.Peers()[0]]
if peer.CurAddr == "" && direct {
if !p2p && direct {
cmd.Printf("Waiting for a direct connection... (%dms via %s)\n", dur.Milliseconds(), peer.Relay)
continue
}
Expand Down
2 changes: 2 additions & 0 deletions cli/ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ func setupWorkspaceForAgent(t *testing.T, mutate func([]*proto.Agent) []*proto.A
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
workspace, err := client.Workspace(context.Background(), workspace.ID)
require.NoError(t, err)

return client, workspace, agentToken
}
Expand Down
88 changes: 88 additions & 0 deletions cli/vscodeipc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package cli

import (
"fmt"
"net"
"net/http"
"net/url"

"github.com/google/uuid"
"github.com/spf13/cobra"
"golang.org/x/xerrors"

"github.com/coder/coder/cli/cliflag"
"github.com/coder/coder/cli/vscodeipc"
"github.com/coder/coder/codersdk"
)

// vscodeipcCmd spawns a local HTTP server on the provided port that listens to messages.
// It's made for use by the Coder VS Code extension. See: https://github.com/coder/vscode-coder
func vscodeipcCmd() *cobra.Command {
var (
rawURL string
token string
port uint16
)
cmd := &cobra.Command{
Use: "vscodeipc <workspace-agent>",
Args: cobra.ExactArgs(1),
SilenceUsage: true,
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
if rawURL == "" {
return xerrors.New("CODER_URL must be set!")
}
// token is validated in a header on each request to prevent
// unauthenticated clients from connecting.
if token == "" {
return xerrors.New("CODER_TOKEN must be set!")
}
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
return xerrors.Errorf("listen: %w", err)
}
defer listener.Close()
addr, ok := listener.Addr().(*net.TCPAddr)
if !ok {
return xerrors.Errorf("listener.Addr() is not a *net.TCPAddr: %T", listener.Addr())
}
url, err := url.Parse(rawURL)
if err != nil {
return err
}
agentID, err := uuid.Parse(args[0])
if err != nil {
return err
}
client := codersdk.New(url)
client.SetSessionToken(token)

handler, closer, err := vscodeipc.New(cmd.Context(), client, agentID, nil)
if err != nil {
return err
}
defer closer.Close()
// nolint:gosec
server := http.Server{
Handler: handler,
}
defer server.Close()
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s\n", addr.String())
errChan := make(chan error, 1)
go func() {
err := server.Serve(listener)
errChan <- err
}()
select {
case <-cmd.Context().Done():
return cmd.Context().Err()
case err := <-errChan:
return err
}
},
}
cliflag.StringVarP(cmd.Flags(), &rawURL, "url", "u", "CODER_URL", "", "The URL of the Coder instance!")
cliflag.StringVarP(cmd.Flags(), &token, "token", "t", "CODER_TOKEN", "", "The session token of the user!")
cmd.Flags().Uint16VarP(&port, "port", "p", 0, "The port to listen on!")
return cmd
}
Loading