- A request that timed out was said as "the controller was not reached, nothing ran", though the line may have run. The engine now follows a line the controller answers as running, by its call, until it ends; when no answer comes in time it says the line may have run and names the call. "Nothing ran" is said only of a request no controller took. - One account could hold the socket: accepting waited for a free slot, and a connection could stall before its deadline was set. The deadline is set at accept, before any lookup; each user id has two lines at once and the machine eight, and one over is answered busy at once. - A container running with the operator's user id shares the number, not the machine: a peer whose mount or user namespace is not the engine's is refused, and nothing is asked. SDK pinned at the go/cli branch's newest commit until go/v0.1.9 is tagged.
99 lines
3.6 KiB
Go
99 lines
3.6 KiB
Go
package meshcli
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// controllerSays is a controller answering each request in turn from a script, and recording what it was asked.
|
|
type controllerSays struct {
|
|
mu sync.Mutex
|
|
script []func(Asked) ([]byte, error)
|
|
asked []Asked
|
|
}
|
|
|
|
func (c *controllerSays) ask(_ context.Context, body []byte) ([]byte, error) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
var a Asked
|
|
_ = json.Unmarshal(body, &a)
|
|
c.asked = append(c.asked, a)
|
|
if len(c.script) == 0 {
|
|
return nil, context.DeadlineExceeded
|
|
}
|
|
next := c.script[0]
|
|
if len(c.script) > 1 {
|
|
c.script = c.script[1:]
|
|
}
|
|
return next(a)
|
|
}
|
|
|
|
func result(v any) func(Asked) ([]byte, error) {
|
|
return func(Asked) ([]byte, error) { return json.Marshal(map[string]any{"result": v}) }
|
|
}
|
|
|
|
func fastFollow(t *testing.T) {
|
|
was := followEvery
|
|
followEvery = time.Millisecond
|
|
t.Cleanup(func() { followEvery = was })
|
|
}
|
|
|
|
// A line still running when the controller first answers is followed, by its call, to what it printed.
|
|
func TestALineStillRunningIsFollowedToItsAnswer(t *testing.T) {
|
|
fastFollow(t)
|
|
running := result(map[string]any{"running": true, "call": "call-9"})
|
|
c := &controllerSays{script: []func(Asked) ([]byte, error){running, running, running,
|
|
result(map[string]any{"stdout": []byte("pushed\n"), "exit": 0, "terminal": true})}}
|
|
a := AskTheController(context.Background(), c.ask, Asked{Line: []string{"push", "laptop"}, Account: "op", UID: 1000})
|
|
if string(a.Stdout) != "pushed\n" || !a.Terminal || a.Refused != "" || a.Unknown != "" {
|
|
t.Fatalf("followed to %+v", a)
|
|
}
|
|
for _, f := range c.asked[1:] {
|
|
if f.Follow != "call-9" || f.Account != "op" || len(f.Line) != 0 {
|
|
t.Fatalf("a follow asked %+v", f)
|
|
}
|
|
}
|
|
}
|
|
|
|
// **No answer in time is never "nothing ran"** (review of ADR 0272): the line was asked and may have run, and the
|
|
// call to look at is named when it is known.
|
|
func TestNoAnswerInTimeSaysTheLineMayHaveRun(t *testing.T) {
|
|
fastFollow(t)
|
|
c := &controllerSays{}
|
|
a := AskTheController(context.Background(), c.ask, Asked{Line: []string{"push"}, Account: "op"})
|
|
if a.Refused != "" || !strings.Contains(a.Unknown, "may have run") || a.Exit == 0 {
|
|
t.Fatalf("a request with no answer reads %+v", a)
|
|
}
|
|
c = &controllerSays{script: []func(Asked) ([]byte, error){result(map[string]any{"running": true, "call": "call-3"}),
|
|
func(Asked) ([]byte, error) { return nil, context.DeadlineExceeded }}}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
|
defer cancel()
|
|
a = AskTheController(ctx, c.ask, Asked{Line: []string{"push"}, Account: "op"})
|
|
if a.Refused != "" || !strings.Contains(a.Unknown, "call-3") || !strings.Contains(a.Unknown, "may have run") {
|
|
t.Fatalf("a running line never answered reads %+v", a)
|
|
}
|
|
}
|
|
|
|
// A request the bus never carried to a controller ran nothing, and says so.
|
|
func TestARequestNobodyTookSaysNothingRan(t *testing.T) {
|
|
c := &controllerSays{script: []func(Asked) ([]byte, error){func(Asked) ([]byte, error) {
|
|
return nil, fmt.Errorf("nothing on this machine answers mesh.control.laptop.cli")
|
|
}}}
|
|
a := AskTheController(context.Background(), c.ask, Asked{Line: []string{"status"}, Account: "op"})
|
|
if !strings.Contains(a.Refused, "nothing ran") || a.Unknown != "" {
|
|
t.Fatalf("a request nobody took reads %+v", a)
|
|
}
|
|
c = &controllerSays{script: []func(Asked) ([]byte, error){func(Asked) ([]byte, error) {
|
|
return nil, errors.New("this machine has no link to the bus open now")
|
|
}}}
|
|
if a := AskTheController(context.Background(), c.ask, Asked{Line: []string{"status"}}); !strings.Contains(a.Refused, "nothing ran") {
|
|
t.Fatalf("no link reads %+v", a)
|
|
}
|
|
}
|