Add selectable canary-first fleet updates
CI / Test (rest) (pull_request) Successful in 39s
CI / Test (store) (pull_request) Successful in 42s
CI / Lint (pull_request) Successful in 11s
CI / Build (windows/amd64) (pull_request) Successful in 7s
CI / Build (linux/amd64) (pull_request) Successful in 8s
CI / Build (linux/arm64) (pull_request) Successful in 8s
CI / Test (server-http) (pull_request) Successful in 1m45s
e2e / Playwright vs docker-compose (pull_request) Successful in 1m17s
CI / Test (rest) (pull_request) Successful in 39s
CI / Test (store) (pull_request) Successful in 42s
CI / Lint (pull_request) Successful in 11s
CI / Build (windows/amd64) (pull_request) Successful in 7s
CI / Build (linux/amd64) (pull_request) Successful in 8s
CI / Build (linux/arm64) (pull_request) Successful in 8s
CI / Test (server-http) (pull_request) Successful in 1m45s
e2e / Playwright vs docker-compose (pull_request) Successful in 1m17s
This commit is contained in:
@@ -50,6 +50,10 @@ func (f *fakeFleetWorker) Start(_ context.Context, userID, target string, hostID
|
||||
return f.startID, nil
|
||||
}
|
||||
|
||||
func (f *fakeFleetWorker) StartCanary(ctx context.Context, userID, target string, hostIDs []string) (string, error) {
|
||||
return f.Start(ctx, userID, target, hostIDs)
|
||||
}
|
||||
|
||||
func (f *fakeFleetWorker) Cancel(_ context.Context, id string) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
@@ -190,6 +194,67 @@ func TestFleetUpdateStartDerivesHostIDsWhenEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFleetUpdateStartDeduplicatesExplicitSelection(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, ts, st := rawTestServer(t)
|
||||
worker := &fakeFleetWorker{startID: ulid.Make().String()}
|
||||
srv.deps.FleetWorker = worker
|
||||
cookie := loginAsAdmin(t, st)
|
||||
hostID := helloOnlineHost(t, srv, st, "duplicate-host", "v0")
|
||||
raw, _ := json.Marshal(map[string]any{"host_ids": []string{hostID, hostID}})
|
||||
req, _ := stdhttp.NewRequest("POST", ts.URL+"/api/fleet/update", bytes.NewReader(raw))
|
||||
req.AddCookie(cookie)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
res, err := stdhttp.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("do: %v", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != stdhttp.StatusAccepted {
|
||||
t.Fatalf("status: %d", res.StatusCode)
|
||||
}
|
||||
if got := worker.startCalls[0].HostIDs; len(got) != 1 || got[0] != hostID {
|
||||
t.Fatalf("deduplicated ids: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFleetUpdateStartRejectsUnknownAndIneligibleHosts(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, ts, st := rawTestServer(t)
|
||||
worker := &fakeFleetWorker{startID: ulid.Make().String()}
|
||||
srv.deps.FleetWorker = worker
|
||||
cookie := loginAsAdmin(t, st)
|
||||
offline := makeHost(t, st, "offline-host")
|
||||
if err := st.MarkHostHello(context.Background(), offline, "v0", "0.17", api.CurrentProtocolVersion, time.Now().UTC()); err != nil {
|
||||
t.Fatalf("mark offline host: %v", err)
|
||||
}
|
||||
raw, _ := json.Marshal(map[string]any{"host_ids": []string{offline, "does-not-exist"}})
|
||||
req, _ := stdhttp.NewRequest("POST", ts.URL+"/api/fleet/update", bytes.NewReader(raw))
|
||||
req.AddCookie(cookie)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
res, err := stdhttp.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("do: %v", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != stdhttp.StatusUnprocessableEntity {
|
||||
t.Fatalf("status: got %d, want 422", res.StatusCode)
|
||||
}
|
||||
var body struct {
|
||||
Code string `json:"code"`
|
||||
Reasons map[string]string `json:"reasons"`
|
||||
}
|
||||
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if body.Code != "ineligible_hosts" || len(body.Reasons) != 2 {
|
||||
t.Fatalf("structured reasons: %+v", body)
|
||||
}
|
||||
if len(worker.startCalls) != 0 {
|
||||
t.Fatal("worker must not start with invalid membership")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFleetUpdateCancelHappyPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, ts, st := rawTestServer(t)
|
||||
|
||||
Reference in New Issue
Block a user