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

Commit 40e39c7

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

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

coder-sdk/env.go

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

33
import (
44
"context"
5+
"fmt"
56
"net/http"
67
"time"
78

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

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)