예제 #1
0
파일: purge.php 프로젝트: jquery/testswarm
    protected function purgeData($timestamp, $batchSize)
    {
        $date = swarmdb_dateformat($timestamp);
        // Based on ManageProjectScript::delete()
        $stats = array();
        $db = $this->getContext()->getDB();
        while (true) {
            $jobRows = $db->getRows(str_queryf('SELECT id
				FROM jobs
				WHERE created < %s
				LIMIT %u;', $date, $batchSize));
            if (!$jobRows) {
                // Done
                break;
            }
            $jobIDs = array_map(function ($row) {
                return $row->id;
            }, $jobRows);
            $this->out('...deleting ' . count($jobIDs) . ' jobs');
            $action = WipejobAction::newFromContext($this->getContext());
            $result = $action->doWipeJobs('delete', $jobIDs, $batchSize);
            $this->mergeStats($stats, $result);
        }
        // TODO: Purge rows from clients table for clients that are no
        // longer active and don't have 0 runresults after the purge.
        foreach ($stats as $key => $val) {
            $this->out("deleted {$key} rows: {$val}");
        }
        $this->out('');
        $this->out('Done!');
    }
예제 #2
0
 /**
  * @actionNote This action takes no parameters.
  */
 public function doAction()
 {
     $context = $this->getContext();
     $browserInfo = $context->getBrowserInfo();
     $db = $context->getDB();
     $conf = $context->getConf();
     $request = $context->getRequest();
     $resetTimedoutRuns = 0;
     // Get clients that are considered disconnected (not responding to the latest pings).
     // Then mark the runresults of its active runs as timed-out, and reset those runs so
     // they become available again for different clients in GetrunAction.
     $rows = $db->getRows(str_queryf("SELECT\n\t\t\t\trunresults.id as id\n\t\t\tFROM\n\t\t\t\trunresults\n\t\t\tINNER JOIN clients ON runresults.client_id = clients.id\n\t\t\tWHERE runresults.status = 1\n\t\t\tAND   clients.updated < %s;", swarmdb_dateformat(Client::getMaxAge($context))));
     if ($rows) {
         foreach ($rows as $row) {
             // Reset the run
             $ret = $db->query(str_queryf("UPDATE run_useragent\n\t\t\t\t\tSET\n\t\t\t\t\t\tstatus = 0,\n\t\t\t\t\t\tresults_id = NULL\n\t\t\t\t\tWHERE results_id = %u;", $row->id));
             // If the previous UPDATE query failed for whatever
             // reason, don't do the below query as that will lead
             // to data corruption (results with state LOST must never
             // be referenced from run_useragent.results_id).
             if ($ret) {
                 // Update status of the result
                 $ret = $db->query(str_queryf("UPDATE runresults\n\t\t\t\t\t\tSET status = %s\n\t\t\t\t\t\tWHERE id = %u;", ResultAction::$STATE_LOST, $row->id));
             }
             if ($ret) {
                 $resetTimedoutRuns++;
             }
         }
     }
     $this->setData(array("resetTimedoutRuns" => $resetTimedoutRuns));
 }
예제 #3
0
 /**
  * @actionNote This action takes no parameters.
  */
 public function doAction()
 {
     $browserInfo = $this->getContext()->getBrowserInfo();
     $db = $this->getContext()->getDB();
     $conf = $this->getContext()->getConf();
     $request = $this->getContext()->getRequest();
     // Get runs that were given to a client (status=1),
     // but haven't pinged back when they should.
     $maxage = time() - $conf->client->runTimeout - $conf->client->saveRetryMax * ($conf->client->saveReqTimeout + $conf->client->saveRetrySleep);
     $rows = $db->getRows(str_queryf("SELECT\n\t\t\t\tid,\n\t\t\t\tresults_id\n\t\t\tFROM\n\t\t\t\trun_useragent\n\t\t\tWHERE status = 1\n\t\t\tAND   updated < %s;", swarmdb_dateformat($maxage)));
     $resetTimedoutRuns = 0;
     // For clients that have stopped pinging,
     // assume disconnection (browser crashed, network lost, closed browser, ..)
     // @todo: Incorrect, the above query finds runs that have timed out.
     // Not dead runs from no longer connected clients, both should be checked.
     // The latter involves 3 cross-checks. Get runresults entry. Get client_id.
     // Get clients entry. Check updated property against pingTime+pingTimeMargin (see UserAction/SwarmstateAction).
     // Make 2 arrays of runUaIds and runresultsIds and unique them before the if(). Change if to if-count()
     if ($rows) {
         $resetTimedoutRuns = count($rows);
         foreach ($rows as $row) {
             $db->query(str_queryf("UPDATE run_useragent\n\t\t\t\t\tSET\n\t\t\t\t\t\tstatus = 0,\n\t\t\t\t\t\tresults_id = NULL\n\t\t\t\t\tWHERE id = %u;", $row->id));
             // Record runresults status as having timed-out (status=3)
             $db->query(str_queryf("UPDATE runresults\n\t\t\t\t\tSET status = %s\n\t\t\t\t\tWHERE id = %u;", ResultAction::$STATE_LOST, $row->results_id));
         }
     }
     $this->setData(array("resetTimedoutRuns" => $resetTimedoutRuns));
 }
예제 #4
0
 /**
  * @actionNote This action takes no parameters.
  */
 public function doAction()
 {
     $browserInfo = $this->getContext()->getBrowserInfo();
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     // Get runs that were given to a client (status=1),
     // but haven't responded with a save (status=2) within 5 minutes.
     $rows = $db->getRows(str_queryf("SELECT\n\t\t\t\trun_id,\n\t\t\t\tclient_id,\n\t\t\t\tuseragent_id\n\t\t\tFROM\n\t\t\t\trun_client, clients\n\t\t\tWHERE run_client.updated < %s\n\t\t\tAND   clients.id = run_client.client_id\n\t\t\tAND   run_client.status = 1;", swarmdb_dateformat(strtotime('5 minutes ago'))));
     $resetTimedoutRuns = 0;
     if ($rows) {
         $resetTimedoutRuns = count($rows);
         foreach ($rows as $row) {
             // Undo runcount and reset status
             $db->query(str_queryf("UPDATE\n\t\t\t\t\t\trun_useragent\n\t\t\t\t\tSET\n\t\t\t\t\t\truns = runs - 1,\n\t\t\t\t\t\tstatus = 0\n\t\t\t\t\tWHERE run_id = %u\n\t\t\t\t\tAND   useragent_id = %s;", $row->run_id, $row->useragent_id));
             // Remove run_client entry,
             // after 5 minutes we'll assume the client crashed, refreshed, closed the browser
             // or something else...
             $db->query(str_queryf("DELETE FROM\n\t\t\t\t\t\trun_client\n\t\t\t\t\tWHERE run_id = %u\n\t\t\t\t\tAND   client_id = %u;", $row->run_id, $row->client_id));
         }
     }
     // Reset runs that race-condition deleted themselves
     $db->query("UPDATE\n\t\t\t\trun_useragent\n\t\t\tSET\n\t\t\t\truns = 0,\n\t\t\t\tcompleted = 0,\n\t\t\t\tstatus = 0\n\t\t\tWHERE runs = max\n\t\t\tAND   NOT EXISTS (\n\t\t\t\tSELECT *\n\t\t\t\tFROM run_client, clients\n\t\t\t\tWHERE run_client.run_id = run_useragent.run_id\n\t\t\t\tAND   run_client.client_id = clients.id\n\t\t\t\tAND   clients.useragent_id = run_useragent.useragent_id\n\t\t\t);");
     $resetRaceConditionDeleted = $db->getAffectedRows();
     $this->setData(array("resetTimedoutRuns" => $resetTimedoutRuns, "resetRaceConditionDeleted" => $resetRaceConditionDeleted));
 }
예제 #5
0
    protected function loadNew()
    {
        $browserInfo = $this->context->getBrowserInfo();
        $db = $this->context->getDB();
        $request = $this->context->getRequest();
        // If the useragent isn't known, abort with an error message
        if (!$browserInfo->isInSwarmUaIndex()) {
            throw new SwarmException('Your browser is not needed by this swarm.');
        }
        $clientName = $request->getVal('item', 'anonymous');
        if (!$clientName) {
            // The UI javascript injects a default value and if the field is missing
            // the above WebRequest#getVal fallback catches it. But if the field
            // was submitted with an empty string, then just ignore it and go to anonymous as well.
            // We don't want to hold back potential swarm joiners.
            $clientName = 'anonymous';
        }
        if (!self::isValidName($clientName)) {
            throw new SwarmException('Invalid client name. Names should be no longer than 128 characters.');
        }
        // Insert in a new record for the client and get its ID
        $db->query(str_queryf('INSERT INTO clients (name, useragent_id, useragent, ip, updated, created)
			VALUES(%s, %s, %s, %s, %s, %s);', $clientName, $browserInfo->getSwarmUaID(), $browserInfo->getRawUA(), $request->getIP(), swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
        $this->clientRow = $db->getRow(str_queryf('SELECT * FROM clients WHERE id = %u LIMIT 1;', $db->getInsertId()));
    }
예제 #6
0
    /**
     * @actionMethod POST: Required.
     * @actionParam int job_id
     * @actionParam string type: one of 'delete', 'reset'.
     * @actionAuth: Required.
     */
    public function doAction()
    {
        $db = $this->getContext()->getDB();
        $request = $this->getContext()->getRequest();
        $jobID = $request->getInt('job_id');
        $wipeType = $request->getVal('type');
        if (!$jobID || !$wipeType) {
            $this->setError('missing-parameters');
            return;
        }
        if (!in_array($wipeType, array('delete', 'reset'))) {
            $this->setError('invalid-input', 'Invalid wipeType');
            return;
        }
        $projectID = $db->getOne(str_queryf('SELECT
				project_id
			FROM jobs
			WHERE id = %u;', $jobID));
        if (!$projectID) {
            $this->setError('invalid-input', 'Job not found');
            return;
        }
        // Check authentication
        if (!$this->doRequireAuth($projectID)) {
            return;
        }
        $runRows = $db->getRows(str_queryf('SELECT id
			FROM runs
			WHERE job_id = %u;', $jobID));
        if ($runRows) {
            foreach ($runRows as $runRow) {
                if ($wipeType === 'delete') {
                    $db->query(str_queryf('DELETE
						FROM run_useragent
						WHERE run_id = %u;', $runRow->id));
                } elseif ($wipeType === 'reset') {
                    $db->query(str_queryf('UPDATE run_useragent
						SET
							status = 0,
							completed = 0,
							results_id = NULL,
							updated = %s
						WHERE run_id = %u;', swarmdb_dateformat(SWARM_NOW), $runRow->id));
                }
            }
        }
        // This should be outside the if for $runRows, because jobs
        // can sometimes be created without any runs (by accidently).
        // Those should be deletable as well, thus this has to be outside the loop.
        // Also, no need to do this in a loop, just delete them all in one query.
        if ($wipeType === 'delete') {
            $db->query(str_queryf('DELETE
				FROM runs
				WHERE job_id = %u;', $jobID));
            $db->query(str_queryf('DELETE
				FROM jobs
				WHERE id = %u;', $jobID));
        }
        $this->setData(array('jobID' => $jobID, 'type' => $wipeType, 'result' => 'ok'));
    }
예제 #7
0
 protected function loadNew()
 {
     $browserInfo = $this->context->getBrowserInfo();
     $db = $this->context->getDB();
     $request = $this->context->getRequest();
     // If the useragent isn't known, abort with an error message
     if (!$browserInfo->isInSwarmUaIndex()) {
         throw new SwarmException("Your browser is not suported in this TestSwarm " . "(useragent string: {$browserInfo->getRawUA()}).");
     }
     // Running a client doesn't require being logged in
     $username = $request->getSessionData("username", $request->getVal("item"));
     if (!$username) {
         throw new SwarmException("Username required.");
     }
     // Figure out what the user's ID number is
     $userRow = $db->getRow(str_queryf("SELECT * FROM users WHERE name = %s LIMIT 1;", $username));
     // If the user doesn't have one, create a new user row for this name
     if (!$userRow || !$userRow->id) {
         $db->query(str_queryf("INSERT INTO users (name, updated, created) VALUES(%s, %s, %s);", $username, swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
         $userRow = $db->getRow(str_queryf("SELECT * FROM users WHERE id = %u LIMIT 1;", $db->getInsertId()));
     }
     // Insert in a new record for the client and get its ID
     $db->query(str_queryf("INSERT INTO clients (user_id, useragent_id, useragent, ip, updated, created)\n\t\t\tVALUES(%u, %s, %s, %s, %s, %s);", $userRow->id, $browserInfo->getSwarmUaID(), $browserInfo->getRawUA(), $request->getIP(), swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
     $this->clientRow = $db->getRow(str_queryf("SELECT * FROM clients WHERE id = %u LIMIT 1;", $db->getInsertId()));
     $this->userRow = $userRow;
 }
예제 #8
0
 /**
  * @requestParam browserSet string: Show useragents from a specific
  * browserset only.
  * @requestParam onlyactive bool: If true, only user agents that
  * have online clients and/or pending runs are included.
  * If both "browserSet" and "onlyactive" are used, the overlaping
  * subset will be output.
  */
 public function doAction()
 {
     $conf = $this->getContext()->getConf();
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     $showOnlyactive = $request->hasKey("onlyactive");
     $filterBrowserSet = $request->getVal("browserSet", false);
     $data = array("userAgents" => array());
     $uaIndex = BrowserInfo::getSwarmUAIndex();
     foreach ($uaIndex as $uaID => $uaData) {
         if ($filterBrowserSet && isset($conf->browserSets->{$filterBrowserSet}) && !in_array($uaID, $conf->browserSets->{$filterBrowserSet})) {
             continue;
         }
         // Count online clients with this UA
         $clients = $db->getOne(str_queryf("SELECT\n\t\t\t\t\tCOUNT(id)\n\t\t\t\tFROM clients\n\t\t\t\tWHERE useragent_id = %s\n\t\t\t\tAND   updated > %s", $uaID, swarmdb_dateformat(strtotime('1 minute ago'))));
         $clients = intval($clients);
         // Count pending runs for this UA
         $pendingRuns = $db->getOne(str_queryf("SELECT\n\t\t\t\t\tCOUNT(*)\n\t\t\t\tFROM run_useragent\n\t\t\t\tWHERE useragent_id = %s\n\t\t\t\tAND   status = 0;", $uaID));
         $pendingRuns = intval($pendingRuns);
         // Count past runs that can still be re-run to
         // possibly fix non-passing results
         $pendingReRuns = $db->getOne(str_queryf("SELECT\n\t\t\t\t\tCOUNT(*)\n\t\t\t\tFROM run_useragent\n\t\t\t\tWHERE useragent_id = %s\n\t\t\t\tAND   runs < max\n\t\t\t\tAND   completed > 0;", $uaID));
         $pendingReRuns = intval($pendingReRuns);
         if ($showOnlyactive && !$clients && !$pendingRuns && !$pendingReRuns) {
             continue;
         }
         $data["userAgents"][$uaID] = array("data" => $uaData, "stats" => array("onlineClients" => $clients, "pendingRuns" => $pendingRuns, "pendingReRuns" => $pendingReRuns));
     }
     $this->setData($data);
 }
예제 #9
0
 /**
  * @actionMethod POST: Required.
  * @actionParam client_id int
  * @actionParam run_token string
  * @actionParam run_id int
  * @actionParam fail int
  * @actionParam error int
  * @actionParam total int
  * @actionParam results string: HTML snapshot of the test results page.
  */
 public function doAction()
 {
     $browserInfo = $this->getContext()->getBrowserInfo();
     $conf = $this->getContext()->getConf();
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     if (!$request->wasPosted()) {
         $this->setError("requires-post");
         return;
     }
     $runToken = $request->getVal("run_token");
     if ($conf->client->requireRunToken && !$runToken) {
         $this->setError("invalid-input", "This TestSwarm does not allow unauthorized clients to join the swarm.");
         return;
     }
     $clientID = $request->getInt("client_id");
     if (!$clientID) {
         $this->setError("invalid-input");
         return;
     }
     // Create a Client object that verifies client id, user agent and run token.
     // Also updates the client 'alive' timestamp.
     // Throws exception (caught higher up) if stuff is invalid.
     $client = Client::newFromContext($this->getContext(), $runToken, $clientID);
     $runID = $request->getInt("run_id");
     $fail = $request->getInt("fail");
     $error = $request->getInt("error");
     $total = $request->getInt("total");
     $results = gzencode($request->getVal("results", ""));
     $db->query(str_queryf("UPDATE\n\t\t\t\trun_client\n\t\t\tSET\n\t\t\t\tstatus = 2,\n\t\t\t\tfail = %u,\n\t\t\t\terror = %u,\n\t\t\t\ttotal = %u,\n\t\t\t\tresults = %s,\n\t\t\t\tupdated = %s\n\t\t\tWHERE client_id = %u\n\t\t\tAND   run_id = %u\n\t\t\tLIMIT 1;", $fail, $error, $total, $results, swarmdb_dateformat(SWARM_NOW), $clientID, $runID));
     if (mysql_affected_rows() > 0) {
         // If we're 100% passing we don't need any more runs
         // Clear out other entries from other browsers for the same run
         // that were bad, since we now have a good one.
         if ($total > 0 && $fail === 0 && $error === 0) {
             $rows = $db->getRows(str_queryf("SELECT client_id\n\t\t\t\t\tFROM\n\t\t\t\t\t\trun_client, clients\n\t\t\t\t\tWHERE run_id = %u\n\t\t\t\t\tAND   client_id != %u\n\t\t\t\t\tAND   (total <= 0 OR error > 0 OR fail > 0)\n\t\t\t\t\tAND   clients.id = client_id\n\t\t\t\t\tAND   clients.useragent_id = %s;", $runID, $clientID, $client->getClientRow()->useragent_id));
             if ($rows) {
                 foreach ($rows as $row) {
                     $db->query(str_queryf("DELETE\n\t\t\t\t\t\t\tFROM run_client\n\t\t\t\t\t\t\tWHERE run_id = %u\n\t\t\t\t\t\t\tAND   client_id = %u;", $runID, $row->client_id));
                 }
             }
             $db->query(str_queryf("UPDATE\n\t\t\t\t\t\trun_useragent\n\t\t\t\t\tSET\n\t\t\t\t\t\truns = max,\n\t\t\t\t\t\tcompleted = completed + 1,\n\t\t\t\t\t\tstatus = 2,\n\t\t\t\t\t\tupdated = %s\n\t\t\t\t\tWHERE useragent_id = %s\n\t\t\t\t\tAND   run_id = %u\n\t\t\t\t\tLIMIT 1;", swarmdb_dateformat(SWARM_NOW), $browserInfo->getSwarmUaID(), $runID));
         } else {
             // Clear out old runs that timed out.
             if ($total > 0) {
                 $rows = $db->getRows(str_queryf("SELECT\n\t\t\t\t\t\t\tclient_id\n\t\t\t\t\t\tFROM\n\t\t\t\t\t\t\trun_client\n\t\t\t\t\t\tWHERE run_id = %u\n\t\t\t\t\t\tAND   client_id != %u\n\t\t\t\t\t\tAND   total <= 0;", $runID, $clientID));
                 if ($rows) {
                     foreach ($rows as $row) {
                         $db->query(str_queryf("DELETE\n\t\t\t\t\t\t\t\tFROM run_client\n\t\t\t\t\t\t\t\tWHERE run_id = %u\n\t\t\t\t\t\t\t\tAND   client_id = %u;", $runID, $row->client_id));
                     }
                 }
             }
             $db->query(str_queryf("UPDATE\n\t\t\t\t\t\trun_useragent\n\t\t\t\t\tSET\n\t\t\t\t\t\tcompleted = completed + 1,\n\t\t\t\t\t\tstatus = IF(completed + 1 < max, 1, 2),\n\t\t\t\t\t\tupdated = %s\n\t\t\t\t\tWHERE useragent_id = %s\n\t\t\t\t\tAND   run_id = %u\n\t\t\t\t\tLIMIT 1;", swarmdb_dateformat(SWARM_NOW), $browserInfo->getSwarmUaID(), $runID));
         }
     }
     $this->setData("ok");
 }
예제 #10
0
    public function doAction()
    {
        $db = $this->getContext()->getDB();
        $request = $this->getContext()->getRequest();
        $runID = $request->getInt("run_id");
        $clientID = $request->getInt("client_id");
        $useragentID = $request->getVal("useragent_id");
        if (!$runID || !$clientID) {
            $this->setError("missing-parameters");
            return;
        }
        $jobID = (int) $db->getOne(str_queryf('SELECT job_id FROM runs WHERE id = %u;', $runID));
        if (!$jobID) {
            $this->setError("invalid-input", "Run {$runID} not found.");
            return;
        }
        $jobOwner = $db->getOne(str_queryf('SELECT
				users.name as user_name
			FROM jobs, users
			WHERE jobs.id = %u
			AND   users.id = jobs.user_id
			LIMIT 1;', $jobID));
        if (!$jobOwner) {
            $this->setError("invalid-input", "Job {$jobID} not found.");
            return;
        }
        // Check authentication
        $userId = $this->doRequireAuth($jobOwner);
        if (!$userId) {
            return;
        }
        $runJobID = (int) $db->getOne(str_queryf('SELECT job_id
			FROM runs
			WHERE id = %u;', $runID));
        if ($runJobID !== $jobID) {
            $this->setError("invalid-input", "Run {$runID} does not belong to job {$jobID}.");
            return;
        }
        $clientUseragentID = $db->getOne(str_queryf('SELECT useragent_id
			FROM clients
			WHERE id = %u;', $clientID));
        if ($clientUseragentID !== $useragentID) {
            $this->setError("invalid-input", "Client {$clientID} does not run useragent {$useragentID}");
            return;
        }
        $db->query(str_queryf('UPDATE
				run_useragent
			SET
				status = 0,
				completed = 0,
				results_id = NULL,
				updated = %s
			WHERE run_id = %u
			AND   useragent_id = %s;', swarmdb_dateformat(SWARM_NOW), $runID, $useragentID));
        $this->setData(array("jobID" => $jobID, "runID" => $runID, "clientID" => $clientID, "useragentID" => $useragentID, "result" => "ok"));
    }
예제 #11
0
 /**
  * @actionMethod POST: Required.
  * @actionParam int job_id
  * @actionParam string type: one of 'delete', 'reset'
  */
 public function doAction()
 {
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     if (!$request->wasPosted()) {
         $this->setError("requires-post");
         return;
     }
     $jobID = $request->getInt("job_id");
     $wipeType = $request->getVal("type");
     if (!$jobID || !$wipeType) {
         $this->setError("missing-parameters");
         return;
     }
     if (!in_array($wipeType, array("delete", "reset", "cancel"))) {
         $this->setError("invalid-input");
         return;
     }
     $jobOwner = $db->getOne(str_queryf("SELECT\n\t\t\t\tusers.name as user_name\n\t\t\tFROM jobs, users\n\t\t\tWHERE jobs.id = %u\n\t\t\tAND   users.id = jobs.user_id\n\t\t\tLIMIT 1;", $jobID));
     if (!$jobOwner) {
         // Job row by this ID didn't exist
         $this->setError("invalid-input");
         return;
     }
     // Check authentication
     if ($request->getSessionData("auth") !== "yes" || $request->getSessionData("username") !== $jobOwner) {
         $this->setError("requires-auth");
         return;
     }
     $runRows = $db->getRows(str_queryf("SELECT id\n\t\t\tFROM runs\n\t\t\tWHERE job_id = %u;", $jobID));
     if ($runRows) {
         foreach ($runRows as $runRow) {
             switch ($wipeType) {
                 case "delete":
                     $db->query(str_queryf("DELETE\n\t\t\t\t\t\t\tFROM run_useragent\n\t\t\t\t\t\t\tWHERE run_id = %u;", $runRow->id));
                     break;
                 case "reset":
                     $db->query(str_queryf("UPDATE run_useragent\n\t\t\t\t\t\t\tSET\n\t\t\t\t\t\t\t\tstatus = 0,\n\t\t\t\t\t\t\t\tcompleted = 0,\n\t\t\t\t\t\t\t\tresults_id = NULL,\n\t\t\t\t\t\t\t\tupdated = %s\n\t\t\t\t\t\t\tWHERE run_id = %u;", swarmdb_dateformat(SWARM_NOW), $runRow->id));
                     break;
                 case "cancel":
                     $db->query(str_queryf("UPDATE run_useragent\n\t\t\t\t\t\t\tSET\n\t\t\t\t\t\t\t\tstatus = 3,\n\t\t\t\t\t\t\t\tupdated = %s\n\t\t\t\t\t\t\tWHERE run_id = %u \n\t\t\t\t\t\t\t\tAND status = 0;", swarmdb_dateformat(SWARM_NOW), $runRow->id));
                     break;
             }
         }
     }
     // This should be outside the if for $runRows, because jobs
     // can sometimes be created without any runs (by accidently).
     // Those should be deletable as well, thus this has to be outside the loop.
     // Also, no  need to do this in a loop, just delete them all in one query.
     if ($wipeType === "delete") {
         $db->query(str_queryf("DELETE\n\t\t\t\tFROM runs\n\t\t\t\tWHERE job_id = %u;", $jobID));
         $db->query(str_queryf("DELETE\n\t\t\t\tFROM jobs\n\t\t\t\tWHERE id = %u;", $jobID));
     }
     $this->setData(array("jobID" => $jobID, "type" => $wipeType, "result" => "ok"));
 }
예제 #12
0
 /**
  * @actionMethod POST: Required.
  * @actionParam string jobName: May contain HTML.
  * @actionParam int runMax
  * @actionParam array runNames
  * @actionParam array runUrls
  * @actionParam array browserSets
  * @actionAuth: Required.
  */
 public function doAction()
 {
     $conf = $this->getContext()->getConf();
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     $projectID = $this->doRequireAuth();
     if (!$projectID) {
         return;
     }
     $jobId = $request->getInt("job_id");
     $testName = $request->getVal("test_name");
     $uaId = $request->getVal("ua_id");
     $db->query(str_queryf("LOCK TABLES runs WRITE;"));
     $runId = $db->getOne(str_queryf('SELECT
   id
   FROM
   runs
   WHERE job_id = %u
   AND   name = %s
   ORDER BY id DESC
   LIMIT 1;', $jobId, $testName));
     if (!$runId) {
         // Create this run
         $isInserted = $db->query(str_queryf("INSERT INTO runs (job_id, name, url, created)\n        VALUES(%u, %s, %s, %s);", $jobId, $testName, "http://localhost", swarmdb_dateformat(SWARM_NOW)));
         $runId = $db->getInsertId();
     }
     $db->query(str_queryf("UNLOCK TABLES;"));
     if (!$runId) {
         $this->setError("internal-error", "Could not get or create run id");
         return;
     }
     $db->query(str_queryf("LOCK TABLES clients WRITE;"));
     $clientId = $db->getOne(str_queryf('SELECT
   id
   FROM
   clients
   WHERE useragent_id = %s
   LIMIT 1;', $uaId));
     if (!$clientId) {
         $isNew = true;
         $isInserted = $db->query(str_queryf("INSERT INTO clients (name, useragent_id, useragent, ip, updated, created)\n        VALUES(%s, %s, %s, %s, %s, %s);", $uaId, $uaId, "SauceLabs", "123.456.789.000", swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
         $clientId = $db->getInsertId();
     }
     $db->query(str_queryf("UNLOCK TABLES;"));
     $resultInserted = $db->query(str_queryf('INSERT INTO runresults
   (run_id, client_id, status, store_token, updated, created)
   VALUES(%u, %u, 1, %s, %s, %s);', $runId, $clientId, 0, swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
     $runresultsId = $db->getInsertId();
     $isInserted = $db->query(str_queryf("INSERT INTO run_useragent (run_id, useragent_id, max, results_id, updated, created)\n      VALUES(%u, %s, %u, %u, %s, %s);", $runId, $uaId, 1, $runresultsId, swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
     $newRunUAId = $db->getInsertId();
     $this->setData(array("resultsId" => $runresultsId, "runUAId" => $newRunUAId));
 }
예제 #13
0
    /**
     * @requestParam browserSet string: Show useragents from a specific
     * browserset only.
     * @requestParam onlyactive bool: If true, only user agents that
     * have online clients and/or pending runs are included.
     * If both "browserSet" and "onlyactive" are used, the overlaping
     * subset will be output.
     */
    public function doAction()
    {
        $conf = $this->getContext()->getConf();
        $db = $this->getContext()->getDB();
        $request = $this->getContext()->getRequest();
        $showOnlyactive = $request->getBool('onlyactive');
        $filterBrowserSet = $request->getVal('browserSet', false);
        $data = array('userAgents' => array());
        $uaIndex = BrowserInfo::getSwarmUAIndex();
        foreach ($uaIndex as $uaID => $uaData) {
            if ($filterBrowserSet && isset($conf->browserSets->{$filterBrowserSet}) && !in_array($uaID, $conf->browserSets->{$filterBrowserSet})) {
                continue;
            }
            // Count online clients with this UA
            $clients = $db->getOne(str_queryf('SELECT
					COUNT(id)
				FROM clients
				WHERE useragent_id = %s
				AND   updated > %s', $uaID, swarmdb_dateformat(time() - ($conf->client->pingTime + $conf->client->pingTimeMargin))));
            $clients = intval($clients);
            // Count active runs for this UA
            $activeRuns = $db->getOne(str_queryf('SELECT
					COUNT(*)
				FROM run_useragent
				WHERE useragent_id = %s
				AND   status = 1;', $uaID));
            $activeRuns = intval($activeRuns);
            // Count pending runs for this UA
            $pendingRuns = $db->getOne(str_queryf('SELECT
					COUNT(*)
				FROM run_useragent
				WHERE useragent_id = %s
				AND   status = 0
				AND   completed = 0;', $uaID));
            $pendingRuns = intval($pendingRuns);
            // Count past runs that can still be re-run to
            // possibly fix non-passing results
            $pendingReRuns = $db->getOne(str_queryf('SELECT
					COUNT(*)
				FROM run_useragent
				WHERE useragent_id = %s
				AND   status = 0
				AND   completed > 0;', $uaID));
            $pendingReRuns = intval($pendingReRuns);
            if ($showOnlyactive && !$clients && !$activeRuns && !$pendingRuns && !$pendingReRuns) {
                continue;
            }
            $data['userAgents'][$uaID] = array('data' => $uaData, 'stats' => array('onlineClients' => $clients, 'activeRuns' => $activeRuns, 'pendingRuns' => $pendingRuns, 'pendingReRuns' => $pendingReRuns));
        }
        $this->setData($data);
    }
예제 #14
0
    public function doWipeJobs($wipeType, array $jobIDs, $batchSize = 100)
    {
        $db = $this->getContext()->getDB();
        $stats = array('jobs' => 0, 'runs' => 0, 'run_useragent' => 0, 'runresults' => 0);
        $allRunRows = $db->getRows(str_queryf('SELECT id
			FROM runs
			WHERE job_id IN %l;', $jobIDs));
        if ($allRunRows) {
            $chunks = array_chunk($allRunRows, $batchSize);
            foreach ($chunks as $runRows) {
                $runIDs = array_map(function ($row) {
                    return $row->id;
                }, $runRows);
                if ($wipeType === 'delete') {
                    $db->query(str_queryf('DELETE
						FROM run_useragent
						WHERE run_id in %l;', $runIDs));
                } elseif ($wipeType === 'reset') {
                    $db->query(str_queryf('UPDATE run_useragent
						SET
							status = 0,
							completed = 0,
							results_id = NULL,
							updated = %s
						WHERE run_id in %l;', swarmdb_dateformat(SWARM_NOW), $runIDs));
                }
                $stats['run_useragent'] += $db->getAffectedRows();
                if ($wipeType === 'delete') {
                    $db->query(str_queryf('DELETE
						FROM runresults
						WHERE run_id in %l;', $runIDs));
                    $stats['runresults'] += $db->getAffectedRows();
                }
            }
        }
        // This should be outside the if for $allRunRows, because jobs
        // can sometimes be created without any runs (by accident).
        // Those should be deletable as well, thus this has to be outside the loop.
        // Also, no need to do this in a loop, just delete them all in one query.
        if ($wipeType === 'delete') {
            $db->query(str_queryf('DELETE
				FROM runs
				WHERE job_id IN %l;', $jobIDs));
            $stats['runs'] += $db->getAffectedRows();
            $db->query(str_queryf('DELETE
				FROM jobs
				WHERE id IN %l;', $jobIDs));
            $stats['jobs'] += $db->getAffectedRows();
        }
        return $stats;
    }
예제 #15
0
 /**
  * @requestParam "item" integer: job id
  * @requestParam "type" string: one of 'delete', 'reset'
  */
 public function doAction()
 {
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     if (!$request->wasPosted()) {
         $this->setError("requires-post");
         return;
     }
     $jobID = $request->getInt("job_id");
     $wipeType = $request->getVal("type");
     if (!$jobID || !$wipeType) {
         $this->setError("missing-parameters");
         return;
     }
     if (!in_array($wipeType, array("delete", "reset"))) {
         $this->setError("invalid-input");
         return;
     }
     $jobOwner = $db->getOne(str_queryf("SELECT\n\t\t\t\tusers.name as user_name\n\t\t\tFROM jobs, users\n\t\t\tWHERE jobs.id = %u\n\t\t\tAND   users.id = jobs.user_id\n\t\t\tLIMIT 1;", $jobID));
     if (!$jobOwner) {
         // Job row by this ID didn't exist
         $this->setError("invalid-input");
         return;
     }
     // Check authentication
     if ($request->getSessionData("auth") !== "yes" || $request->getSessionData("username") !== $jobOwner) {
         $this->setError("requires-auth");
         return;
     }
     $runRows = $db->getRows(str_queryf("SELECT\n\t\t\t\tid\n\t\t\tFROM\n\t\t\t\truns\n\t\t\tWHERE runs.job_id = %u;", $jobID));
     // Put this outside the if for runRows,
     // otherwise bogus jobs with 0 runs can't be deleted
     if ($wipeType === "delete") {
         $db->query(str_queryf("DELETE\n\t\t\t\tFROM run_client\n\t\t\t\tWHERE run_id in (\n\t\t\t\t\tSELECT id\n\t\t\t\t\tFROM runs\n\t\t\t\t\tWHERE job_id = %u\n\t\t\t\t);", $jobID));
         $db->query(str_queryf("DELETE\n\t\t\t\tFROM run_useragent\n\t\t\t\tWHERE run_id in (\n\t\t\t\t\tSELECT id\n\t\t\t\t\tFROM runs\n\t\t\t\t\tWHERE job_id = %u\n\t\t\t\t);", $jobID));
         $db->query(str_queryf("DELETE\n\t\t\t\tFROM runs\n\t\t\t\tWHERE job_id = %u;", $jobID));
         $db->query(str_queryf("DELETE\n\t\t\t\tFROM jobs\n\t\t\t\tWHERE id = %u;", $jobID));
     }
     if ($runRows) {
         foreach ($runRows as $runRow) {
             $db->query(str_queryf("DELETE\n\t\t\t\t\tFROM run_client\n\t\t\t\t\tWHERE run_id = %u;", $runRow->id));
             if ($wipeType === "delete") {
                 $db->query(str_queryf("DELETE\n\t\t\t\t\t\tFROM run_useragent\n\t\t\t\t\t\tWHERE run_id = %u;", $runRow->id));
             } elseif ($wipeType === "reset") {
                 $db->query(str_queryf("UPDATE run_useragent\n\t\t\t\t\t\tSET\n\t\t\t\t\t\t\truns = 0,\n\t\t\t\t\t\t\tcompleted = 0,\n\t\t\t\t\t\t\tstatus = 0,\n\t\t\t\t\t\t\tupdated = %s\n\t\t\t\t\t\tWHERE run_id = %u;", swarmdb_dateformat(SWARM_NOW), $runRow->id));
             }
         }
     }
     $this->setData(array("jobID" => $jobID, "type" => $wipeType, "result" => "ok"));
 }
예제 #16
0
 /**
  * @actionMethod GET: Required.
  * @actionParam int run_id
  * @actionParam string type: one of 'specStart', 'timeoutCheck'
  */
 public function doAction()
 {
     $request = $this->getContext()->getRequest();
     $runID = $request->getInt("run_id");
     $type = $request->getVal("type");
     if (!$runID || !$type) {
         $this->setError("missing-parameters");
         return;
     }
     if (!in_array($type, array("specStart", "timeoutCheck"))) {
         $this->setError("invalid-input");
         return;
     }
     $now = time();
     $db = $this->getContext()->getDB();
     $result = "";
     switch ($type) {
         case "specStart":
             if (!$request->wasGetted()) {
                 $this->setError("requires-get");
                 return;
             }
             $beatRate = $request->getInt("beatRate");
             $fail = $request->getInt("fail");
             $error = $request->getInt("error");
             $total = $request->getInt("total");
             if (!$beatRate) {
                 $this->setError("missing-parameters");
                 return;
             }
             $expected_update = $now + $beatRate;
             $db->query(str_queryf("UPDATE runresults\n\t\t\t\t\tSET\n\t\t\t\t\t\tfail = %u,\n\t\t\t\t\t\terror = %u,\n\t\t\t\t\t\ttotal = %u,\n\t\t\t\t\t\texpected_update = %s,\n\t\t\t\t\t\tupdated = %s\n\t\t\t\t\tWHERE run_id = %u\n\t\t\t\t\tAND status = 1\n\t\t\t\t\tAND ( expected_update IS NULL OR expected_update < %u );", $fail, $error, $total, swarmdb_dateformat($expected_update), swarmdb_dateformat($now), $runID, swarmdb_dateformat($expected_update)));
             $result = "ok";
             break;
         case "timeoutCheck":
             if (!$request->wasPosted()) {
                 $this->setError("requires-post");
                 return;
             }
             $timeoutMargin = 10;
             // 10 seconds margin
             $timestamp = $now + $timeoutMargin;
             // Check if run is timedout. Null expected_update stands for not timedout.
             $isTimedout = (bool) $db->getOne(str_queryf("SELECT IF(expected_update IS NULL, false, expected_update > %u)\n\t\t\t\t\tFROM runresults\n\t\t\t\t\tWHERE run_id = %u;", swarmdb_dateformat($timestamp), $runID));
             $result = array("testTimedout" => $isTimedout ? 'true' : 'false');
             break;
     }
     $this->setData($result);
 }
예제 #17
0
 public function doAction()
 {
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     if (!$request->wasPosted()) {
         $this->setError("requires-post");
         return;
     }
     $runID = $request->getInt("run_id");
     $clientID = $request->getInt("client_id");
     $useragentID = $request->getVal("useragent_id");
     if (!$runID || !$clientID) {
         $this->setError("missing-parameters");
         return;
     }
     $jobID = (int) $db->getOne(str_queryf("SELECT job_id FROM runs WHERE id = %u;", $runID));
     if (!$jobID) {
         $this->setError("invalid-input", "Run {$runID} not found.");
         return;
     }
     $jobOwner = $db->getOne(str_queryf("SELECT\n\t\t\t\tusers.name as user_name\n\t\t\tFROM jobs, users\n\t\t\tWHERE jobs.id = %u\n\t\t\tAND   users.id = jobs.user_id\n\t\t\tLIMIT 1;", $jobID));
     if (!$jobOwner) {
         $this->setError("invalid-input", "Job {$jobID} not found.");
         return;
     }
     // Check authentication
     if ($request->getSessionData("auth") !== "yes" || $request->getSessionData("username") !== $jobOwner) {
         $this->setError("requires-auth");
         return;
     }
     $runJobID = (int) $db->getOne(str_queryf("SELECT job_id\n\t\t\tFROM runs\n\t\t\tWHERE id = %u;", $runID));
     if ($runJobID !== $jobID) {
         $this->setError("invalid-input", "Run {$runID} does not belong to job {$jobID}.");
         return;
     }
     $clientUseragentID = $db->getOne(str_queryf("SELECT useragent_id\n\t\t\tFROM clients\n\t\t\tWHERE id = %u;", $clientID));
     if ($clientUseragentID !== $useragentID) {
         $this->setError("invalid-input", "Client {$clientID} does not run useragent {$useragentID}");
         return;
     }
     $db->query(str_queryf("UPDATE\n\t\t\t\trun_useragent\n\t\t\tSET\n\t\t\t\tstatus = 0,\n\t\t\t\tcompleted = 0,\n\t\t\t\tresults_id = NULL,\n\t\t\t\tupdated = %s\n\t\t\tWHERE run_id = %u\n\t\t\tAND   useragent_id = %s;", swarmdb_dateformat(SWARM_NOW), $runID, $useragentID));
     $this->setData(array("jobID" => $jobID, "runID" => $runID, "clientID" => $clientID, "useragentID" => $useragentID, "result" => "ok"));
 }
예제 #18
0
 /**
  * @actionMethod POST: Required.
  * @actionParam string jobName: May contain HTML.
  * @actionParam int runMax
  * @actionParam array runNames
  * @actionParam array runUrls
  * @actionParam array browserSets
  * @actionAuth: Required.
  */
 public function doAction()
 {
     $conf = $this->getContext()->getConf();
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     $projectID = $this->doRequireAuth();
     if (!$projectID) {
         return;
     }
     $buildId = $request->getInt("buildId");
     $jobName = $request->getVal("jobName");
     if (!$jobName || !$buildId) {
         $this->setError("missing-parameters");
         return;
     }
     // Verify job name maxlength (otherwise MySQL will crop it, which might
     // result in incomplete html, screwing up the JobPage).
     if (strlen($jobName) > 255) {
         $this->setError("invalid-input", "Job name too long (up to 255 characters).");
     }
     $db->query(str_queryf("LOCK TABLES jobs WRITE;"));
     $jobId = $db->getOne(str_queryf('SELECT
   id
   FROM
   jobs
   WHERE build_id = %u
     AND project_id = %s
   LIMIT 1;', $buildId, $projectID));
     $isNew = true;
     if ($jobId) {
         $isNew = false;
     } else {
         // Create job
         $isInserted = $db->query(str_queryf("INSERT INTO jobs (build_id, name, project_id, created)\n        VALUES (%u, %s, %s, %s);", $buildId, $jobName, $projectID, swarmdb_dateformat(SWARM_NOW)));
         $jobId = $db->getInsertId();
     }
     $db->query(str_queryf("UNLOCK TABLES;"));
     if (!$jobId) {
         $this->setError("internal-error", "Get or create of job failed.");
         return;
     }
     $this->setData(array("id" => $jobId, "isNew" => $isNew, "runTotal" => count($runs)));
 }
예제 #19
0
 /**
  * @actionMethod POST: Required.
  * @actionParam run_token string
  * @actionParam client_id int
  */
 public function doAction()
 {
     $browserInfo = $this->getContext()->getBrowserInfo();
     $conf = $this->getContext()->getConf();
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     if (!$request->wasPosted()) {
         $this->setError("requires-post");
         return;
     }
     $runToken = $request->getVal("run_token");
     if ($conf->client->requireRunToken && !$runToken) {
         $this->setError("invalid-input", "This TestSwarm does not allow unauthorized clients to join the swarm.");
         return;
     }
     $clientID = $request->getInt("client_id");
     if (!$clientID) {
         $this->setError("invalid-input");
         return;
     }
     // Create a Client object that verifies client id, user agent and run token.
     // Also updates the client 'alive' timestamp.
     // Throws exception (caught higher up) if stuff is invalid.
     $client = Client::newFromContext($this->getContext(), $runToken, $clientID);
     // Get oldest run for this user agent, that isn't on the max yet and isn't
     // already ran by another client.
     $runID = $db->getOne(str_queryf("SELECT\n\t\t\t\trun_id\n\t\t\tFROM\n\t\t\t\trun_useragent\n\t\t\tWHERE useragent_id = %s\n\t\t\tAND   runs < max\n\t\t\tAND NOT EXISTS (SELECT 1 FROM run_client WHERE run_useragent.run_id = run_id AND client_id = %u)\n\t\t\tORDER BY run_id DESC\n\t\t\tLIMIT 1;", $browserInfo->getSwarmUaID(), $clientID));
     $runInfo = false;
     // A run was found for the current user_agent
     if ($runID) {
         $row = $db->getRow(str_queryf("SELECT\n\t\t\t\t\truns.url as run_url,\n\t\t\t\t\tjobs.name as job_name,\n\t\t\t\t\truns.name as run_name\n\t\t\t\tFROM\n\t\t\t\t\truns, jobs\n\t\t\t\tWHERE runs.id = %u\n\t\t\t\tAND   jobs.id = runs.job_id\n\t\t\t\tLIMIT 1;", $runID));
         if ($row->run_url && $row->job_name && $row->run_name) {
             # Mark the run as "in progress" on the useragent
             $db->query(str_queryf("UPDATE run_useragent\n\t\t\t\t\tSET\n\t\t\t\t\t\truns = runs + 1,\n\t\t\t\t\t\tstatus = 1,\n\t\t\t\t\t\tupdated = %s\n\t\t\t\t\tWHERE run_id = %u\n\t\t\t\t\tAND   useragent_id = %s\n\t\t\t\t\tLIMIT 1;", swarmdb_dateformat(SWARM_NOW), $runID, $browserInfo->getSwarmUaID()));
             # Initialize the client run
             $db->query(str_queryf("INSERT INTO run_client\n\t\t\t\t\t(run_id, client_id, status, updated, created)\n\t\t\t\t\tVALUES(%u, %u, 1, %s, %s);", $runID, $clientID, swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
             $runInfo = array("id" => $runID, "url" => $row->run_url, "desc" => $row->job_name . ' ' . $row->run_name);
         }
     }
     $this->setData(array("confUpdate" => array("client" => $conf->client), "runInfo" => $runInfo));
 }
예제 #20
0
 /**
  * @actionNote This action takes no parameters.
  */
 public function doAction()
 {
     $browserInfo = $this->getContext()->getBrowserInfo();
     $db = $this->getContext()->getDB();
     $conf = $this->getContext()->getConf();
     $request = $this->getContext()->getRequest();
     $resetTimedoutRuns = 0;
     // Get clients that are considered disconnected (not responding to the latest pings).
     // Then mark the runresults of its active runs as timed-out, and reset those runs so
     // they become available again for different clients in GetrunAction.
     $clientMaxAge = swarmdb_dateformat(time() - ($conf->client->pingTime + $conf->client->pingTimeMargin));
     $rows = $db->getRows(str_queryf("SELECT\n\t\t\t\trunresults.id as id\n\t\t\tFROM\n\t\t\t\trunresults\n\t\t\tINNER JOIN clients ON runresults.client_id = clients.id\n\t\t\tWHERE runresults.status = 1\n\t\t\tAND   clients.updated < %s;", $clientMaxAge));
     if ($rows) {
         $resetTimedoutRuns = count($rows);
         foreach ($rows as $row) {
             // Reset the run
             $db->query(str_queryf("UPDATE run_useragent\n\t\t\t\t\tSET\n\t\t\t\t\t\tstatus = 0,\n\t\t\t\t\t\tresults_id = NULL\n\t\t\t\t\tWHERE results_id = %u;", $row->id));
             // Update status of the result
             $db->query(str_queryf("UPDATE runresults\n\t\t\t\t\tSET status = %s\n\t\t\t\t\tWHERE id = %u;", ResultAction::$STATE_LOST, $row->id));
         }
     }
     $this->setData(array("resetTimedoutRuns" => $resetTimedoutRuns));
 }
예제 #21
0
 /**
  * Creates the actual user, seperated from doAction to allow
  * make internal use easier, also use or modify the session.
  * @param $username string
  * @param $password string
  * @return bool
  */
 public function doCreateUser($username, $password)
 {
     $db = $this->getContext()->getDB();
     if (!$username || !$password) {
         $this->setError("missing-parameters");
         return false;
     }
     // Validate user name (github.com/jquery/testswarm/issues/118)
     // Only allow lowercase a-z, 0-9 and dashed, must start with a letter
     if (!preg_match("/^[a-z][-a-z0-9]*\$/", $username)) {
         $this->setError("invalid-input", "Username may only contain lowercase a-z, 0-9 and dashes and must start with a letter.");
         return false;
     }
     // Verify user name maxlength (otherwise MySQL will crop it)
     if (strlen($username) > 255) {
         $this->setError("invalid-input", "User name too long (up to 255 characters).");
     }
     // Check if this user name is already taken
     $row = $db->getRow(str_queryf("SELECT id FROM users WHERE name = %s;", $username));
     if ($row) {
         $this->setError("invalid-input", "Username \"{$username}\" is already taken.");
         return false;
     }
     // Random between 1,000,000,000 and 9,999,999,999
     $seedHash = sha1(mt_rand(1000000000, 9999999999.0));
     $passwordHash = sha1($seedHash . $password);
     $authTokenHash = sha1(mt_rand(1000000000, 9999999999.0));
     // Create the user
     $isInserted = $db->query(str_queryf("INSERT INTO users\n\t\t\t(name, updated, created, seed, password, auth)\n\t\t\tVALUES(%s, %s, %s, %s, %s, %s);", $username, swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW), $seedHash, $passwordHash, $authTokenHash));
     $newUserId = $db->getInsertId();
     if (!$isInserted || !$newUserId) {
         $this->setError("internal-error", "Insertion of user into database failed.");
         return false;
     }
     $this->setData(array("status" => "logged-in", "username" => $username, "userID" => $newUserId));
     return true;
 }
예제 #22
0
 /**
  * @actionMethod POST: Required.
  * @actionParam string jobName: May contain HTML.
  * @actionParam int runMax
  * @actionParam array runNames
  * @actionParam array runUrls
  * @actionParam array browserSets
  * @actionAuth: Required.
  */
 public function doAction()
 {
     $conf = $this->getContext()->getConf();
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     $projectID = $this->doRequireAuth();
     if (!$projectID) {
         return;
     }
     $uaId = $request->getVal("ua_id");
     $isNew = false;
     $clientId = $db->getOne(str_queryf('SELECT
   id
   FROM
   clients
   WHERE useragent_id = %s
   LIMIT 1;', $uaId));
     if (!$clientId) {
         $isNew = true;
         $isInserted = $db->query(str_queryf("INSERT INTO clients (name, useragent_id, useragent, ip, updated, created)\n        VALUES(%s, %s, %s, %s, %s, %s);", $uaId, $uaId, "SauceLabs", "123.456.789.000", swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
         $clientId = $db->getInsertId();
     }
     $this->setData(array("clientId" => $clientId, "isNew" => $isNew));
 }
예제 #23
0
 protected function update()
 {
     $db = $this->getContext()->getDB();
     $id = $this->getOption('id');
     $displayTitle = $this->getOption('display-title');
     $siteUrl = $this->getOption('site-url');
     if (!$id) {
         $this->error('--id is required.');
     }
     // Check if this project exists.
     $field = $db->getOne(str_queryf('SELECT id FROM projects WHERE id = %s;', $id));
     if (!$field) {
         $this->error('Project does not exist. Set --create to create a project.');
     }
     if (!$displayTitle && !$siteUrl) {
         $this->error('Unable to perform update. No values provided.');
     }
     if ($displayTitle) {
         $isUpdated = $db->query(str_queryf('UPDATE projects SET display_title = %s, updated = %s WHERE id = %s;', $displayTitle, swarmdb_dateformat(SWARM_NOW), $id));
         if (!$isUpdated) {
             $this->error('Failed to update database.');
         }
     }
     if ($siteUrl) {
         $isUpdated = $db->query(str_queryf('UPDATE projects SET site_url = %s, updated = %s WHERE id = %s;', $siteUrl, swarmdb_dateformat(SWARM_NOW), $id));
         if (!$isUpdated) {
             $this->error('Failed to update database.');
         }
     }
     $this->out('Project has been updated.');
 }
예제 #24
0
    /**
     * @param string $id
     * @param array $options
     * @return array Exposes the new auth token
     */
    public function create($id, array $options = null)
    {
        $db = $this->getContext()->getDB();
        $password = isset($options['password']) ? $options['password'] : null;
        $displayTitle = isset($options['displayTitle']) ? $options['displayTitle'] : null;
        $siteUrl = isset($options['siteUrl']) ? $options['siteUrl'] : '';
        if (!$id || !$displayTitle || !$password) {
            $this->setError('missing-parameters');
            return;
        }
        // Check if a project by this id doesn't exist already
        $row = $db->getOne(str_queryf('SELECT id FROM projects WHERE id = %s;', $id));
        if ($row) {
            $this->setError('invalid-input', 'Unable to create project, a project by that name exists already.');
            return;
        }
        // Validate project id
        if (!LoginAction::isValidName($id)) {
            $this->setError('invalid-input', 'Project ids must be in format: "' . LoginAction::getNameValidationRegex() . '".');
            return;
        }
        // maxlength (otherwise MySQL will crop it)
        if (strlen($displayTitle) > 255) {
            $this->setError('Display title has to be no longer than 255 characters.');
            return;
        }
        // Create the project
        $authToken = LoginAction::generateRandomHash(40);
        $authTokenHash = sha1($authToken);
        $isInserted = $db->query(str_queryf('INSERT INTO projects
			(id, display_title, site_url, password, auth_token, updated, created)
			VALUES(%s, %s, %s, %s, %s, %s, %s);', $id, $displayTitle, $siteUrl, LoginAction::generatePasswordHash($password), $authTokenHash, swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
        if (!$isInserted) {
            $this->setError('internal-error', 'Insertion of row into database failed.');
            return;
        }
        return array('authToken' => $authToken);
    }
예제 #25
0
 public function doAction()
 {
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     $userName = $request->getVal("item");
     if (!$userName) {
         $this->setError("missing-parameters");
         return;
     }
     $userID = $db->getOne(str_queryf("SELECT id FROM users WHERE name = %s;", $userName));
     $userID = intval($userID);
     if (!$userID) {
         $this->setError("invalid-input", "User does not exist");
         return;
     }
     $uaIndex = BrowserInfo::getSwarmUAIndex();
     // Active clients
     $activeClients = array();
     $clientRows = $db->getRows(str_queryf("SELECT\n\t\t\t\tuseragent_id,\n\t\t\t\tuseragent,\n\t\t\t\tcreated\n\t\t\tFROM\n\t\t\t\tclients\n\t\t\tWHERE user_id = %u\n\t\t\tAND   updated > %s\n\t\t\tORDER BY created DESC;", $userID, swarmdb_dateformat(strtotime("1 minutes ago"))));
     if ($clientRows) {
         foreach ($clientRows as $clientRow) {
             $bi = BrowserInfo::newFromContext($this->getContext(), $clientRow->useragent);
             $activeClient = array("uaID" => $clientRow->useragent_id, "uaRaw" => $bi->getRawUA(), "uaData" => $bi->getSwarmUaItem(), "uaBrowscap" => $bi->getBrowscap());
             self::addTimestampsTo($activeClient, $clientRow->created, "connected");
             $activeClients[] = $activeClient;
         }
     }
     // Recent jobs
     $recentJobs = array();
     // List of all user agents used in recent jobs
     // This is as helper allow creating proper gaps when iterating
     // over jobs.
     $userAgents = array();
     $jobRows = $db->getRows(str_queryf("SELECT\n\t\t\t\tid,\n\t\t\t\tname\n\t\t\tFROM\n\t\t\t\tjobs\n\t\t\tWHERE jobs.user_id = %u\n\t\t\tORDER BY jobs.created DESC\n\t\t\tLIMIT 15;", $userID));
     if ($jobRows) {
         $uaRunStatusStrength = array_flip(array("passed", "new", "progress", "failed", "timedout", "error"));
         foreach ($jobRows as $jobRow) {
             $jobID = intval($jobRow->id);
             $jobActionContext = $this->getContext()->createDerivedRequestContext(array("action" => "job", "item" => $jobID), "GET");
             $jobAction = JobAction::newFromContext($jobActionContext);
             $jobAction->doAction();
             if ($jobAction->getError()) {
                 $this->setError($jobAction->getError());
                 return;
             }
             $jobActionData = $jobAction->getData();
             // Add user agents array of this job to the overal user agents list.
             // php array+ automatically fixes clashing keys. The values are always the same
             // so it doesn't matter whether or not it overwrites.
             $userAgents += $jobActionData["userAgents"];
             // The summerized status for each user agent run
             // of this job. e.g. if all are new except one,
             // then it will be on "progress", if all are complete
             // then the worst failure is put in the summary
             $uaSummary = array();
             $uaNotNew = array();
             $uaHasIncomplete = array();
             $uaStrongestStatus = array();
             foreach ($jobActionData["runs"] as $run) {
                 foreach ($run["uaRuns"] as $uaID => $uaRun) {
                     if ($uaRun["runStatus"] !== "new" && !in_array($uaID, $uaNotNew)) {
                         $uaNotNew[] = $uaID;
                     }
                     if ($uaRun["runStatus"] === "new" || $uaRun["runStatus"] === "progress") {
                         if (!in_array($uaID, $uaHasIncomplete)) {
                             $uaHasIncomplete[] = $uaID;
                         }
                     }
                     if (!isset($uaStrongestStatus[$uaID]) || $uaRunStatusStrength[$uaRun["runStatus"]] > $uaRunStatusStrength[$uaStrongestStatus[$uaID]]) {
                         $uaStrongestStatus[$uaID] = $uaRun["runStatus"];
                     }
                     $uaSummary[$uaID] = !in_array($uaID, $uaNotNew) ? "new" : (in_array($uaID, $uaHasIncomplete) ? "progress" : $uaStrongestStatus[$uaID]);
                 }
             }
             $recentJobs[] = array("id" => $jobID, "name" => $jobRow->name, "url" => swarmpath("job/{$jobID}", "fullurl"), "uaSummary" => $uaSummary);
         }
     }
     natcaseksort($userAgents);
     $this->setData(array("userName" => $userName, "activeClients" => $activeClients, "recentJobs" => $recentJobs, "uasInJobs" => $userAgents));
 }
예제 #26
0
    /**
     * @actionMethod POST: Required.
     * @actionParam run_token string
     * @actionParam client_id int
     */
    public function doAction()
    {
        $browserInfo = $this->getContext()->getBrowserInfo();
        $conf = $this->getContext()->getConf();
        $db = $this->getContext()->getDB();
        $request = $this->getContext()->getRequest();
        if (!$request->wasPosted()) {
            $this->setError("requires-post");
            return;
        }
        $runToken = $request->getVal("run_token");
        if ($conf->client->requireRunToken && !$runToken) {
            $this->setError("missing-parameters", "This TestSwarm does not allow unauthorized clients to join the swarm.");
            return;
        }
        $clientID = $request->getInt("client_id");
        if (!$clientID) {
            $this->setError("missing-parameters");
            return;
        }
        // Create a Client object that verifies client id, user agent and run token.
        // Also updates the client 'alive' timestamp.
        // Throws exception (caught higher up) if stuff is invalid.
        $client = Client::newFromContext($this->getContext(), $runToken, $clientID);
        // Get oldest idle (status=0) run for this user agent.
        // Except if it was already ran in this client in the past (client_id=%u), because
        // in that case it must've failed. We don't want it to run in the same client again.
        $runID = $db->getOne(str_queryf('SELECT
				run_id
			FROM
				run_useragent
			WHERE useragent_id = %s
			AND   status = 0
			AND NOT EXISTS (SELECT 1 FROM runresults WHERE runresults.run_id = run_useragent.run_id AND runresults.client_id = %u)
			ORDER BY run_id DESC
			LIMIT 1;', $browserInfo->getSwarmUaID(), $clientID));
        $runInfo = false;
        // A run was found for the current user_agent
        if ($runID) {
            $row = $db->getRow(str_queryf("SELECT\n\t\t\t\t\truns.url as run_url,\n\t\t\t\t\tjobs.name as job_name,\n\t\t\t\t\truns.name as run_name\n\t\t\t\tFROM\n\t\t\t\t\truns, jobs\n\t\t\t\tWHERE runs.id = %u\n\t\t\t\tAND   jobs.id = runs.job_id\n\t\t\t\tLIMIT 1;", $runID));
            if ($row->run_url && $row->job_name && $row->run_name) {
                // Create stub runresults entry
                $storeToken = sha1(mt_rand());
                $isInserted = $db->query(str_queryf('INSERT INTO runresults
					(run_id, client_id, status, store_token, updated, created)
					VALUES(%u, %u, 1, %s, %s, %s);', $runID, $clientID, sha1($storeToken), swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
                $runresultsId = $db->getInsertId();
                if (!$isInserted || !$runresultsId) {
                    $this->setError('internal-error', 'Creation of runresults database entry failed.');
                    return false;
                }
                // Mark as in-progress (status=1), and link runresults entry
                $db->query(str_queryf('UPDATE run_useragent
					SET
						status = 1,
						updated = %s,
						results_id = %u
					WHERE run_id = %u
					AND   useragent_id = %s
					LIMIT 1;', swarmdb_dateformat(SWARM_NOW), $runresultsId, $runID, $browserInfo->getSwarmUaID()));
                $runInfo = array("id" => $runID, "url" => $row->run_url, "desc" => $row->job_name . ' ' . $row->run_name, 'resultsId' => $runresultsId, 'resultsStoreToken' => $storeToken);
            }
        }
        $this->setData(array('runInfo' => $runInfo));
    }
예제 #27
0
 /**
  * @actionMethod POST: Required.
  * @actionParam jobName string: May contain HTML.
  * @actionParam runMax int
  * @actionParam runNames array
  * @actionParam runUrls array
  * @actionParam browserSets array
  * @actionParam string authUsername
  * @actionParam string authToken
  * @actionAuth: Yes.
  */
 public function doAction()
 {
     $conf = $this->getContext()->getConf();
     $db = $this->getContext()->getDB();
     $request = $this->getContext()->getRequest();
     $userId = $this->doRequireAuth();
     if (!$userId) {
         return;
     }
     $jobName = $request->getVal("jobName");
     $runMax = $request->getInt("runMax");
     $runNames = $request->getArray("runNames");
     $runUrls = $request->getArray("runUrls");
     $browserSets = $request->getArray("browserSets");
     if (!$jobName || !$runNames || !count($runNames) || !$runUrls || !count($runUrls) || !$browserSets || !count($browserSets)) {
         $this->setError("missing-parameters");
         return;
     }
     if ($runMax < 1 || $runMax > 99) {
         $this->setError("invalid-input", "runMax must be a number between 1 and 99.");
         return;
     }
     $runs = array();
     // Loop through runNames / runUrls to validate them ahead of time,
     // and filter out empty ones from the AddjobPage.
     foreach ($runNames as $runNr => $runName) {
         if (!isset($runUrls[$runNr])) {
             $this->setError("invalid-input", "One or more runs is missing a URL.");
             return;
         }
         $runUrl = $runUrls[$runNr];
         // Filter out empty submissions,
         // AddjobPage may submit more input fields then filled in
         if ($runUrl == '' && $runName == '') {
             continue;
         }
         if ($runUrl == '' || $runName == '') {
             $this->setError("invalid-input", "Run names and urls must be non-empty.");
             return;
         }
         if (strlen($runName) > 255) {
             $formRunNr = $runNr + 1;
             // offset 0
             $this->setError("invalid-input", "Run #{$formRunNr} name was too long (up to 255 characters).");
             return;
         }
         $runs[] = array("name" => $runName, "url" => $runUrl);
     }
     if (!count($runs)) {
         $this->setError('missing-parameters', 'Job must have atleast 1 run.');
         return;
     }
     // Generate a list of user agent IDs based on the selected browser sets
     $browserSetsCnt = count($browserSets);
     $browserSets = array_unique($browserSets);
     if ($browserSetsCnt !== count($browserSets)) {
         $this->setError("invalid-input", "Duplicate entries in browserSets parameter.");
         return;
     }
     $swarmUaIndex = BrowserInfo::getSwarmUAIndex();
     $uaIDs = array();
     foreach ($browserSets as $browserSet) {
         if (!isset($conf->browserSets->{$browserSet})) {
             $this->setError("invalid-input", "Unknown browser set: {$browserSet}.");
             return;
         }
         // Merge the arrays, and re-index with unique (also prevents duplicate entries)
         $uaIDs = array_unique(array_merge($uaIDs, $conf->browserSets->{$browserSet}));
     }
     if (!count($uaIDs)) {
         $this->setError("data-corrupt", "No user agents matched the generated browserset filter.");
         return;
     }
     // Verify job name maxlength (otherwise MySQL will crop it, which might
     // result in incomplete html, screwing up the JobPage).
     if (strlen($jobName) > 255) {
         $this->setError("invalid-input", "Job name too long (up to 255 characters).");
     }
     // Create job
     $isInserted = $db->query(str_queryf("INSERT INTO jobs (user_id, name, created)\n\t\t\tVALUES (%u, %s, %s);", $userId, $jobName, swarmdb_dateformat(SWARM_NOW)));
     $newJobId = $db->getInsertId();
     if (!$isInserted || !$newJobId) {
         $this->setError("internal-error", "Insertion of job into database failed.");
         return;
     }
     // Create all runs and schedule them for the wanted browsersets in run_useragent
     foreach ($runs as $run) {
         // Create this run
         $isInserted = $db->query(str_queryf("INSERT INTO runs (job_id, name, url, created)\n\t\t\t\tVALUES(%u, %s, %s, %s);", $newJobId, $run['name'], $run['url'], swarmdb_dateformat(SWARM_NOW)));
         $newRunId = $db->getInsertId();
         if (!$isInserted || !$newRunId) {
             $this->setError("internal-error", "Insertion of job into database failed.");
             return;
         }
         // Schedule run_useragent entries for all user agents matching
         // the browerset(s) for this job.
         foreach ($uaIDs as $uaID) {
             $isInserted = $db->query(str_queryf("INSERT INTO run_useragent (run_id, useragent_id, max, updated, created)\n\t\t\t\t\tVALUES(%u, %s, %u, %s, %s);", $newRunId, $uaID, $runMax, swarmdb_dateformat(SWARM_NOW), swarmdb_dateformat(SWARM_NOW)));
         }
     }
     $this->setData(array("id" => $newJobId, "runTotal" => count($runs), "uaTotal" => count($uaIDs)));
 }
예제 #28
0
    /**
     * The actual database updates
     * Friendly reminder from http://dev.mysql.com/doc/refman/5.1/en/alter-table.html
     * - Column name must be mentioned twice in ALTER TABLE CHANGE
     * - Definition must be complete
     *   So 'CHANGE foo BIGINT' on a 'foo INT UNSIGNED DEFAULT 1' will remove
     *   the default and unsigned property.
     *   Except for PRIMARY KEY or UNIQUE properties, those must never be
     *   part of a CHANGE clause.
     */
    protected function doDatabaseUpdates()
    {
        if ($this->getContext()->dbLock()) {
            $this->error('Database is currently locked, please remove ./cache/database.lock before updating.');
        }
        $db = $this->getContext()->getDB();
        $this->out('Setting database.lock, other requests may not access the database during the update.');
        $this->getContext()->dbLock(true);
        $this->out('Running tests on the database to detect which updates are needed.');
        /**
         * 0.2.0 -> 1.0.0-alpha (patch-new-ua-runresults.sql)
         * useragents and run_client table removed, many column changes, new runresults table.
         */
        // If the previous version was before 1.0.0 we won't offer an update, because most
        // changes in 1.0.0 can't be simulated without human intervention. The changes are not
        // backwards compatible. Instead do a few quick checks to verify this is in fact a
        // pre-1.0.0 database, then ask the user for a re-install from scratch
        // (except for the users table).
        $has_run_client = $db->tableExists('run_client');
        $has_users_request = $db->fieldExists('users', 'request');
        $clients_useragent_id = $db->fieldInfo('clients', 'useragent_id');
        if (!$clients_useragent_id) {
            $this->unknownDatabaseState('clients.useragent_id not found');
            return;
        }
        if (!$has_run_client && !$has_users_request && !$clients_useragent_id->numeric && $clients_useragent_id->type === 'string') {
            $this->out('... run_client table already dropped');
            $this->out('... users.request already dropped');
            $this->out('... client.useragent_id is up to date');
        } else {
            $this->out("\n" . "It appears this database is from before 1.0.0. No update exists for those versions.\n" . "The updater could re-install TestSwarm (optionally preserving user accounts)\n" . "THIS WILL DELETE ALL DATA.\nContinue? (Y/N)");
            $reinstall = $this->cliInput();
            if ($reinstall !== 'Y') {
                // Nothing left to do. Remove database.lock and abort the script
                $this->getContext()->dbLock(false);
                return;
            }
            $this->out("Import user names and tokens from the old database after re-installing?\n" . "(Note: password and seed cannot be restored due to incompatibility in the database.\n" . ' Instead the auth token will be used as the the new password) (Y/N)');
            $reimportUsers = $this->cliInput();
            // Drop all known TestSwarm tables in the database
            // (except users, handled separately)
            foreach (array('run_client', 'clients', 'run_useragent', 'useragents', 'runs', 'jobs') as $dropTable) {
                $this->outRaw("Dropping {$dropTable} table...");
                $exists = $db->tableExists($dropTable);
                if ($exists) {
                    $dropped = $db->query('DROP TABLE ' . $db->addIdentifierQuotes($dropTable));
                    $this->out(' ' . ($dropped ? 'OK' : 'FAILED'));
                } else {
                    $this->out('SKIPPED (didn\'t exist)');
                }
            }
            // Handle users table (reimport or drop as well)
            $userRows = array();
            if ($reimportUsers === 'Y') {
                $this->out('Upgrading users table');
                $this->outRaw('Fetching current users...');
                $has_users = $db->tableExists('users');
                if (!$has_users) {
                    $this->out('SKIPPED (users table didn\'t exist)');
                } else {
                    $userRows = $db->getRows('SELECT * FROM users');
                    $this->out('OK');
                }
            }
            $this->outRaw('Dropping users table...');
            $dropped = $db->query('DROP TABLE users');
            $this->out(' ' . ($dropped ? 'OK' : 'FAILED'));
            // Create new tables
            $this->outRaw('Creating new tables... (this may take a few minutes)');
            global $swarmInstallDir;
            $fullSchemaFile = "{$swarmInstallDir}/config/tables.sql";
            if (!is_readable($fullSchemaFile)) {
                $this->error('Can\'t read schema file');
            }
            $fullSchemaSql = file_get_contents($fullSchemaFile);
            $executed = $db->batchQueryFromFile($fullSchemaSql);
            if (!$executed) {
                $this->error('Creating new tables failed');
            }
            $this->out('OK');
            if ($reimportUsers === 'Y') {
                $this->out('Re-importing ' . count($userRows) . ' users...');
                foreach ($userRows as $userRow) {
                    $this->outRaw('- creating user "' . $userRow->name . '"... ');
                    if (empty($userRow->password) || empty($userRow->seed) || empty($userRow->auth)) {
                        $this->out('SKIPPED: Not a project account but a swarm client.');
                        continue;
                    }
                    try {
                        $signupAction = SignupAction::newFromContext($this->getContext());
                        // Password stored in the old database is a hash of the old seed (of type 'double'
                        // and the actual password. We can't create this user with the same password because
                        // sha1 is not supposed to be decodable.
                        // I tried overriding the created row after the creation with the old seed and password,
                        // but that didn't work because the old seed doesn't fit in the new seed field (of binary(40)).
                        // When inserted, mysql transforms it into something else and sha1(seed + password) will no
                        // longer match the hash. So instead create the new user with the auth token as password.
                        $signupAction->doCreateUser($userRow->name, $userRow->auth);
                        $err = $signupAction->getError();
                        if (!$err) {
                            $this->outRaw('OK. Restoring auth token... ');
                            $data = $signupAction->getData();
                            $updated = $db->query(str_queryf('UPDATE users
								SET
									auth = %s
								WHERE id = %u', $userRow->auth, $data['userID']));
                            $this->out($updated ? 'OK.' : 'FAILED.');
                        } else {
                            $this->out("FAILED. SignupAction error. {$err['info']}");
                        }
                    } catch (Exception $e) {
                        $this->out("FAILED. Unexpected exception thrown while creating account. {$e->getMessage()}");
                    }
                }
            }
            // End of users re-import
        }
        // End of patch-new-ua-runresults.sql
        /**
         * 1.0.0-alpha (patch-users-projects-conversion.sql)
         * users table removed, new projects table, various column changes.
         */
        $has_users = $db->tableExists('users');
        $has_clients_user_id = $db->fieldInfo('clients', 'user_id');
        $has_jobs_user_id = $db->fieldInfo('jobs', 'user_id');
        $has_projects = $db->tableExists('projects');
        $has_clients_name = $db->fieldInfo('clients', 'name');
        $has_jobs_project_id = $db->fieldInfo('jobs', 'project_id');
        $has_clients_useragent_id = $db->fieldInfo('clients', 'useragent_id');
        if (!$has_users && !$has_clients_user_id && !$has_jobs_user_id) {
            $this->out('... users table already dropped');
            $this->out('... clients.user_id already dropped');
            $this->out('... jobs.user_id already dropped');
        } else {
            // Verify that the entire database is in the 1.0.0-alpha2012 state,
            // not just part of it.
            foreach (array('users table' => $has_users, 'clients.user_id' => $has_clients_user_id, 'jobs.user_id' => $has_jobs_user_id, 'projects table' => !$has_projects, 'clients.name' => !$has_clients_name, 'jobs.project_id' => !$has_jobs_project_id, 'clients.useragent_id' => $has_clients_useragent_id) as $label => $isAsExpected) {
                if (!$isAsExpected) {
                    $this->unknownDatabaseState($label . ' not found');
                    return;
                }
            }
            $this->out('Schema changes before users-projects-conversion migration...');
            $this->out('... creating projects table');
            $db->query("CREATE TABLE `projects` (\n  `id` varchar(255) binary NOT NULL PRIMARY KEY,\n  `display_title` varchar(255) binary NOT NULL,\n  `site_url` blob NOT NULL default '',\n  `password` tinyblob NOT NULL,\n  `auth_token` tinyblob NOT NULL,\n  `updated` binary(14) NOT NULL,\n  `created` binary(14) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
            $this->out('... adding clients.name');
            $db->query("ALTER TABLE clients\n  ADD `name` varchar(255) binary NOT NULL AFTER `id`");
            $this->out('... adding jobs.project_id');
            $db->query("ALTER TABLE jobs\n  ADD `project_id` varchar(255) binary NOT NULL AFTER `name`");
            $this->out('... dropping constraint fk_clients_user_id');
            $db->query("ALTER TABLE clients\n  DROP FOREIGN KEY fk_clients_user_id");
            $this->out('... dropping constraint fk_jobs_user_id');
            $db->query("ALTER TABLE jobs\n  DROP FOREIGN KEY fk_jobs_user_id");
            $this->out('... dropping constraint fk_runs_job_id');
            $db->query("ALTER TABLE runs\n  DROP FOREIGN KEY fk_runs_job_id");
            $this->out('... dropping constraint fk_run_useragent_run_id');
            $db->query("ALTER TABLE run_useragent\n  DROP FOREIGN KEY fk_run_useragent_run_id");
            $this->out('... dropping constraint fk_runresults_client_id');
            $db->query("ALTER TABLE runresults\n  DROP FOREIGN KEY fk_runresults_client_id");
            $this->out('... dropping index idx_users_name');
            $db->query("ALTER TABLE users\n  DROP INDEX idx_users_name");
            $this->out('... dropping index idx_clients_user_useragent_updated');
            $db->query("ALTER TABLE clients\n  DROP INDEX idx_clients_user_useragent_updated");
            $this->out('... dropping index idx_jobs_user');
            $db->query("ALTER TABLE jobs\n  DROP INDEX idx_jobs_user");
            $this->out('Migrating old content into new schema...');
            $this->out('... fetching users table');
            $userRows = $db->getRows('SELECT * FROM users') ?: array();
            $this->out('... found ' . count($userRows) . ' users');
            foreach ($userRows as $userRow) {
                $this->out('... creating project "' . $userRow->name . '"');
                if (!trim($userRow->seed) || !trim($userRow->password) || !trim($userRow->auth)) {
                    // Client.php used to create rows in the users table with blanks
                    // in these "required" fields. MySQL expands the emptyness to the full
                    // 40-width of the column. Hence the trim().
                    $this->out('    SKIPPED: Not a project account but a swarm client.');
                    continue;
                }
                // Validate project id
                if (!LoginAction::isValidName($userRow->name)) {
                    $this->out('    SKIPPED: User name not a valid project id. Must match: ' . LoginAction::getNameValidationRegex());
                    continue;
                }
                if (!$db->getOne(str_queryf('SELECT 1 FROM jobs WHERE user_id=%u', $userRow->id))) {
                    $this->out('    SKIPPED: Account has 0 jobs');
                    continue;
                }
                $isInserted = $db->query(str_queryf('INSERT INTO projects
					(id, display_title, site_url, password, auth_token, updated, created)
					VALUES(%s, %s, %s, %s, %s, %s, %s);', $userRow->name, $userRow->name, '', LoginAction::generatePasswordHashForUserrow($userRow), sha1($userRow->auth), swarmdb_dateformat(SWARM_NOW), $userRow->created));
                if (!$isInserted) {
                    $this->out('    FAILED: Failed to insert row into projects table.');
                    continue;
                }
                $this->out('... updating references for project "' . $userRow->name . '"');
                $isUpdated = $db->query(str_queryf('UPDATE clients
					SET name=%s
					WHERE user_id=%u', $userRow->name, $userRow->id));
                if (!$isUpdated) {
                    $this->out('    FAILED: Failed to update rows in clients table.');
                    continue;
                }
                $isUpdated = $db->query(str_queryf('UPDATE jobs
					SET project_id=%s
					WHERE user_id=%u', $userRow->name, $userRow->id));
                if (!$isUpdated) {
                    $this->out('    FAILED: Failed to update rows in jobs table.');
                    continue;
                }
            }
            $this->out('Schema changes after users-projects-conversion migration...');
            $this->out('... changing clients.useragent_id');
            $db->query("ALTER TABLE clients\n  CHANGE COLUMN `useragent_id` `useragent_id` varchar(255) NOT NULL");
            $this->out('... dropping clients.user_id');
            $db->query("ALTER TABLE clients\n  DROP COLUMN `user_id`");
            $this->out('... dropping jobs.user_id');
            $db->query("ALTER TABLE jobs\n  DROP COLUMN `user_id`");
            $this->out('... dropping users table');
            $db->query("DROP TABLE users");
            $this->out('... adding index idx_clients_name_ua_created');
            $db->query("ALTER TABLE clients\n  ADD INDEX idx_clients_name_ua_created (name, useragent_id, created);");
            $this->out('... adding index idx_jobs_project_created');
            $db->query("ALTER TABLE jobs\n  ADD INDEX idx_jobs_project_created (project_id, created);");
        }
        // End of patch-users-projects-conversion.sql
        $this->getContext()->dbLock(false);
        $this->out("Removed database.lock.\nNo more updates.");
    }
예제 #29
0
    /**
     * @actionMethod POST: Required.
     * @actionParam int client_id
     * @actionParam string run_token
     * @actionParam int run_id
     * @actionParam string results_id
     * @actionParam string results_store_token
     * @actionParam int total
     * @actionParam int fail
     * @actionParam int error
     * @actionParam int status: `runresults.status`
     * @actionParam string report_html: HTML snapshot of the test results page.
     */
    public function doAction()
    {
        $browserInfo = $this->getContext()->getBrowserInfo();
        $conf = $this->getContext()->getConf();
        $db = $this->getContext()->getDB();
        $request = $this->getContext()->getRequest();
        if (!$request->wasPosted()) {
            $this->setError('requires-post');
            return;
        }
        $runToken = $request->getVal('run_token');
        if ($conf->client->requireRunToken && !$runToken) {
            $this->setError('missing-parameters', 'This TestSwarm does not allow unauthorized clients to join the swarm.');
            return;
        }
        $runID = $request->getInt('run_id');
        $clientID = $request->getInt('client_id');
        $resultsID = $request->getVal('results_id');
        $resultsStoreToken = $request->getVal('results_store_token');
        if (!$runID || !$clientID || !$resultsID || !$resultsStoreToken) {
            $this->setError('missing-parameters');
            return;
        }
        // Create a Client object that verifies client id, user agent and run token.
        // Also updates the client 'alive' timestamp.
        // Throws exception (caught higher up) if stuff is invalid.
        $client = Client::newFromContext($this->getContext(), $runToken, $clientID);
        $total = $request->getInt('total', 0);
        $fail = $request->getInt('fail', 0);
        $error = $request->getInt('error', 0);
        $status = $request->getInt('status', 2);
        $reportHtml = $request->getVal('report_html', '');
        if (!in_array($status, array(2, 3))) {
            $this->setError('invalid-input', 'Illegal status to be set from the client side in action=saverun.');
            return;
        }
        // Verify this runresults row exists,
        // also naturally validates run_id and store_token
        $res = $db->query(str_queryf('SELECT
				id
			FROM runresults
			WHERE id = %u
			AND   run_id = %u
			AND   store_token = %s;', $resultsID, $runID, sha1($resultsStoreToken)));
        if (!$res || $db->getNumRows($res) !== 1) {
            $this->setError('invalid-input');
            return;
        }
        $db->query(str_queryf('UPDATE
				runresults
			SET
				status = %u,
				total = %u,
				fail = %u,
				error = %u,
				report_html = %s,
				updated = %s
			WHERE id = %u
			LIMIT 1;', $status, $total, $fail, $error, gzencode($reportHtml), swarmdb_dateformat(SWARM_NOW), $resultsID));
        if ($db->getAffectedRows() !== 1) {
            $this->setError('internal-error', 'Updating of results table failed.');
            return;
        }
        $isPassed = $total > 0 && $fail === 0 && $error === 0;
        // Use results_id in the WHERE clause as additional check, just in case
        // this runresults row is no longer the primary linked one.
        // This fixes a race condition where (for some reason) 2 clients run the
        // same run, and the "good" one started last and finishes first. When the
        // "bad" client finishes and updates this run as not passing, it would
        // mismatch the results the user would find in the linked report from runresults.
        // Be sure to use it only as "WHERE" not in "SET", as that  could cause
        // an equally bad effect (unlink a good run).
        if ($isPassed) {
            $db->query(str_queryf('UPDATE
					run_useragent
				SET
					completed = completed + 1,
					status = 2,
					updated = %s
				WHERE run_id = %u
				AND   useragent_id = %s
				AND   results_id = %u
				LIMIT 1;', swarmdb_dateformat(SWARM_NOW), $runID, $browserInfo->getSwarmUaID(), $resultsID));
        } else {
            // If we don't pass and we haven't reached max yet,
            // set status back to 0 so that this run may be
            // distributed again (see also GetrunAction),
            // If we don't pass and did reach the max, set
            // status=2.
            $db->query(str_queryf('UPDATE
					run_useragent
				SET
					completed = completed + 1,
					status = IF(completed + 1 < max, 0, 2),
					updated = %s
				WHERE run_id = %u
				AND   useragent_id = %s
				AND   results_id = %u
				LIMIT 1;', swarmdb_dateformat(SWARM_NOW), $runID, $browserInfo->getSwarmUaID(), $resultsID));
        }
        $this->setData('ok');
    }
예제 #30
0
 /**
  * @param string $sortField
  * @param string $sortDir
  * @param string $include
  * @param string|bool $name
  */
 protected function getOverview($sortField, $sortDir, $include, $name)
 {
     $context = $this->getContext();
     $db = $context->getDB();
     $sortDirQuery = strtoupper($sortDir);
     $sortFieldQuery = "ORDER BY {$sortField} {$sortDirQuery}";
     $whereClause = array();
     if ($include === 'active') {
         $whereClause[] = 'updated >= ' . swarmdb_dateformat(Client::getMaxAge($context));
     }
     if ($name) {
         $whereClause[] = 'name = \'' . $db->strEncode($name) . '\'';
     }
     if (count($whereClause)) {
         $whereClause = 'WHERE ' . implode(' AND ', $whereClause);
     } else {
         $whereClause = '';
     }
     $rows = $db->getRows("SELECT\n\t\t\t\tname,\n\t\t\t\tMAX(updated) as updated\n\t\t\tFROM\n\t\t\t\tclients\n\t\t\t{$whereClause}\n\t\t\tGROUP BY name\n\t\t\t{$sortFieldQuery};");
     $results = array();
     if ($rows) {
         foreach ($rows as $row) {
             $result = array('name' => $row->name, 'viewUrl' => swarmpath("clients/{$row->name}"), 'clientIDs' => array());
             $this->addTimestampsTo($result, $row->updated, 'updated');
             $results[$row->name] = $result;
         }
     }
     return $results;
 }