From 8dec89e207f520cd9dde1ccfe2e84aeade640682 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jul 2021 16:27:44 +0100 Subject: [PATCH] Finish dendrite upgrade checks --- cmd/dendrite-upgrade-tests/main.go | 38 +++++- cmd/dendrite-upgrade-tests/tests.go | 192 +++++++++++++++++++++++++--- 2 files changed, 206 insertions(+), 24 deletions(-) diff --git a/cmd/dendrite-upgrade-tests/main.go b/cmd/dendrite-upgrade-tests/main.go index cf121a4bd..19ea9341b 100644 --- a/cmd/dendrite-upgrade-tests/main.go +++ b/cmd/dendrite-upgrade-tests/main.go @@ -273,7 +273,7 @@ func buildDendriteImages(httpClient *http.Client, dockerClient *client.Client, b func runImage(dockerClient *client.Client, volumeName, version, imageID string) (csAPIURL, containerID string, err error) { log.Printf("%s: running image %s\n", version, imageID) - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) defer cancel() body, err := dockerClient.ContainerCreate(ctx, &container.Config{ Image: imageID, @@ -284,7 +284,7 @@ func runImage(dockerClient *client.Client, volumeName, version, imageID string) { Type: mount.TypeVolume, Source: volumeName, - Target: "/usr/local/pgsql/data", + Target: "/var/lib/postgresql/9.6/main", }, }, }, nil, nil, "dendrite_upgrade_test_"+version) @@ -346,6 +346,16 @@ func loadAndRunTests(dockerClient *client.Client, volumeName, v string, branchTo return nil } +func verifyTests(dockerClient *client.Client, volumeName string, versions []string, branchToImageID map[string]string) error { + lastVer := versions[len(versions)-1] + csAPIURL, containerID, err := runImage(dockerClient, volumeName, lastVer, branchToImageID[lastVer]) + if err != nil { + return fmt.Errorf("failed to run container for branch %v: %v", lastVer, err) + } + defer destroyContainer(dockerClient, containerID) + return verifyTestsRan(csAPIURL, versions) +} + func main() { flag.Parse() httpClient := &http.Client{ @@ -375,10 +385,32 @@ func main() { log.Fatalf("failed to make docker volume: %s", err) } + failed := false + defer func() { + perr := recover() + log.Println("removing postgres volume") + verr := dockerClient.VolumeRemove(context.Background(), volume.Name, true) + if perr == nil { + perr = verr + } + if perr != nil { + panic(perr) + } + if failed { + os.Exit(1) + } + }() + // run through images sequentially for _, v := range versions { if err = loadAndRunTests(dockerClient, volume.Name, v, branchToImageID); err != nil { - log.Fatalf("failed to run tests for %v: %s", v, err) + log.Printf("failed to run tests for %v: %s\n", v, err) + failed = true + break } } + if err := verifyTests(dockerClient, volume.Name, versions, branchToImageID); err != nil { + log.Printf("failed to verify test results: %s", err) + failed = true + } } diff --git a/cmd/dendrite-upgrade-tests/tests.go b/cmd/dendrite-upgrade-tests/tests.go index f9879df27..b0447bb63 100644 --- a/cmd/dendrite-upgrade-tests/tests.go +++ b/cmd/dendrite-upgrade-tests/tests.go @@ -1,39 +1,189 @@ package main import ( - "bytes" "fmt" - "net/http" - "net/http/httputil" - "time" + "log" + "strings" + + "github.com/matrix-org/gomatrix" + "github.com/matrix-org/gomatrixserverlib" ) +const userPassword = "this_is_a_long_password" + +type user struct { + userID string + localpart string + client *gomatrix.Client +} + +// runTests performs the following operations: +// - register alice and bob with branch name muxed into the localpart +// - create a DM room for the 2 users and exchange messages +// - create/join a public #global room and exchange messages func runTests(baseURL, branchName string) error { - httpClient := &http.Client{ - Timeout: 60 * time.Second, + // register 2 users + users := []user{ + { + localpart: "alice" + branchName, + }, + { + localpart: "bob" + branchName, + }, } - err := assertSuccess(httpClient.Post( - fmt.Sprintf("%s/_matrix/client/r0/register", baseURL), - "application/json", - bytes.NewBufferString(fmt.Sprintf(`{"username":"%s","password":"%s","auth":{"type":"m.login.dummy"}}`, "alice", "this_is_a_long_password")), - )) + for i, u := range users { + client, err := gomatrix.NewClient(baseURL, "", "") + if err != nil { + return err + } + resp, err := client.RegisterDummy(&gomatrix.ReqRegister{ + Username: strings.ToLower(u.localpart), + Password: userPassword, + }) + if err != nil { + return fmt.Errorf("failed to register %s: %s", u.localpart, err) + } + client, err = gomatrix.NewClient(baseURL, resp.UserID, resp.AccessToken) + if err != nil { + return err + } + users[i].client = client + users[i].userID = resp.UserID + } + + // create DM room, join it and exchange messages + createRoomResp, err := users[0].client.CreateRoom(&gomatrix.ReqCreateRoom{ + Preset: "trusted_private_chat", + Invite: []string{users[1].userID}, + IsDirect: true, + }) if err != nil { - return fmt.Errorf("failed to /register: %s", err) + return fmt.Errorf("failed to create DM room: %s", err) } + dmRoomID := createRoomResp.RoomID + if _, err = users[1].client.JoinRoom(dmRoomID, "", nil); err != nil { + return fmt.Errorf("failed to join DM room: %s", err) + } + msgs := []struct { + client *gomatrix.Client + text string + }{ + { + client: users[0].client, text: "1: " + branchName, + }, + { + client: users[1].client, text: "2: " + branchName, + }, + { + client: users[0].client, text: "3: " + branchName, + }, + { + client: users[1].client, text: "4: " + branchName, + }, + } + for _, msg := range msgs { + _, err = msg.client.SendText(dmRoomID, msg.text) + if err != nil { + return fmt.Errorf("failed to send text in dm room: %s", err) + } + } + + // attempt to create/join the shared public room + publicRoomID := "" + createRoomResp, err = users[0].client.CreateRoom(&gomatrix.ReqCreateRoom{ + RoomAliasName: "global", + Preset: "public_chat", + }) + if err != nil { // this is okay and expected if the room already exists and the aliases clash + // try to join it + _, domain, err := gomatrixserverlib.SplitID('@', users[0].userID) + if err != nil { + return fmt.Errorf("failed to split user ID: %s, %s", users[0].userID, err) + } + joinRoomResp, err := users[0].client.JoinRoom(fmt.Sprintf("#global:%s", domain), "", nil) + if err != nil { + return fmt.Errorf("alice failed to join public room: %s", err) + } + publicRoomID = joinRoomResp.RoomID + } else { + publicRoomID = createRoomResp.RoomID + } + if _, err = users[1].client.JoinRoom(publicRoomID, "", nil); err != nil { + return fmt.Errorf("bob failed to join public room: %s", err) + } + // send messages + for _, msg := range msgs { + _, err = msg.client.SendText(publicRoomID, "public "+msg.text) + if err != nil { + return fmt.Errorf("failed to send text in public room: %s", err) + } + } + log.Printf("OK! rooms(public=%s, dm=%s) users(%s, %s)\n", publicRoomID, dmRoomID, users[0].userID, users[1].userID) return nil } -func assertSuccess(res *http.Response, err error) error { +// verifyTestsRan checks that the HS has the right rooms/messages +func verifyTestsRan(baseURL string, branchNames []string) error { + log.Println("Verifying tests....") + // check we can login as all users + var resp *gomatrix.RespLogin + for _, branchName := range branchNames { + client, err := gomatrix.NewClient(baseURL, "", "") + if err != nil { + return err + } + userLocalparts := []string{ + "alice" + branchName, + "bob" + branchName, + } + for _, userLocalpart := range userLocalparts { + resp, err = client.Login(&gomatrix.ReqLogin{ + Type: "m.login.password", + User: strings.ToLower(userLocalpart), + Password: userPassword, + }) + if err != nil { + return fmt.Errorf("failed to login as %s: %s", userLocalpart, err) + } + if resp.AccessToken == "" { + return fmt.Errorf("failed to login, bad response: %+v", resp) + } + } + } + log.Println(" accounts exist: OK") + client, err := gomatrix.NewClient(baseURL, resp.UserID, resp.AccessToken) if err != nil { - return fmt.Errorf("response returned error: %s", err) + return err } - if res.StatusCode != 200 { - return fmt.Errorf("response returned HTTP %d - %s", res.StatusCode, dumpResponse(res)) + _, domain, err := gomatrixserverlib.SplitID('@', client.UserID) + if err != nil { + return err } + u := client.BuildURL("directory", "room", fmt.Sprintf("#global:%s", domain)) + r := struct { + RoomID string `json:"room_id"` + }{} + err = client.MakeRequest("GET", u, nil, &r) + if err != nil { + return fmt.Errorf("failed to /directory: %s", err) + } + log.Println(" public room exists: OK") + + history, err := client.Messages(r.RoomID, client.Store.LoadNextBatch(client.UserID), "", 'b', 100) + if err != nil { + return fmt.Errorf("failed to get /messages: %s", err) + } + // we expect 4 messages per version + msgCount := 0 + for _, ev := range history.Chunk { + if ev.Type == "m.room.message" { + msgCount += 1 + } + } + wantMsgCount := len(branchNames) * 4 + if msgCount != wantMsgCount { + return fmt.Errorf("got %d messages in global room, want %d", msgCount, wantMsgCount) + } + log.Println(" messages exist: OK") return nil } - -func dumpResponse(res *http.Response) string { - d, _ := httputil.DumpResponse(res, true) - return string(d) -}