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