a58ce74ed0
Primary goals: - Scrollable output ( fixes #245 ) - Sending job signals from status view - Filtering of output by filesystem Implementation: - original TUI framework: github.com/rivo/tview - but: tview is quasi-unmaintained, didn't support some features - => use fork https://gitlab.com/tslocum/cview - however, don't buy into either too much to avoid lock-in - instead: **port over the existing status UI drawing code and adjust it to produce strings instead of directly drawing into the termbox buffer** Co-authored-by: Calistoc <calistoc@protonmail.com> Co-authored-by: InsanePrawn <insane.prawny@gmail.com> fixes #245 fixes #220
106 lines
2.2 KiB
Go
106 lines
2.2 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/zrepl/zrepl/daemon"
|
|
)
|
|
|
|
type Client struct {
|
|
h http.Client
|
|
}
|
|
|
|
func New(network, addr string) (*Client, error) {
|
|
httpc, err := controlHttpClient(func(_ context.Context) (net.Conn, error) { return net.Dial(network, addr) })
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Client{httpc}, nil
|
|
}
|
|
|
|
func (c *Client) Status() (s daemon.Status, _ error) {
|
|
err := jsonRequestResponse(c.h, daemon.ControlJobEndpointStatus,
|
|
struct{}{},
|
|
&s,
|
|
)
|
|
return s, err
|
|
}
|
|
|
|
func (c *Client) StatusRaw() ([]byte, error) {
|
|
var r json.RawMessage
|
|
err := jsonRequestResponse(c.h, daemon.ControlJobEndpointStatus, struct{}{}, &r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
func (c *Client) signal(job, sig string) error {
|
|
return jsonRequestResponse(c.h, daemon.ControlJobEndpointSignal,
|
|
struct {
|
|
Name string
|
|
Op string
|
|
}{
|
|
Name: job,
|
|
Op: sig,
|
|
},
|
|
struct{}{},
|
|
)
|
|
}
|
|
|
|
func (c *Client) SignalReplication(job string) error {
|
|
return c.signal(job, "replication")
|
|
}
|
|
|
|
func (c *Client) SignalSnapshot(job string) error {
|
|
return c.signal(job, "snapshot")
|
|
}
|
|
|
|
func (c *Client) SignalReset(job string) error {
|
|
return c.signal(job, "reset")
|
|
}
|
|
|
|
func controlHttpClient(dialfunc func(context.Context) (net.Conn, error)) (client http.Client, err error) {
|
|
return http.Client{
|
|
Transport: &http.Transport{
|
|
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
|
return dialfunc(ctx)
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func jsonRequestResponse(c http.Client, endpoint string, req interface{}, res interface{}) error {
|
|
var buf bytes.Buffer
|
|
encodeErr := json.NewEncoder(&buf).Encode(req)
|
|
if encodeErr != nil {
|
|
return encodeErr
|
|
}
|
|
|
|
resp, err := c.Post("http://unix"+endpoint, "application/json", &buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
var msg bytes.Buffer
|
|
_, _ = io.CopyN(&msg, resp.Body, 4096) // ignore error, just display what we got
|
|
return errors.Errorf("%s", msg.String())
|
|
}
|
|
|
|
decodeError := json.NewDecoder(resp.Body).Decode(&res)
|
|
if decodeError != nil {
|
|
return decodeError
|
|
}
|
|
|
|
return nil
|
|
}
|