Skip to content

Commit

Permalink
add team instance list endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
argonaut0 committed Sep 10, 2023
1 parent e2158c1 commit 67eaa46
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
# ubcctf/instanced

currently Jank As Hell.

Manages challenge instances on-demand.

`instanced` runs in the cluster and exposes an HTTP API which is used to request instances.
Challenge templates are added in the form of CRDs. Example format is in this repository.
`instanced` must be restarted every time new CRDs are applied.

Instances created are kept track of in a local sqlite database. The instancer periodically scans the database for expired instances and deletes them.


- GET `/instances` - get list of active instances
- GET `/challenges?team=$ID` - get list of available challenges and instance states for specific team
- POST `/instances?chal=$CHALLNAME&team=$ID` - provision an instance for specific challenge and team
- DELETE `/instances?id=$ID` - delete challenge with id

Authenticate with `Bearer token`
30 changes: 30 additions & 0 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,33 @@ func (in *Instancer) ReadInstanceRecords() ([]InstanceRecord, error) {
err = rows.Err()
return records, err
}

func (in *Instancer) ReadInstanceRecordsTeam(teamID string) ([]InstanceRecord, error) {
if in.db == nil {
return nil, errors.New("db not initialized")
}
stmt, err := in.db.Prepare("SELECT id, challenge, team, expiry FROM instances WHERE team = ?")
if err != nil {
return nil, err
}
defer stmt.Close()

rows, err := stmt.Query(teamID)
if err != nil {
return nil, err
}
defer rows.Close()
records := make([]InstanceRecord, 0)
for rows.Next() {
record := InstanceRecord{}
var t int64
err = rows.Scan(&record.Id, &record.Challenge, &record.TeamID, &t)
if err != nil {
return records, err
}
record.Expiry = time.Unix(t, 0)
records = append(records, record)
}
err = rows.Err()
return records, err
}
20 changes: 20 additions & 0 deletions instancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,26 @@ func (in *Instancer) CreateInstance(challenge, team string) (InstanceRecord, err
return rec, nil
}

func (in *Instancer) GetTeamChallengeStates(teamID string) ([]InstanceRecord, error) {
instances, err := in.ReadInstanceRecordsTeam(teamID)
if err != nil {
return nil, err
}
for k := range in.challengeObjs {
active := false
for _, v := range instances {
if v.Challenge == k {
active = true
break
}
}
if !active {
instances = append(instances, InstanceRecord{Expiry: time.Unix(0, 0), Challenge: k, TeamID: teamID})
}
}
return instances, nil
}

func (in *Instancer) Start() error {
log := in.log.With().Str("component", "instanced").Logger()
log.Info().Msg("starting webserver...")
Expand Down
13 changes: 13 additions & 0 deletions webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ func (in *Instancer) registerEndpoints() {
in.echo.POST("/instances", in.handleInstanceCreate)

in.echo.DELETE("/instances", in.handleInstanceDelete)

in.echo.GET("/challenges", in.handleInstanceListTeam)
}

type InstancesResponse struct {
Expand Down Expand Up @@ -82,3 +84,14 @@ func (in *Instancer) handleInstanceList(c echo.Context) error {
// todo: properly marshal records
return c.JSON(http.StatusOK, records)
}

func (in *Instancer) handleInstanceListTeam(c echo.Context) error {
teamID := c.QueryParam("team")
records, err := in.GetTeamChallengeStates(teamID)
if err != nil {
c.Logger().Errorf("request failed: %v", err)
return c.JSON(http.StatusInternalServerError, "request failed")
}
// todo: properly marshal records
return c.JSON(http.StatusOK, records)
}

0 comments on commit 67eaa46

Please sign in to comment.