Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ clients/clients
.DS_Store
clients/configs
custom
tools/polygon_importer/polygon_importer
tools/polygon_importer/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function RenderCompilationData(compilationData, submission, changeCompila
action: "show",
})
}

return (
<>
<h5 className="mb-3">Compilation result</h5>
Expand Down
4 changes: 2 additions & 2 deletions clients/frontend/admin/pages/NewSubmission.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import axios from "axios";
import {useNavigate, useSearchParams} from "react-router-dom";

export default function NewSubmission() {
const [params, setParams] = useSearchParams()
const [params, _] = useSearchParams()

const [alert, setAlert] = useState({
hasAlert: false,
Expand Down Expand Up @@ -56,7 +56,7 @@ export default function NewSubmission() {
name="problem_id"
id="problem_id"
required={true}
defaultValue={params.problem_id || ""}
defaultValue={params.get("problem_id") || ""}
/>
</div>
</div>
Expand Down
5 changes: 4 additions & 1 deletion clients/frontend/admin/pages/Problem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ export default function Problems() {
<div className="mb-3">
<Link to={`/admin/submissions?problem_id=${problem.id}`}>Submissions</Link>
</div>
<div className="mb-3">
<Link to={`/admin/new/submission?problem_id=${problem.id}`}>Send submission</Link>
</div>
</div>
<hr className="mt-4 mb-4"/>
<div className="px-4 px-sm-5 mx-2 pb-5">
{content}
{content}
</div>
</div>
)
Expand Down
2 changes: 1 addition & 1 deletion clients/frontend/admin/pages/Status.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default function Status() {
</tr>
</thead>
<tbody>
{invoker.active_jobs.map((job, jobIndex) => (
{invoker.testing_jobs.map((job, jobIndex) => (
<tr key={`${index}-${jobIndex}`}>
<td>{getJobType(job.type)}</td>
<td>{job.submit_id}</td>
Expand Down
110 changes: 56 additions & 54 deletions clients/frontend/admin/pages/Submission.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,71 +89,73 @@ export default function Submission() {
</tr>
</thead>
<tbody>
<tr key={submission.id}>
<th scope="row">{submission.id}</th>
<td><Link to={`/admin/problem/${submission.problem_id}`}>{submission.problem_id}</Link></td>
<td>{submission.language}</td>
<td>{submission.score}</td>
<td>{SubmissionVerdict(submission)}</td>
</tr>
<tr key={submission.id}>
<th scope="row">{submission.id}</th>
<td><Link to={`/admin/problem/${submission.problem_id}`}>{submission.problem_id}</Link></td>
<td>{submission.language}</td>
<td>{submission.score}</td>
<td>{SubmissionVerdict(submission)}</td>
</tr>
</tbody>
</table>
<p>Время начала тестирования: {submission.created_at}</p>
<p>Время окончания тестирования: {submission.updated_at}</p>
{RenderCompilationData(compilationData, submission, changeCompilationData)}
{submission.group_results ? (
<>
<h5 className="mb-3">Group results</h5>
<div className="row">
<div className="col-12 col-md-10 col-lg-8">
<table className="table mb-3">
<thead>
<tr>
<th scope="row">Name</th>
<th scope="row">Score</th>
<th scope="row">Passed</th>
</tr>
</thead>
<tbody>
{submission.group_results.map((group, index) => (
<tr key={index}>
<td>{group.group_name}</td>
<td>{group.points}</td>
<td>{group.passed ? (
<span className="text-success">Yes</span>
) : (
<span className="text-danger">No</span>
)}</td>
<h5 className="mb-3">Group results</h5>
<div className="row">
<div className="col-12 col-md-10 col-lg-8">
<table className="table mb-3">
<thead>
<tr>
<th scope="row">Name</th>
<th scope="row">Score</th>
<th scope="row">Passed</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{submission.group_results.map((group, index) => (
<tr key={index}>
<td>{group.group_name}</td>
<td>{group.points}</td>
<td>{group.passed ? (
<span className="text-success">Yes</span>
) : (
<span className="text-danger">No</span>
)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</>
) : null}
{submission.test_results ? (
<>
<h5 className="mb-3">Test results</h5>
<table className="table mb-3">
<thead>
<tr>
<th scope="row">#</th>
<th scope="row">Verdict</th>
<th scope="row">Points</th>
<th scope="row">Time</th>
<th scope="row">Memory</th>
<th scope="row">Wall time</th>
<th scope="row">Exit code</th>
<th scope="row">More info</th>
</tr>
</thead>
<tbody>
{
submission.test_results.map(testResult => (
RenderTest(tests[testResult.test_number - 1], testResult, changeTests)
))
}
</tbody>
</table>
<h5 className="mb-3">Test results</h5>
<table className="table mb-3">
<thead>
<tr>
<th scope="row">#</th>
<th scope="row">Verdict</th>
<th scope="row">Points</th>
<th scope="row">Time</th>
<th scope="row">Memory</th>
<th scope="row">Wall time</th>
<th scope="row">Exit code</th>
<th scope="row">More info</th>
</tr>
</thead>
<tbody>
{
submission.test_results.map(testResult => (
RenderTest(tests[testResult.test_number - 1], testResult, changeTests)
))
}
</tbody>
</table>
</>
) : null}
</div>
Expand Down
16 changes: 8 additions & 8 deletions clients/resources/static/admin/admin.js

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions common/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ type Config struct {
Invoker *InvokerConfig `yaml:"Invoker,omitempty"`
Master *MasterConfig `yaml:"Master,omitempty"`
Storage *StorageConfig `yaml:"Storage,omitempty"`
// TODO: Add instances here

DB DBConfig `yaml:"DB"`
// if instance is set up on server, leave connection empty
Expand Down Expand Up @@ -48,7 +47,13 @@ func fillInConfig(config *Config) {
}

fillInConnections(config)
fillInMasterConfig(config.Master)
fillInStorageConfig(config.Storage)
FillInInvokerConfig(config.Invoker)
if config.Master != nil {
fillInMasterConfig(config.Master)
}
if config.Storage != nil {
fillInStorageConfig(config.Storage)
}
if config.Invoker != nil {
FillInInvokerConfig(config.Invoker)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

можно не экспортировать видимо

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не, оно в тестах инвокера нужно

}
}
3 changes: 2 additions & 1 deletion common/config/invoker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import "time"

type InvokerConfig struct {
// PublicAddress defines address for public access to invoker from master if the server is set up locally with some proxy
PublicAddress *string `yaml:"PublicAddress,omitempty"`
PublicAddress *string `yaml:"PublicAddress,omitempty"`
AutodetectPublicAddress *bool `yaml:"AutodetectPublicAddress,omitempty"`

Threads int `yaml:"Threads"`
Sandboxes int `yaml:"Sandboxes"`
Expand Down
6 changes: 6 additions & 0 deletions common/connectors/invokerconn/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ func (c *Connector) ResetCache() error {
r := c.connection.R()
return connector.ReceiveEmpty(r, "/invoker/reset_cache", resty.MethodPost)
}

func (c *Connector) StopJob(jobID string) error {
r := c.connection.R()
r.SetBody(jobID)
return connector.ReceiveEmpty(r, "/invoker/job/stop", resty.MethodPost)
}
2 changes: 1 addition & 1 deletion common/connectors/invokerconn/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Job struct {
Type JobType `json:"type" binding:"required"`
Test uint64 `json:"test"`

// TODO: Add job dependency
RequiredJobIDs []string
}

func (j Job) String() string {
Expand Down
1 change: 1 addition & 0 deletions common/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func NewDB(config config.DBConfig) (*gorm.DB, error) {
if err = db.AutoMigrate(&models.Submission{}); err != nil {
return nil, logger.Error("Can't migrate Submission: %v", err)
}
logger.Info("Configured DB successfully")
return db, err
}

Expand Down
16 changes: 15 additions & 1 deletion common/db/models/problem.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"database/sql/driver"
"encoding/json"
"errors"
"github.com/xorcare/pointer"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"testing_system/lib/customfields"
Expand Down Expand Up @@ -58,7 +59,20 @@ type TestGroup struct {
RequiredGroupNames []string `json:"required_group_names" yaml:"required_group_names"`
}

type TestGroups []TestGroup
func (g *TestGroup) Copy() *TestGroup {
group := *g
if g.TestScore != nil {
group.TestScore = pointer.Float64(*g.TestScore)
}
if g.GroupScore != nil {
group.GroupScore = pointer.Float64(*g.GroupScore)
}
group.RequiredGroupNames = make([]string, len(g.RequiredGroupNames))
copy(group.RequiredGroupNames, g.RequiredGroupNames)
return &group
}

type TestGroups []*TestGroup

func (t TestGroups) Value() (driver.Value, error) {
return json.Marshal(t)
Expand Down
10 changes: 10 additions & 0 deletions common/metrics/invoker.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strconv"
"testing_system/common/connectors/invokerconn"
"testing_system/common/connectors/masterconn"
"testing_system/common/constants/verdict"
)

func (c *Collector) setupInvokerMetrics() {
Expand Down Expand Up @@ -48,6 +49,11 @@ func (c *Collector) setupInvokerMetrics() {
"Total time spent on sending results from invoker to storage",
)

c.InvokerSkippedJobs = c.createInvokerCounter(
"skipped_jobs_count",
"Total number of jobs that are aborted during execution and marked skipped",
)

c.InvokerLifetimeDuration = c.createInvokerGauge(
"lifetime_duration",
"Total time the invoker is running",
Expand Down Expand Up @@ -128,6 +134,10 @@ func (c *Collector) ProcessJobResult(result *masterconn.InvokerJobResult) {
c.InvokerExecutionWaitDuration.With(labels).Add(result.Metrics.ExecutionWaitDuration.Seconds())
c.InvokerExecutionDuration.With(labels).Add(result.Metrics.ExecutionDuration.Seconds())
c.InvokerSendResultDuration.With(labels).Add(result.Metrics.SendResultDuration.Seconds())

if result.Verdict == verdict.SK {
c.InvokerSkippedJobs.With(labels).Inc()
}
}

func (c *Collector) ProcessInvokerStatus(status *invokerconn.Status) {
Expand Down
1 change: 1 addition & 0 deletions common/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Collector struct {
InvokerExecutionWaitDuration *prometheus.CounterVec
InvokerExecutionDuration *prometheus.CounterVec
InvokerSendResultDuration *prometheus.CounterVec
InvokerSkippedJobs *prometheus.CounterVec

InvokerLifetimeDuration *prometheus.GaugeVec
InvokerSandboxCount *prometheus.GaugeVec
Expand Down
8 changes: 7 additions & 1 deletion invoker/check_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func (s *JobPipelineState) generateCheckerRunConfig() error {
s.test.checkConfig.Args = []string{
testInputFile, testOutputFile, testAnswerFile, checkResultFile, checkResultFileArg,
}
s.test.checkConfig.Ctx = s.job.stopCtx
logger.Trace("Generated checker run config for %s", s.loggerData)
return nil
}
Expand All @@ -69,7 +70,7 @@ func (s *JobPipelineState) executeCheckerRunCommand() error {
}

switch s.test.checkResult.Verdict {
case verdict.OK, verdict.RT:
case verdict.OK, verdict.RT, verdict.SK:
logger.Trace("Finished checker run for %s", s.loggerData)
return nil
case verdict.TL:
Expand All @@ -91,6 +92,11 @@ func (s *JobPipelineState) runChecker() {
}

func (s *JobPipelineState) parseCheckerResult() error {
if s.test.checkResult.Verdict == verdict.SK {
s.test.runResult.Verdict = verdict.SK
logger.Trace("Check result is not parsed because job is stopped", s.loggerData)
return nil
}
_, err := os.Stat(filepath.Join(s.sandbox.Dir(), checkResultFile))
if err == nil {
return s.parseTestlibCheckerResult()
Expand Down
19 changes: 19 additions & 0 deletions invoker/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,22 @@ func (i *Invoker) resetCache(c *gin.Context) {
i.Storage.Reset()
connector.RespOK(c, nil)
}

func (i *Invoker) stopJob(c *gin.Context) {
jobIDBytes, err := c.GetRawData()
if err != nil {
connector.RespErr(c, http.StatusBadRequest, "Can not get job id, error: %s", err.Error())
}
jobID := string(jobIDBytes)

i.Mutex.Lock()

job, ok := i.ActiveJobs[jobID]
if ok {
if job.Type == invokerconn.TestJob {
job.stopFunc()
}
}
i.Mutex.Unlock()
connector.RespOK(c, nil)
}
Loading