Skip to content

Commit d759b31

Browse files
authored
Merge pull request #1 from cdr/initial-port
Export source from monorepo
2 parents ec7bbd7 + fef241e commit d759b31

File tree

19 files changed

+1280
-0
lines changed

19 files changed

+1280
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ node_modules
55
.dbtemp
66
.vscode
77
vendor
8+
cloud-agent

go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module go.coder.com/cloud-agent
2+
3+
go 1.15
4+
5+
require (
6+
cdr.dev/slog v1.3.0
7+
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
8+
github.com/spf13/pflag v1.0.5
9+
go.coder.com/cli v0.4.0
10+
go.coder.com/flog v0.0.0-20200908145530-d7adc3802a47
11+
golang.org/x/sync v0.0.0-20190423024810-112230192c58
12+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
13+
nhooyr.io/websocket v1.8.6
14+
)

go.sum

Lines changed: 291 additions & 0 deletions
Large diffs are not rendered by default.

internal/client/client.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package client
2+
3+
import (
4+
"net/url"
5+
6+
"go.coder.com/cloud-agent/internal/config"
7+
)
8+
9+
type Client struct {
10+
token string
11+
baseURL *url.URL
12+
}
13+
14+
func FromEnv() (*Client, error) {
15+
token, err := config.SessionToken.Read()
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
u, err := config.URL.Read()
21+
if err != nil {
22+
return nil, err
23+
}
24+
25+
parsed, err := url.Parse(u)
26+
if err != nil {
27+
return nil, err
28+
}
29+
30+
return &Client{
31+
token: token,
32+
baseURL: parsed,
33+
}, nil
34+
}

internal/client/code_server.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package client
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"time"
7+
8+
"golang.org/x/xerrors"
9+
)
10+
11+
func (c *Client) CodeServer(id string) (*CodeServer, error) {
12+
path := fmt.Sprintf("/api/servers/%v", id)
13+
14+
var response CodeServer
15+
err := c.requestBody("GET", path, nil, &response)
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
return &response, nil
21+
}
22+
23+
type AccessURLResponse struct {
24+
URL string `json:"url"`
25+
}
26+
27+
func (c *Client) AccessURL(id string) (string, error) {
28+
path := fmt.Sprintf("/api/servers/%v/access-url", id)
29+
30+
var response AccessURLResponse
31+
err := c.requestBody("GET", path, nil, &response)
32+
if err != nil {
33+
return "", err
34+
}
35+
36+
return response.URL, nil
37+
}
38+
39+
type CodeServer struct {
40+
ID string `json:"id"`
41+
UserID string `json:"user_id"`
42+
Name string `json:"name"`
43+
Hostname string `json:"hostname"`
44+
CreatedAt time.Time `json:"created_at"`
45+
LastConnectionAt time.Time `json:"last_connection_at"`
46+
}
47+
48+
// RegisterServerRequest is the request body sent in a
49+
// register server request.
50+
type RegisterServerRequest struct {
51+
Name string `json:"name"`
52+
Hostname string `json:"hostname"`
53+
}
54+
55+
func (c *Client) RegisterCodeServer(name string) (*CodeServer, error) {
56+
const path = "/api/servers"
57+
hostname, err := os.Hostname()
58+
if err != nil {
59+
return nil, xerrors.Errorf("get hostname: %w", err)
60+
}
61+
62+
var response CodeServer
63+
err = c.requestBody("POST", path,
64+
&RegisterServerRequest{
65+
Name: name,
66+
Hostname: hostname,
67+
},
68+
&response,
69+
)
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
return &response, nil
75+
}

internal/client/login.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"io/ioutil"
6+
"net/url"
7+
8+
"github.com/pkg/browser"
9+
"golang.org/x/xerrors"
10+
"nhooyr.io/websocket"
11+
12+
"go.coder.com/cloud-agent/pkg/agentlogin"
13+
"go.coder.com/flog"
14+
)
15+
16+
func init() {
17+
browser.Stderr = ioutil.Discard
18+
browser.Stdout = ioutil.Discard
19+
}
20+
21+
// Login performs the login flow for an agent. It returns the resulting
22+
// session token to use for authenticated routes.
23+
func Login(addr, serverName string) (string, error) {
24+
ctx := context.Background()
25+
26+
u, err := url.Parse(addr)
27+
if err != nil {
28+
return "", err
29+
}
30+
31+
query := url.Values{}
32+
query.Add(agentlogin.ServerNameQueryParam, serverName)
33+
34+
loginURL := &url.URL{
35+
Scheme: u.Scheme,
36+
Host: u.Host,
37+
Path: "/login",
38+
RawQuery: query.Encode(),
39+
}
40+
41+
conn, _, err := websocket.Dial(ctx, loginURL.String(), nil)
42+
if err != nil {
43+
return "", err
44+
}
45+
defer conn.Close(websocket.StatusInternalError, "")
46+
47+
client := &agentlogin.Client{
48+
Ctx: ctx,
49+
Conn: conn,
50+
}
51+
52+
url, err := client.ReadAuthURL()
53+
if err != nil {
54+
return "", xerrors.Errorf("read auth url: %w", err)
55+
}
56+
57+
err = browser.OpenURL(url)
58+
if err != nil {
59+
flog.Info("visit %s to login", url)
60+
}
61+
62+
token, err := client.ReadSessionToken()
63+
if err != nil {
64+
return "", xerrors.Errorf("read session token: %w", err)
65+
}
66+
67+
return token, nil
68+
}

internal/client/request.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package client
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"net/http"
7+
8+
"golang.org/x/xerrors"
9+
)
10+
11+
const sessionHeader = "Session-Token"
12+
13+
func (c *Client) request(method, path string, body interface{}) (*http.Response, error) {
14+
b, err := json.Marshal(body)
15+
if err != nil {
16+
return nil, xerrors.Errorf("marshal body: %w", err)
17+
}
18+
19+
req, err := http.NewRequest(method, c.baseURL.String()+path, bytes.NewReader(b))
20+
if err != nil {
21+
return nil, xerrors.Errorf("new request: %w", err)
22+
}
23+
24+
req.Header.Set(sessionHeader, c.token)
25+
26+
return http.DefaultClient.Do(req)
27+
}
28+
29+
func (c *Client) requestBody(method, path string, request, response interface{}) error {
30+
resp, err := c.request(method, path, request)
31+
if err != nil {
32+
return err
33+
}
34+
defer resp.Body.Close()
35+
36+
if resp.StatusCode != 200 && resp.StatusCode != 201 {
37+
return bodyError(resp)
38+
}
39+
40+
err = json.NewDecoder(resp.Body).Decode(&response)
41+
if err != nil {
42+
return xerrors.Errorf("unmarshal response: %w", err)
43+
}
44+
return nil
45+
}
46+
47+
type apiError struct {
48+
Err struct {
49+
Msg string `json:"msg"`
50+
} `json:"error"`
51+
}
52+
53+
func bodyError(resp *http.Response) error {
54+
var apiErr apiError
55+
56+
err := json.NewDecoder(resp.Body).Decode(&apiErr)
57+
if err != nil {
58+
return xerrors.Errorf("decode err: %w", err)
59+
}
60+
61+
return xerrors.New(apiErr.Err.Msg)
62+
}

internal/client/user.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package client
2+
3+
import (
4+
"time"
5+
)
6+
7+
// User describe a Coder Cloud user.
8+
type User struct {
9+
ID string `json:"id" `
10+
Name string `json:"name"`
11+
Username string `json:"username"`
12+
Email string `json:"email"`
13+
CreatedAt time.Time `json:"created_at"`
14+
}
15+
16+
func (c *Client) Me() (*User, error) {
17+
const path = "/api/users/me"
18+
19+
var response User
20+
err := c.requestBody("GET", path, nil, &response)
21+
if err != nil {
22+
return nil, err
23+
}
24+
25+
return &response, nil
26+
27+
}

internal/cmd/cmd.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/pflag"
5+
"go.coder.com/cli"
6+
)
7+
8+
func Make() cli.Command {
9+
return &rootCmd{}
10+
}
11+
12+
var _ interface {
13+
cli.Command
14+
cli.ParentCommand
15+
} = &rootCmd{}
16+
17+
type rootCmd struct {
18+
}
19+
20+
func (c *rootCmd) Spec() cli.CommandSpec {
21+
return cli.CommandSpec{
22+
Name: "agent",
23+
Usage: "[GLOBAL FLAGS] COMMAND [COMMAND FLAGS] [ARGS...]",
24+
Desc: `Run the Coder Cloud Agent.`,
25+
}
26+
}
27+
28+
func (c *rootCmd) Subcommands() []cli.Command {
29+
return []cli.Command{
30+
&linkCmd{},
31+
&proxyCmd{},
32+
&versionCmd{},
33+
}
34+
}
35+
36+
func (c *rootCmd) Run(fl *pflag.FlagSet) {
37+
fl.Usage()
38+
}

0 commit comments

Comments
 (0)