performance test for assinment polls
This commit is contained in:
parent
8d73f08072
commit
9a9ef8e039
1
performance/.gitignore
vendored
1
performance/.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/performance
|
/performance
|
||||||
|
.vscode
|
||||||
|
@ -14,7 +14,7 @@ const pathLogin = "/apps/users/login/"
|
|||||||
|
|
||||||
// Client can send requests to the server.
|
// Client can send requests to the server.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
domain string
|
addr string
|
||||||
hc *http.Client
|
hc *http.Client
|
||||||
username, password string
|
username, password string
|
||||||
|
|
||||||
@ -22,9 +22,9 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a client object. No requests are sent.
|
// New creates a client object. No requests are sent.
|
||||||
func New(domain, username, password string, options ...Option) (*Client, error) {
|
func New(addr, username, password string, options ...Option) (*Client, error) {
|
||||||
c := &Client{
|
c := &Client{
|
||||||
domain: "https://" + domain,
|
addr: addr,
|
||||||
username: username,
|
username: username,
|
||||||
password: password,
|
password: password,
|
||||||
loginRetry: 1,
|
loginRetry: 1,
|
||||||
@ -51,7 +51,7 @@ func New(domain, username, password string, options ...Option) (*Client, error)
|
|||||||
// Login uses the username and password to login the client. Sets the returned
|
// Login uses the username and password to login the client. Sets the returned
|
||||||
// cookie for later requests.
|
// cookie for later requests.
|
||||||
func (c *Client) Login() error {
|
func (c *Client) Login() error {
|
||||||
url := c.domain + pathLogin
|
url := c.addr + pathLogin
|
||||||
payload := fmt.Sprintf(`{"username": "%s", "password": "%s"}`, c.username, c.password)
|
payload := fmt.Sprintf(`{"username": "%s", "password": "%s"}`, c.username, c.password)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -70,7 +70,7 @@ func (c *Client) Login() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) get(ctx context.Context, path string) error {
|
func (c *Client) get(ctx context.Context, path string) error {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", c.domain+path, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", c.addr+path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating request: %w", err)
|
return fmt.Errorf("creating request: %w", err)
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ func cmdBrowser(cfg *config) *cobra.Command {
|
|||||||
|
|
||||||
clients := make([]*client.Client, clientCount)
|
clients := make([]*client.Client, clientCount)
|
||||||
for i := range clients {
|
for i := range clients {
|
||||||
c, err := client.New(cfg.domain, cfg.username, cfg.password)
|
c, err := client.New(cfg.addr(), cfg.username, cfg.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating client: %w", err)
|
return fmt.Errorf("creating client: %w", err)
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ func cmdBrowser(cfg *config) *cobra.Command {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(c *client.Client) {
|
go func(c *client.Client) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := browser(cfg.domain, c, bar); err != nil {
|
if err := browser(cfg.addr(), c, bar); err != nil {
|
||||||
log.Printf("Client failed: %v", err)
|
log.Printf("Client failed: %v", err)
|
||||||
}
|
}
|
||||||
}(c)
|
}(c)
|
||||||
@ -76,7 +76,7 @@ func cmdBrowser(cfg *config) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func browser(domain string, c *client.Client, bar *mpb.Bar) error {
|
func browser(url string, c *client.Client, bar *mpb.Bar) error {
|
||||||
if err := c.Login(); err != nil {
|
if err := c.Login(); err != nil {
|
||||||
return fmt.Errorf("login client: %w", err)
|
return fmt.Errorf("login client: %w", err)
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ func browser(domain string, c *client.Client, bar *mpb.Bar) error {
|
|||||||
go func(path string) {
|
go func(path string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", "https://"+domain+path, nil)
|
req, err := http.NewRequest("GET", url+path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error creating request: %v", err)
|
log.Printf("Error creating request: %v", err)
|
||||||
return
|
return
|
||||||
|
@ -48,7 +48,7 @@ func cmdConnect(cfg *config) *cobra.Command {
|
|||||||
|
|
||||||
path := "/system/autoupdate"
|
path := "/system/autoupdate"
|
||||||
|
|
||||||
c, err := client.New(cfg.domain, cfg.username, cfg.password)
|
c, err := client.New(cfg.addr(), cfg.username, cfg.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating client: %w", err)
|
return fmt.Errorf("creating client: %w", err)
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ func cmdConnect(cfg *config) *cobra.Command {
|
|||||||
|
|
||||||
for i := 0; i < connectionCount; i++ {
|
for i := 0; i < connectionCount; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
r, err := keepOpen(cfg.domain, c, path)
|
r, err := keepOpen(cfg.addr(), c, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Can not create connection: %w", err)
|
log.Println("Can not create connection: %w", err)
|
||||||
return
|
return
|
||||||
@ -70,7 +70,8 @@ func cmdConnect(cfg *config) *cobra.Command {
|
|||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := bufio.NewScanner(r)
|
||||||
scanner.Buffer(make([]byte, 10), 1_000_000)
|
const MB = 1 << 20
|
||||||
|
scanner.Buffer(make([]byte, 10), 16*MB)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
msg, err := scannerAutoupdate(scanner.Text())
|
msg, err := scannerAutoupdate(scanner.Text())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -86,7 +87,7 @@ func cmdConnect(cfg *config) *cobra.Command {
|
|||||||
if errors.Is(err, context.Canceled) {
|
if errors.Is(err, context.Canceled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println("Can not read body: %w", err)
|
log.Printf("Can not read body: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,8 +116,8 @@ func cmdConnect(cfg *config) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func keepOpen(domain string, c *client.Client, path string) (io.ReadCloser, error) {
|
func keepOpen(url string, c *client.Client, path string) (io.ReadCloser, error) {
|
||||||
req, err := http.NewRequest("GET", "https://"+domain+path, nil)
|
req, err := http.NewRequest("GET", url+path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("creating request: %w", err)
|
return nil, fmt.Errorf("creating request: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ func cmdCreateAmendments(cfg *config) *cobra.Command {
|
|||||||
Short: "Creates a motion with amendments.",
|
Short: "Creates a motion with amendments.",
|
||||||
Long: helpCreateAmendments,
|
Long: helpCreateAmendments,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
c, err := client.New(cfg.domain, cfg.username, cfg.password)
|
c, err := client.New(cfg.addr(), cfg.username, cfg.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating client: %w", err)
|
return fmt.Errorf("creating client: %w", err)
|
||||||
}
|
}
|
||||||
@ -31,15 +31,13 @@ func cmdCreateAmendments(cfg *config) *cobra.Command {
|
|||||||
return fmt.Errorf("login client: %w", err)
|
return fmt.Errorf("login client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := "https://" + cfg.domain
|
motionID, err := createMotion(c, cfg.addr(), amendmentAmount)
|
||||||
|
|
||||||
motionID, err := createMotion(c, addr, amendmentAmount)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create motion: %w", err)
|
return fmt.Errorf("create motion: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < amendmentAmount; i++ {
|
for i := 0; i < amendmentAmount; i++ {
|
||||||
createAmendment(c, addr, motionID, i)
|
createAmendment(c, cfg.addr(), motionID, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(motionID)
|
fmt.Println(motionID)
|
||||||
|
130
performance/cmd/create_candidates.go
Normal file
130
performance/cmd/create_candidates.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/OpenSlides/OpenSlides/performance/client"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
const helpCreateCandidates = `Creates candidates to an assignment
|
||||||
|
|
||||||
|
This command can be used to test assignment votes.
|
||||||
|
`
|
||||||
|
|
||||||
|
func cmdCreateCandidates(cfg *config) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "create_candidates",
|
||||||
|
Short: "Creates candidates to an assignment",
|
||||||
|
Long: helpCreateCandidates,
|
||||||
|
}
|
||||||
|
|
||||||
|
amount := cmd.Flags().IntP("amount", "a", 10, "amount of candidates to add")
|
||||||
|
assignmentID := cmd.Flags().Int("assignment_id", 0, "assignment where the candidates are created")
|
||||||
|
|
||||||
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||||
|
c, err := client.New(cfg.addr(), cfg.username, cfg.password)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Login(); err != nil {
|
||||||
|
return fmt.Errorf("login client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uids, err := userIDs(cfg, c, *amount)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getting userIDs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range uids {
|
||||||
|
if err := createCandidate(c, cfg.addr(), *assignmentID, id); err != nil {
|
||||||
|
return fmt.Errorf("create candidate for uid %d: %w", id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func createCandidate(c *client.Client, addr string, assignmentID int, userID int) error {
|
||||||
|
req, err := http.NewRequest(
|
||||||
|
"POST",
|
||||||
|
fmt.Sprintf("%s/rest/assignments/assignment/%d/candidature_other/", addr, assignmentID),
|
||||||
|
strings.NewReader(fmt.Sprintf(`{"user":%d}`, userID)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if _, err := client.CheckStatus(c.Do(req)); err != nil {
|
||||||
|
return fmt.Errorf("sending request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func userIDs(cfg *config, c *client.Client, amount int) ([]int, error) {
|
||||||
|
req, err := http.NewRequest("GET", fmt.Sprintf("%s/system/autoupdate", cfg.addr()), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creating fetching all request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("sending fetch all request: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
body = []byte(fmt.Sprintf("[can not read body: %v", err))
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("got status %s: %s", resp.Status, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First message is a connection info. Throw it away.
|
||||||
|
var devNull json.RawMessage
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&devNull); err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var content struct {
|
||||||
|
Changed map[string][]json.RawMessage `json:"changed"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&content); err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
users := content.Changed["users/user"]
|
||||||
|
if len(users) == 0 {
|
||||||
|
return nil, fmt.Errorf("no users found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var ids []int
|
||||||
|
for i, u := range users {
|
||||||
|
var user struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
UserName string `json:"username"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(u, &user); err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding %dth user: %w", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(user.UserName, "dummy") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ids = append(ids, user.ID)
|
||||||
|
if len(ids) >= amount {
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("could not find %d dummy users", amount)
|
||||||
|
}
|
@ -31,7 +31,7 @@ func cmdCreateUsers(cfg *config) *cobra.Command {
|
|||||||
Short: "Create a lot of users.",
|
Short: "Create a lot of users.",
|
||||||
Long: createUsersHelp,
|
Long: createUsersHelp,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
c, err := client.New(cfg.domain, cfg.username, cfg.password)
|
c, err := client.New(cfg.addr(), cfg.username, cfg.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating client: %w", err)
|
return fmt.Errorf("creating client: %w", err)
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ func cmdCreateUsers(cfg *config) *cobra.Command {
|
|||||||
return fmt.Errorf("encoding users: %w", err)
|
return fmt.Errorf("encoding users: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "https://"+cfg.domain+"/rest/users/user/mass_import/", bytes.NewReader(bs))
|
req, err := http.NewRequest("POST", cfg.addr()+"/rest/users/user/mass_import/", bytes.NewReader(bs))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating mass import request: %w", err)
|
return fmt.Errorf("creating mass import request: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,15 @@ type config struct {
|
|||||||
domain string
|
domain string
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
|
http bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *config) addr() string {
|
||||||
|
proto := "https"
|
||||||
|
if c.http {
|
||||||
|
proto = "http"
|
||||||
|
}
|
||||||
|
return proto + "://" + c.domain
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdRoot(cfg *config) *cobra.Command {
|
func cmdRoot(cfg *config) *cobra.Command {
|
||||||
@ -19,11 +28,13 @@ func cmdRoot(cfg *config) *cobra.Command {
|
|||||||
Use: "performance",
|
Use: "performance",
|
||||||
Short: "performance is a tool that brings OpenSlides to its limit.",
|
Short: "performance is a tool that brings OpenSlides to its limit.",
|
||||||
Long: rootHelp,
|
Long: rootHelp,
|
||||||
|
SilenceUsage: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVarP(&cfg.domain, "domain", "d", "localhost:8000", "Domain where to send the requests")
|
cmd.PersistentFlags().StringVarP(&cfg.domain, "domain", "d", "localhost:8000", "Domain where to send the requests")
|
||||||
cmd.PersistentFlags().StringVarP(&cfg.username, "username", "u", "admin", "Username that can create the users.")
|
cmd.PersistentFlags().StringVarP(&cfg.username, "username", "u", "admin", "Username that can create the users.")
|
||||||
cmd.PersistentFlags().StringVarP(&cfg.password, "password", "p", "admin", "Password to use.")
|
cmd.PersistentFlags().StringVarP(&cfg.password, "password", "p", "admin", "Password to use.")
|
||||||
|
cmd.PersistentFlags().BoolVar(&cfg.http, "http", false, "Use http instead of https. Default is https.")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -38,6 +49,7 @@ func Execute() error {
|
|||||||
cmdCreateUsers(cfg),
|
cmdCreateUsers(cfg),
|
||||||
cmdVotes(cfg),
|
cmdVotes(cfg),
|
||||||
cmdCreateAmendments(cfg),
|
cmdCreateAmendments(cfg),
|
||||||
|
cmdCreateCandidates(cfg),
|
||||||
)
|
)
|
||||||
|
|
||||||
return cmd.Execute()
|
return cmd.Execute()
|
||||||
|
@ -2,10 +2,13 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -20,58 +23,76 @@ const voteHelp = `Sends many votes from different users.
|
|||||||
This command requires, that there are many user created at the
|
This command requires, that there are many user created at the
|
||||||
backend. You can use the command "create_users" for this job.
|
backend. You can use the command "create_users" for this job.
|
||||||
|
|
||||||
Per default, the command uses the new url to the autoupdate-service.
|
Example:
|
||||||
To test the url from the python-server, you can use the flag "old_url".`
|
|
||||||
|
performance votes motion --amount 100 --poll_id 42
|
||||||
|
|
||||||
|
performance votes assignment --amount 100 --poll_id 42
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
func cmdVotes(cfg *config) *cobra.Command {
|
func cmdVotes(cfg *config) *cobra.Command {
|
||||||
var (
|
|
||||||
count int
|
|
||||||
pollID int
|
|
||||||
oldURL bool
|
|
||||||
interrupt bool
|
|
||||||
loginRetry int
|
|
||||||
)
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "votes",
|
Use: "votes",
|
||||||
Short: "Sends many votes from different users",
|
Short: "Sends many votes from different users",
|
||||||
|
ValidArgs: []string{"motion", "assignment", "m", "a"},
|
||||||
|
Args: cobra.ExactValidArgs(1),
|
||||||
Long: voteHelp,
|
Long: voteHelp,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
url := "https://%s/system/vote/motion/%d"
|
|
||||||
if oldURL {
|
|
||||||
url = "https://%s/rest/motions/motion-poll/%d/vote/"
|
|
||||||
}
|
|
||||||
url = fmt.Sprintf(url, cfg.domain, pollID)
|
|
||||||
|
|
||||||
return sendVotes(url, cfg.domain, count, interrupt, loginRetry)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().IntVarP(&count, "amount", "a", 10, "Amount of users to use.")
|
amount := cmd.Flags().IntP("amount", "n", 10, "Amount of users to use.")
|
||||||
cmd.Flags().IntVarP(&pollID, "poll_id", "i", 1, "ID of the poll to use.")
|
pollID := cmd.Flags().IntP("poll_id", "i", 1, "ID of the poll to use.")
|
||||||
cmd.Flags().BoolVarP(&oldURL, "old_url", "o", false, "Use old url to python.")
|
interrupt := cmd.Flags().Bool("interrupt", false, "Wait for a user input after login.")
|
||||||
cmd.Flags().BoolVar(&interrupt, "interrupt", false, "Wait for a user input after login.")
|
loginRetry := cmd.Flags().IntP("login_retry", "r", 3, "Retries send login requests before returning an error.")
|
||||||
cmd.Flags().IntVarP(&loginRetry, "login_retry", "r", 3, "Retries send login requests before returning an error.")
|
choice := cmd.Flags().IntP("choice", "c", 0, "Amount of answers per vote.")
|
||||||
|
|
||||||
return cmd
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||||
|
var assignment bool
|
||||||
|
if args[0] == "assignment" || args[0] == "a" {
|
||||||
|
assignment = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendVotes(url string, domain string, count int, interrupt bool, loginRetry int) error {
|
url := "%s/rest/motions/motion-poll/%d/vote/"
|
||||||
|
vote := `{"data":"Y"}`
|
||||||
|
if assignment {
|
||||||
|
url = "%s/rest/assignments/assignment-poll/%d/vote/"
|
||||||
|
options, err := assignmentPollOptions(cfg.addr(), *pollID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("fetch options: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
votedata := make(map[string]int)
|
||||||
|
for i, o := range options {
|
||||||
|
if *choice != 0 && i >= *choice {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
votedata[strconv.Itoa(o)] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded, err := json.Marshal(votedata)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("decoding assignment option data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vote = fmt.Sprintf(`{"data":%s}`, decoded)
|
||||||
|
}
|
||||||
|
url = fmt.Sprintf(url, cfg.addr(), *pollID)
|
||||||
|
|
||||||
var clients []*client.Client
|
var clients []*client.Client
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < *amount; i++ {
|
||||||
c, err := client.New(domain, fmt.Sprintf("dummy%d", i+1), "pass", client.WithLoginRetry(loginRetry))
|
c, err := client.New(cfg.addr(), fmt.Sprintf("dummy%d", i+1), "pass", client.WithLoginRetry(*loginRetry))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating client: %w", err)
|
return fmt.Errorf("creating client: %w", err)
|
||||||
}
|
}
|
||||||
clients = append(clients, c)
|
clients = append(clients, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Login %d clients", count)
|
log.Printf("Login %d clients", *amount)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
massLogin(clients, loginRetry)
|
massLogin(clients, *loginRetry)
|
||||||
log.Printf("All clients logged in %v", time.Now().Sub(start))
|
log.Printf("All clients logged in %v", time.Now().Sub(start))
|
||||||
|
|
||||||
if interrupt {
|
if *interrupt {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
fmt.Println("Hit enter to continue")
|
fmt.Println("Hit enter to continue")
|
||||||
reader.ReadString('\n')
|
reader.ReadString('\n')
|
||||||
@ -79,9 +100,76 @@ func sendVotes(url string, domain string, count int, interrupt bool, loginRetry
|
|||||||
}
|
}
|
||||||
|
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
massVotes(clients, url)
|
massVotes(clients, url, vote)
|
||||||
log.Printf("All Clients have voted in %v", time.Now().Sub(start))
|
log.Printf("All Clients have voted in %v", time.Now().Sub(start))
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignmentPollOptions(addr string, pollID int) ([]int, error) {
|
||||||
|
c, err := client.New(addr, "dummy1", "pass")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creating admin client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Login(); err != nil {
|
||||||
|
return nil, fmt.Errorf("login admin client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", fmt.Sprintf("%s/system/autoupdate", addr), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creating fetching all request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("sending fetch all request: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
body = []byte(fmt.Sprintf("[can not read body: %v", err))
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("got status %s: %s", resp.Status, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First message is a connection info. Throw it away.
|
||||||
|
var devNull json.RawMessage
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&devNull); err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var content struct {
|
||||||
|
Changed map[string][]json.RawMessage `json:"changed"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&content); err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
polls := content.Changed["assignments/assignment-poll"]
|
||||||
|
if len(polls) == 0 {
|
||||||
|
return nil, fmt.Errorf("no polls found")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, p := range polls {
|
||||||
|
var poll struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
OptionIDs []int `json:"options_id"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(p, &poll); err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding %dth poll: %w", i, err)
|
||||||
|
}
|
||||||
|
if poll.ID != pollID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return poll.OptionIDs, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no assignment-poll with id %d", pollID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func massLogin(clients []*client.Client, tries int) {
|
func massLogin(clients []*client.Client, tries int) {
|
||||||
@ -105,7 +193,7 @@ func massLogin(clients []*client.Client, tries int) {
|
|||||||
progress.Wait()
|
progress.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func massVotes(clients []*client.Client, url string) {
|
func massVotes(clients []*client.Client, url string, vote string) {
|
||||||
var wgVote sync.WaitGroup
|
var wgVote sync.WaitGroup
|
||||||
progress := mpb.New(mpb.WithWaitGroup(&wgVote))
|
progress := mpb.New(mpb.WithWaitGroup(&wgVote))
|
||||||
voteBar := progress.AddBar(int64(len(clients)))
|
voteBar := progress.AddBar(int64(len(clients)))
|
||||||
@ -114,8 +202,7 @@ func massVotes(clients []*client.Client, url string) {
|
|||||||
go func(c *client.Client) {
|
go func(c *client.Client) {
|
||||||
defer wgVote.Done()
|
defer wgVote.Done()
|
||||||
|
|
||||||
body := `{"data":"Y"}`
|
req, err := http.NewRequest("POST", url, strings.NewReader(vote))
|
||||||
req, err := http.NewRequest("POST", url, strings.NewReader(body))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error creating request: %v", err)
|
log.Printf("Error creating request: %v", err)
|
||||||
return
|
return
|
||||||
@ -124,7 +211,7 @@ func massVotes(clients []*client.Client, url string) {
|
|||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
if _, err := client.CheckStatus(c.Do(req)); err != nil {
|
if _, err := client.CheckStatus(c.Do(req)); err != nil {
|
||||||
log.Printf("Error sending vote request: %v", err)
|
log.Printf("Error sending vote request to %s: %v", url, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user