@@ -2,9 +2,12 @@ package coder
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"net/http"
6
7
"time"
7
8
9
+ "nhooyr.io/websocket/wsjson"
10
+
8
11
"cdr.dev/coder-cli/internal/x/xjson"
9
12
"nhooyr.io/websocket"
10
13
)
@@ -75,3 +78,127 @@ func (c Client) DialWsep(ctx context.Context, env *Environment) (*websocket.Conn
75
78
}
76
79
return conn , nil
77
80
}
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
+ }
0 commit comments