Skip to content

Commit 89ddc09

Browse files
committed
Consolidate agent to single 'bind' command
1 parent c062712 commit 89ddc09

File tree

5 files changed

+152
-191
lines changed

5 files changed

+152
-191
lines changed

internal/cmd/bind.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"os"
6+
"regexp"
7+
"strings"
8+
"time"
9+
10+
"cdr.dev/slog/sloggers/sloghuman"
11+
"github.com/spf13/pflag"
12+
"golang.org/x/xerrors"
13+
14+
"go.coder.com/cli"
15+
"go.coder.com/cloud-agent/internal/client"
16+
"go.coder.com/cloud-agent/internal/config"
17+
"go.coder.com/cloud-agent/internal/ideproxy"
18+
"go.coder.com/flog"
19+
)
20+
21+
var (
22+
DefaultCloudURL = "https://cloud.coder.com"
23+
)
24+
25+
var codeServerNameRx = regexp.MustCompile("^[a-z][a-z0-9_]{0,50}$")
26+
27+
type bindCmd struct {
28+
cloudURL string
29+
codeServerAddr string
30+
}
31+
32+
func (c *bindCmd) Spec() cli.CommandSpec {
33+
return cli.CommandSpec{
34+
Name: "bind",
35+
Usage: "[NAME]",
36+
Desc: "Bind a server to Coder Cloud. A name will be generated from the hostname if one is not provided.",
37+
}
38+
}
39+
40+
func (c *bindCmd) RegisterFlags(fl *pflag.FlagSet) {
41+
fl.StringVar(&c.cloudURL, "cloud-url", DefaultCloudURL, "The Coder Cloud URL to connect to.")
42+
fl.StringVar(&c.codeServerAddr,
43+
"code-server-addr",
44+
"localhost:8080",
45+
"The address of the code-server instance to proxy.",
46+
)
47+
}
48+
49+
func (c *bindCmd) Run(fl *pflag.FlagSet) {
50+
var (
51+
err error
52+
ctx = context.Background()
53+
)
54+
55+
name := fl.Arg(0)
56+
if name == "" {
57+
// Generate a name based on the hostname if one is not provided.
58+
name, err = genServerName()
59+
if err != nil {
60+
flog.Fatal("Failed to generate server name: %v", err.Error())
61+
}
62+
}
63+
64+
if !codeServerNameRx.MatchString(name) {
65+
flog.Fatal("Name must conform to regex %s", codeServerNameRx.String())
66+
}
67+
68+
cli, err := client.FromEnv()
69+
if xerrors.Is(err, os.ErrNotExist) {
70+
cli, err = loginClient(c.cloudURL, name)
71+
}
72+
if err != nil {
73+
flog.Fatal("Failed to login: %v", err)
74+
}
75+
76+
// Register the server with Coder Cloud. This is an idempotent
77+
// operation.
78+
cs, err := cli.RegisterCodeServer(name)
79+
if err != nil {
80+
flog.Fatal("Failed to register server: %v", err)
81+
}
82+
83+
// Get the Access URL for the user.
84+
url, err := cli.AccessURL(cs.ID)
85+
if err != nil {
86+
flog.Fatal("Failed to query server: %v", err)
87+
}
88+
89+
token, err := config.SessionToken.Read()
90+
if err != nil {
91+
flog.Fatal("Failed to read session token: %v", err)
92+
}
93+
94+
agent := &ideproxy.Agent{
95+
Log: sloghuman.Make(os.Stderr),
96+
CodeServerID: cs.ID,
97+
SessionToken: token,
98+
CloudProxyURL: c.cloudURL,
99+
CodeServerAddr: os.Getenv(c.codeServerAddr),
100+
}
101+
102+
proxy := func() {
103+
err = agent.Proxy(ctx)
104+
if err != nil {
105+
flog.Error("Connection to Coder-Cloud disrupted, re-establishing connection: %v", err.Error())
106+
}
107+
}
108+
109+
flog.Info("Proxying code-server to Coder Cloud, you can access your IDE at %v", url)
110+
111+
proxy()
112+
113+
// Avoid a super tight loop.
114+
ticker := time.NewTicker(time.Second)
115+
for range ticker.C {
116+
proxy()
117+
}
118+
}
119+
120+
func loginClient(url, serverName string) (*client.Client, error) {
121+
token, err := client.Login(url, serverName)
122+
if err != nil {
123+
return nil, xerrors.Errorf("unable to login: %w", err)
124+
}
125+
126+
err = config.SessionToken.Write(token)
127+
if err != nil {
128+
return nil, xerrors.Errorf("write session token to file: %w", err)
129+
}
130+
131+
err = config.URL.Write(url)
132+
if err != nil {
133+
return nil, xerrors.Errorf("write coder-cloud url to file: %w", err)
134+
}
135+
136+
return client.FromEnv()
137+
}
138+
139+
func genServerName() (string, error) {
140+
hostname, err := os.Hostname()
141+
if err != nil {
142+
xerrors.Errorf("get hostname: %w", err)
143+
}
144+
145+
hostname = strings.ToLower(hostname)
146+
147+
// Only use the first token.
148+
hostname = strings.Split(hostname, ".")[0]
149+
// '-' are not allowed, convert them to '_'.
150+
return strings.Replace(hostname, "-", "_", -1), nil
151+
}

internal/cmd/cmd.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ func (c *rootCmd) Spec() cli.CommandSpec {
2727

2828
func (c *rootCmd) Subcommands() []cli.Command {
2929
return []cli.Command{
30-
&linkCmd{},
31-
&proxyCmd{},
30+
&bindCmd{},
3231
&versionCmd{},
3332
}
3433
}

internal/cmd/link.go

Lines changed: 0 additions & 91 deletions
This file was deleted.

internal/cmd/proxy.go

Lines changed: 0 additions & 96 deletions
This file was deleted.

internal/config/file.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import "golang.org/x/xerrors"
55
var (
66
// SessionToken is the file containing the session token.
77
SessionToken File = "session"
8-
// ServerID is the file containing the server ID.
9-
ServerID File = "server"
108
// URL is the file containing the url to a Coder-Cloud instance.
119
URL File = "url"
1210
)

0 commit comments

Comments
 (0)