Skip to content

Commit 25a9822

Browse files
committed
Consolidate agent to single 'bind' command
1 parent c062712 commit 25a9822

File tree

5 files changed

+149
-191
lines changed

5 files changed

+149
-191
lines changed

internal/cmd/bind.go

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

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)