Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 74c6417

Browse files
committed
Add ability to create new environments and images
1 parent 2d52bb3 commit 74c6417

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

coder-sdk/env.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package coder
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
7+
"nhooyr.io/websocket/wsjson"
68
"time"
79

810
"cdr.dev/coder-cli/internal/x/xjson"
@@ -75,3 +77,127 @@ func (c Client) DialWsep(ctx context.Context, env *Environment) (*websocket.Conn
7577
}
7678
return conn, nil
7779
}
80+
81+
// CreateEnvironmentRequest is used to configure a new environment
82+
type CreateEnvironmentRequest struct {
83+
Name string `json:"name"`
84+
ImageID string `json:"image_id"`
85+
ImageTag string `json:"image_tag"`
86+
CPUCores float32 `json:"cpu_cores"`
87+
MemoryGB int `json:"memory_gb"`
88+
DiskGB int `json:"disk_gb"`
89+
GPUs int `json:"gpus"`
90+
Services []string `json:"services"`
91+
}
92+
93+
// CreateEnvironment sends a request to create an environment.
94+
func (c Client) CreateEnvironment(ctx context.Context, orgID string, req CreateEnvironmentRequest) (Environment, error) {
95+
var env Environment
96+
err := c.requestBody(
97+
ctx,
98+
http.MethodPost, "/api/orgs/"+orgID+"/environments",
99+
req,
100+
&env,
101+
)
102+
return env, err
103+
}
104+
105+
type envUpdate struct {
106+
Type string `json:"type"`
107+
}
108+
109+
// WaitForEnvironmentReady watches the environment update websocket and waits for the "done" message type before returning.
110+
func (c Client) WaitForEnvironmentReady(ctx context.Context, envID string) error {
111+
u := c.copyURL()
112+
if c.BaseURL.Scheme == "https" {
113+
u.Scheme = "wss"
114+
} else {
115+
u.Scheme = "ws"
116+
}
117+
u.Path = "/api/environments/" + envID + "/watch-update"
118+
119+
conn, resp, err := websocket.Dial(ctx, u.String(),
120+
&websocket.DialOptions{
121+
HTTPHeader: map[string][]string{
122+
"Cookie": {"session_token=" + c.Token},
123+
},
124+
},
125+
)
126+
if err != nil {
127+
if resp != nil {
128+
return bodyError(resp)
129+
}
130+
return err
131+
}
132+
133+
for {
134+
m := envUpdate{}
135+
err = wsjson.Read(ctx, conn, &m)
136+
if err != nil {
137+
return fmt.Errorf("read ws json msg: %w", err)
138+
}
139+
if m.Type == "done" {
140+
break
141+
}
142+
}
143+
144+
return nil
145+
}
146+
147+
type stats struct {
148+
ContainerStatus string `json:"container_status"`
149+
StatError string `json:"stat_error"`
150+
Time string `json:"time"`
151+
}
152+
153+
// WatchEnvironmentStats watches the environment update websocket for a given duration.
154+
func (c Client) WatchEnvironmentStats(ctx context.Context, envID string, duration time.Duration) error {
155+
u := c.copyURL()
156+
if c.BaseURL.Scheme == "https" {
157+
u.Scheme = "wss"
158+
} else {
159+
u.Scheme = "ws"
160+
}
161+
u.Path = "/api/environments/" + envID + "/watch-stats"
162+
163+
conn, resp, err := websocket.Dial(ctx, u.String(),
164+
&websocket.DialOptions{
165+
HTTPHeader: map[string][]string{
166+
"Cookie": {"session_token=" + c.Token},
167+
},
168+
},
169+
)
170+
if err != nil {
171+
if resp != nil {
172+
return bodyError(resp)
173+
}
174+
return err
175+
}
176+
177+
statsCtx, statsCancel := context.WithTimeout(ctx, duration)
178+
defer statsCancel()
179+
180+
for {
181+
select {
182+
case <-statsCtx.Done():
183+
return nil
184+
default:
185+
m := stats{}
186+
err = wsjson.Read(ctx, conn, &m)
187+
if err != nil {
188+
return fmt.Errorf("read ws json msg: %w", err)
189+
}
190+
}
191+
}
192+
}
193+
194+
// DeleteEnvironment deletes the environment.
195+
func (c Client) DeleteEnvironment(ctx context.Context, envID string) error {
196+
err := c.requestBody(
197+
ctx,
198+
http.MethodDelete, "/api/environments/" + envID,
199+
nil,
200+
nil,
201+
)
202+
return err
203+
}

coder-sdk/image.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package coder
2+
3+
import (
4+
"context"
5+
"net/http"
6+
)
7+
8+
// Image describes a Coder Image
9+
type Image struct {
10+
ID string `json:"id"`
11+
OrganizationID string `json:"organization_id"`
12+
Repository string `json:"repository"`
13+
Description string `json:"description"`
14+
URL string `json:"url"` // user-supplied URL for image
15+
DefaultCPUCores float32 `json:"default_cpu_cores"`
16+
DefaultMemoryGB int `json:"default_memory_gb"`
17+
DefaultDiskGB int `json:"default_disk_gb"`
18+
Deprecated bool `json:"deprecated"`
19+
}
20+
21+
// NewRegistryRequest describes a docker registry used in importing an image
22+
type NewRegistryRequest struct {
23+
FriendlyName string `json:"friendly_name"`
24+
Registry string `json:"registry"`
25+
Username string `json:"username"`
26+
Password string `json:"password"`
27+
}
28+
29+
// ImportImageRequest is used to import new images and registries into Coder
30+
type ImportImageRequest struct {
31+
// RegistryID is used to import images to existing registries.
32+
RegistryID *string `json:"registry_id"`
33+
// NewRegistry is used when adding a new registry.
34+
NewRegistry *NewRegistryRequest `json:"new_registry"`
35+
// Repository refers to the image. For example: "codercom/ubuntu".
36+
Repository string `json:"repository"`
37+
Tag string `json:"tag"`
38+
DefaultCPUCores float32 `json:"default_cpu_cores"`
39+
DefaultMemoryGB int `json:"default_memory_gb"`
40+
DefaultDiskGB int `json:"default_disk_gb"`
41+
Description string `json:"description"`
42+
URL string `json:"url"`
43+
}
44+
45+
// ImportImage creates a new image and optionally a new registry
46+
func (c Client) ImportImage(ctx context.Context, orgID string, req ImportImageRequest) (Image, error) {
47+
var img Image
48+
err := c.requestBody(
49+
ctx,
50+
http.MethodPost, "/api/orgs/"+orgID+"/images",
51+
req,
52+
&img,
53+
)
54+
return img, err
55+
}

0 commit comments

Comments
 (0)