upwatch: hold a seeding torrent app for one line test, then put its limit back exactly (R-1250)
gates / gates (push) Successful in 1m22s
gates / gates (push) Successful in 1m22s
When the agent waits for quiet and qBittorrent or Transmission runs: record the limit first, hold at 1 KiB/s, ask the agent for a test on request, put the old limit back (deferred; a household change during the test stays). Start-up puts back a limit a killed controller left; the 50 % rule skips while a hold record exists. Commit only.
This commit is contained in:
@@ -211,10 +211,32 @@ type Transmission struct {
|
||||
func (t *Transmission) Stack() string { return "transmission" }
|
||||
|
||||
func (t *Transmission) Ask(ctx context.Context) (Report, error) {
|
||||
pw, err := t.Box.Env("transmission", "PASS")
|
||||
b, err := t.rpc(ctx, `{"method":"session-stats"}`)
|
||||
if err != nil {
|
||||
return Report{}, err
|
||||
}
|
||||
var st struct {
|
||||
Arguments struct {
|
||||
Current struct {
|
||||
UploadedBytes uint64 `json:"uploadedBytes"`
|
||||
} `json:"current-stats"`
|
||||
} `json:"arguments"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &st); err != nil {
|
||||
return Report{}, err
|
||||
}
|
||||
return Report{UploadCounter: st.Arguments.Current.UploadedBytes, HasUploadCounter: true}, nil
|
||||
}
|
||||
|
||||
var rpcMethodRe = regexp.MustCompile(`"method":"([a-z-]+)"`)
|
||||
|
||||
// rpc is one Transmission RPC call: Basic auth with the stack's PASS, and the X-Transmission-Session-Id handshake
|
||||
// (the first answer is 409 carrying it). It returns the 200 body; `result` other than "success" is an error.
|
||||
func (t *Transmission) rpc(ctx context.Context, body string) ([]byte, error) {
|
||||
pw, err := t.Box.Env("transmission", "PASS")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base := t.URL
|
||||
if base == "" {
|
||||
base = "http://transmission:9091"
|
||||
@@ -224,32 +246,31 @@ func (t *Transmission) Ask(ctx context.Context) (Report, error) {
|
||||
c = &http.Client{Timeout: 20 * time.Second}
|
||||
}
|
||||
h := map[string]string{"Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("admin:"+pw)), "Host": "transmission:9091"}
|
||||
body := []byte(`{"method":"session-stats"}`)
|
||||
method := body
|
||||
if m := rpcMethodRe.FindStringSubmatch(body); m != nil {
|
||||
method = m[1]
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
code, b, hd, err := httpDo(ctx, c, "POST", base+"/transmission/rpc", h, body, "application/json")
|
||||
code, b, hd, err := httpDo(ctx, c, "POST", base+"/transmission/rpc", h, []byte(body), "application/json")
|
||||
if err != nil {
|
||||
return Report{}, err
|
||||
return nil, err
|
||||
}
|
||||
if code == 409 {
|
||||
h["X-Transmission-Session-Id"] = hd.Get("X-Transmission-Session-Id")
|
||||
continue
|
||||
}
|
||||
if code != 200 {
|
||||
return Report{}, fmt.Errorf("transmission session-stats: HTTP %d", code)
|
||||
return nil, fmt.Errorf("transmission %s: HTTP %d", method, code)
|
||||
}
|
||||
var st struct {
|
||||
Arguments struct {
|
||||
Current struct {
|
||||
UploadedBytes uint64 `json:"uploadedBytes"`
|
||||
} `json:"current-stats"`
|
||||
} `json:"arguments"`
|
||||
var r struct {
|
||||
Result string `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &st); err != nil {
|
||||
return Report{}, err
|
||||
if err := json.Unmarshal(b, &r); err == nil && r.Result != "" && r.Result != "success" {
|
||||
return nil, fmt.Errorf("transmission %s: %s", method, r.Result)
|
||||
}
|
||||
return Report{UploadCounter: st.Arguments.Current.UploadedBytes, HasUploadCounter: true}, nil
|
||||
return b, nil
|
||||
}
|
||||
return Report{}, fmt.Errorf("transmission session-stats: no session id")
|
||||
return nil, fmt.Errorf("transmission %s: no session id", method)
|
||||
}
|
||||
|
||||
// ── Plex ─────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user