diff options
| author | Xe Iaso <me@xeiaso.net> | 2024-02-07 07:14:31 -0500 |
|---|---|---|
| committer | Xe Iaso <me@xeiaso.net> | 2024-02-08 12:28:58 -0500 |
| commit | 6284afac164bbc81ef9303379b3f964b23df0f2f (patch) | |
| tree | 14947271556381c5ebe9dfb8486a0205e94b0c8c /web | |
| parent | a03f394dff22ba60e386a63ffe868e9b917af9b5 (diff) | |
| download | x-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.go | 26 | ||||
| -rw-r--r-- | web/fly/flymachines/client.go | 60 | ||||
| -rw-r--r-- | web/fly/flymachines/machines.go | 42 | ||||
| -rw-r--r-- | web/fly/flymachines/volumes.go | 4 |
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 |
