aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-02-07 07:14:31 -0500
committerXe Iaso <me@xeiaso.net>2024-02-08 12:28:58 -0500
commit6284afac164bbc81ef9303379b3f964b23df0f2f (patch)
tree14947271556381c5ebe9dfb8486a0205e94b0c8c /web
parenta03f394dff22ba60e386a63ffe868e9b917af9b5 (diff)
downloadx-6284afac164bbc81ef9303379b3f964b23df0f2f.tar.xz
x-6284afac164bbc81ef9303379b3f964b23df0f2f.zip
web/fly/flymachines: fix some stuff
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'web')
-rw-r--r--web/fly/flymachines/apps.go26
-rw-r--r--web/fly/flymachines/client.go60
-rw-r--r--web/fly/flymachines/machines.go42
-rw-r--r--web/fly/flymachines/volumes.go4
4 files changed, 86 insertions, 46 deletions
diff --git a/web/fly/flymachines/apps.go b/web/fly/flymachines/apps.go
index 19e3b07..230ee4b 100644
--- a/web/fly/flymachines/apps.go
+++ b/web/fly/flymachines/apps.go
@@ -3,11 +3,8 @@ package flymachines
import (
"context"
"encoding/json"
- "fmt"
"net/http"
"time"
-
- "within.website/x/web"
)
// CreateAppArgs are the arguments to the CreateApp call.
@@ -47,7 +44,7 @@ func (mt MilliTime) MarshalJSON() ([]byte, error) {
func (c *Client) CreateApp(ctx context.Context, caa CreateAppArgs) (*CreateAppResponse, error) {
result, err := doJSONBody[CreateAppArgs, CreateAppResponse](ctx, c, http.MethodPost, "/v1/apps", caa, http.StatusCreated)
if err != nil {
- return nil, fmt.Errorf("flymachines: can't decode CreateApp response: %w", err)
+ return nil, err
}
return &result, nil
@@ -87,7 +84,7 @@ func (c *Client) GetApps(ctx context.Context, orgSlug string) ([]ListApp, error)
TotalApps int `json:"total_apps"`
}](ctx, c, http.MethodGet, "/v1/apps?org_slug="+orgSlug, http.StatusOK)
if err != nil {
- return nil, fmt.Errorf("flymachines: can't decode GetApps response: %w", err)
+ return nil, err
}
return result.Apps, nil
@@ -97,27 +94,12 @@ func (c *Client) GetApps(ctx context.Context, orgSlug string) ([]ListApp, error)
func (c *Client) GetApp(ctx context.Context, appName string) (*SingleApp, error) {
result, err := doJSON[SingleApp](ctx, c, http.MethodGet, "/v1/apps/"+appName, http.StatusOK)
if err != nil {
- return nil, fmt.Errorf("flymachines: can't decode GetApp response: %w", err)
+ return nil, err
}
return &result, nil
}
func (c *Client) DeleteApp(ctx context.Context, appName string) error {
- req, err := http.NewRequestWithContext(ctx, http.MethodDelete, c.apiURL+"/v1/apps/"+appName, nil)
- if err != nil {
- return fmt.Errorf("flymachines: can't create DeleteApp request: %w", err)
- }
-
- resp, err := c.Do(req)
- if err != nil {
- return fmt.Errorf("flymachines: can't perform DeleteApp request: %w", err)
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusAccepted {
- return web.NewError(http.StatusAccepted, resp)
- }
-
- return nil
+ return c.doRequestNoResponse(ctx, http.MethodDelete, "/v1/apps/"+appName)
}
diff --git a/web/fly/flymachines/client.go b/web/fly/flymachines/client.go
index 30fc881..fac75ae 100644
--- a/web/fly/flymachines/client.go
+++ b/web/fly/flymachines/client.go
@@ -4,10 +4,11 @@ import (
"bytes"
"context"
"encoding/json"
+ "fmt"
+ "io"
+ "log/slog"
"net/http"
"os"
-
- "within.website/x/web"
)
const (
@@ -15,6 +16,48 @@ const (
publicURL = "https://api.machines.dev"
)
+type Error struct {
+ ErrorString string `json:"error"`
+ StatusCode int `json:"status_code"`
+ ReqID string `json:"req_id"`
+ Method string `json:"method"`
+ URL string `json:"url"`
+}
+
+func NewError(resp *http.Response) error {
+ body, err := io.ReadAll(io.LimitReader(resp.Body, 4096))
+ if err != nil {
+ return fmt.Errorf("flymachines: can't read response body while creating error: %w", err)
+ }
+
+ result := &Error{
+ StatusCode: resp.StatusCode,
+ ReqID: resp.Header.Get("Fly-Request-Id"),
+ Method: resp.Request.Method,
+ URL: resp.Request.URL.String(),
+ }
+
+ if err := json.Unmarshal(body, result); err != nil {
+ result.ErrorString = string(body)
+ }
+
+ return result
+}
+
+func (e *Error) Error() string {
+ return fmt.Sprintf("flymachines: %s %s (%d): %s", e.Method, e.URL, e.StatusCode, e.ErrorString)
+}
+
+func (e *Error) LogValue() slog.Value {
+ return slog.GroupValue(
+ slog.String("error", e.ErrorString),
+ slog.Int("status_code", e.StatusCode),
+ slog.String("req_id", e.ReqID),
+ slog.String("method", e.Method),
+ slog.String("url", e.URL),
+ )
+}
+
type Client struct {
token, apiURL string
cli *http.Client
@@ -34,10 +77,12 @@ func NewClient(token, apiURL string, cli *http.Client) *Client {
func New(token string, cli *http.Client) *Client {
resp, err := http.Get(internalURL)
if err != nil {
+ slog.Debug("can't reach internal API, falling back to public API", "err", err)
return NewClient(token, publicURL, cli)
}
if resp.StatusCode != http.StatusNotFound {
+ slog.Debug("can't reach internal API, falling back to public API", "status_code", resp.StatusCode)
return NewClient(token, publicURL, cli)
}
@@ -48,10 +93,11 @@ func New(token string, cli *http.Client) *Client {
func (c *Client) Do(req *http.Request) (*http.Response, error) {
req.Header.Set("Authorization", "Bearer "+c.token)
req.Header.Set("User-Agent", "within.website/x/web/fly/flymachines in "+os.Args[0])
+
return c.cli.Do(req)
}
-func (c *Client) doRequestNoResponse(ctx context.Context, method, path string, wantStatusCode int) error {
+func (c *Client) doRequestNoResponse(ctx context.Context, method, path string) error {
req, err := http.NewRequestWithContext(ctx, method, c.apiURL+path, nil)
if err != nil {
return err
@@ -63,8 +109,8 @@ func (c *Client) doRequestNoResponse(ctx context.Context, method, path string, w
}
defer resp.Body.Close()
- if resp.StatusCode != wantStatusCode {
- return web.NewError(wantStatusCode, resp)
+ if resp.StatusCode/100 != 2 {
+ return NewError(resp)
}
return nil
@@ -86,7 +132,7 @@ func doJSON[Output any](ctx context.Context, c *Client, method, path string, wan
defer resp.Body.Close()
if resp.StatusCode != wantStatusCode {
- return zilch[Output](), web.NewError(wantStatusCode, resp)
+ return zilch[Output](), NewError(resp)
}
var output Output
@@ -116,7 +162,7 @@ func doJSONBody[Input any, Output any](ctx context.Context, c *Client, method, p
defer resp.Body.Close()
if resp.StatusCode != wantStatusCode {
- return zilch[Output](), web.NewError(wantStatusCode, resp)
+ return zilch[Output](), NewError(resp)
}
var output Output
diff --git a/web/fly/flymachines/machines.go b/web/fly/flymachines/machines.go
index bf01e72..eeb4d22 100644
--- a/web/fly/flymachines/machines.go
+++ b/web/fly/flymachines/machines.go
@@ -12,6 +12,14 @@ func Ptr[T any](t T) *T {
return &t
}
+type MachineRestartPolicy string
+
+const (
+ MachineRestartPolicyNo MachineRestartPolicy = "no"
+ MachineRestartPolicyAlways MachineRestartPolicy = "always"
+ MachineRestartPolicyOnFailure MachineRestartPolicy = "on-failure"
+)
+
type MachineConfig struct {
Env map[string]string `json:"env"`
Metadata map[string]string `json:"metadata"`
@@ -60,13 +68,13 @@ type MachineGuest struct {
}
type MachineStopConfig struct {
- Timeout time.Duration `json:"timeout"`
- Signal string `json:"signal"`
+ Timeout string `json:"timeout"`
+ Signal string `json:"signal"`
}
type MachineRestart struct {
- MaxRetries int `json:"max_retries"` // only relevant when Policy is "on-fail"
- Policy string `json:"policy"` // "no", "always", or "on-fail"
+ MaxRetries int `json:"max_retries"` // only relevant when Policy is "on-fail"
+ Policy MachineRestartPolicy `json:"policy"`
}
type MachineServiceConcurrency struct {
@@ -168,14 +176,14 @@ type CreateMachine struct {
LSVD bool `json:"lsvd"` // should be true?
Name string `json:"name"`
Region string `json:"region"`
- SkipLaunch bool `json:"skip_launch"`
- SkipServiceRegistration bool `json:"skip_service_registration"`
+ SkipLaunch *bool `json:"skip_launch,omitempty"`
+ SkipServiceRegistration *bool `json:"skip_service_registration,omitempty"`
}
func (c *Client) CreateMachine(ctx context.Context, appID string, cm CreateMachine) (*Machine, error) {
result, err := doJSONBody[CreateMachine, Machine](ctx, c, http.MethodPost, "/v1/apps/"+appID+"/machines", cm, http.StatusOK)
if err != nil {
- return nil, fmt.Errorf("flymachines: can't decode CreateMachine response: %w", err)
+ return nil, err
}
return &result, nil
@@ -191,33 +199,37 @@ func (c *Client) GetAppMachine(ctx context.Context, appID, machineID string) (*M
}
func (c *Client) DeleteAppMachine(ctx context.Context, appID, machineID string) error {
- return c.doRequestNoResponse(ctx, http.MethodDelete, "/v1/apps/"+appID+"/machines/"+machineID, http.StatusOK)
+ return c.doRequestNoResponse(ctx, http.MethodDelete, "/v1/apps/"+appID+"/machines/"+machineID)
+}
+
+func (c *Client) DestroyAppMachine(ctx context.Context, appID, machineID string) error {
+ return c.doRequestNoResponse(ctx, http.MethodDelete, "/v1/apps/"+appID+"/machines/"+machineID+"?force=true")
}
func (c *Client) CordonAppMachine(ctx context.Context, appID, machineID string) error {
- return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/cordon", http.StatusOK)
+ return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/cordon")
}
func (c *Client) UncordonAppMachine(ctx context.Context, appID, machineID string) error {
- return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/uncordon", http.StatusOK)
+ return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/uncordon")
}
func (c *Client) StartAppMachine(ctx context.Context, appID, machineID string) error {
- return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/start", http.StatusOK)
+ return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/start")
}
func (c *Client) StopAppMachine(ctx context.Context, appID, machineID string) error {
- return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/stop", http.StatusOK)
+ return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/stop")
}
func (c *Client) RestartAppMachine(ctx context.Context, appID, machineID string) error {
- return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/restart", http.StatusOK)
+ return c.doRequestNoResponse(ctx, http.MethodPost, "/v1/apps/"+appID+"/machines/"+machineID+"/restart")
}
func (c *Client) GetAppMachineEvents(ctx context.Context, appID, machineID string) ([]MachineEvent, error) {
result, err := doJSON[[]MachineEvent](ctx, c, http.MethodGet, "/v1/apps/"+appID+"/machines/"+machineID+"/events", http.StatusOK)
if err != nil {
- return nil, fmt.Errorf("flymachines: can't decode GetAppMachineEvents response: %w", err)
+ return nil, err
}
return result, nil
@@ -226,7 +238,7 @@ func (c *Client) GetAppMachineEvents(ctx context.Context, appID, machineID strin
func (c *Client) GetAppMachineMetadata(ctx context.Context, appID, machineID string) (map[string]string, error) {
result, err := doJSON[map[string]string](ctx, c, http.MethodGet, "/v1/apps/"+appID+"/machines/"+machineID+"/metadata", http.StatusOK)
if err != nil {
- return nil, fmt.Errorf("flymachines: can't decode GetAppMachineMetadata response: %w", err)
+ return nil, err
}
return result, nil
diff --git a/web/fly/flymachines/volumes.go b/web/fly/flymachines/volumes.go
index 8f2563d..b3d4c9c 100644
--- a/web/fly/flymachines/volumes.go
+++ b/web/fly/flymachines/volumes.go
@@ -64,9 +64,9 @@ func (c *Client) CreateVolume(ctx context.Context, appName string, cv CreateVolu
}
func (c *Client) DeleteVolume(ctx context.Context, appName, volumeID string) error {
- _, err := doJSON[Volume](ctx, c, http.MethodDelete, "/v1/apps/"+appName+"/volumes/"+volumeID, http.StatusNoContent)
+ err := c.doRequestNoResponse(ctx, http.MethodDelete, "/v1/apps/"+appName+"/volumes/"+volumeID)
if err != nil {
- return fmt.Errorf("flymachines: can't decode DeleteVolume response: %w", err)
+ return err
}
return nil