moth/src/instance.go

135 lines
3.2 KiB
Go

package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"strings"
"time"
)
type Instance struct {
Base string
MothballDir string
StateDir string
ResourcesDir string
Categories map[string]*Mothball
}
func NewInstance(base, mothballDir, stateDir, resourcesDir string) (*Instance, error) {
ctx := &Instance{
Base: strings.TrimRight(base, "/"),
MothballDir: mothballDir,
StateDir: stateDir,
ResourcesDir: resourcesDir,
Categories: map[string]*Mothball{},
}
// Roll over and die if directories aren't even set up
if _, err := os.Stat(mothballDir); err != nil {
return nil, err
}
if _, err := os.Stat(stateDir); err != nil {
return nil, err
}
ctx.MaybeInitialize()
return ctx, nil
}
func (ctx *Instance) MaybeInitialize() {
// Only do this if it hasn't already been done
if _, err := os.Stat(ctx.StatePath("initialized")); err == nil {
return
}
log.Print("initialized file missing, re-initializing")
// Remove any extant control and state files
os.Remove(ctx.StatePath("until"))
os.Remove(ctx.StatePath("disabled"))
os.Remove(ctx.StatePath("points.log"))
os.RemoveAll(ctx.StatePath("points.tmp"))
os.RemoveAll(ctx.StatePath("points.new"))
os.RemoveAll(ctx.StatePath("teams"))
// Make sure various subdirectories exist
os.Mkdir(ctx.StatePath("points.tmp"), 0755)
os.Mkdir(ctx.StatePath("points.new"), 0755)
os.Mkdir(ctx.StatePath("teams"), 0755)
// Preseed available team ids if file doesn't exist
if f, err := os.OpenFile(ctx.StatePath("teamids.txt"), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644); err == nil {
defer f.Close()
for i := 0; i <= 9999; i += 1 {
fmt.Fprintf(f, "%04d\n", i)
}
}
// Create initialized file that signals whether we're set up
f, err := os.OpenFile(ctx.StatePath("initialized"), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
if err != nil {
log.Print(err)
}
defer f.Close()
fmt.Fprintln(f, "Remove this file to reinitialize the contest")
}
func (ctx Instance) MothballPath(parts ...string) string {
tail := path.Join(parts...)
return path.Join(ctx.MothballDir, tail)
}
func (ctx *Instance) StatePath(parts ...string) string {
tail := path.Join(parts...)
return path.Join(ctx.StateDir, tail)
}
func (ctx *Instance) PointsLog() []Award {
var ret []Award
fn := ctx.StatePath("points.log")
f, err := os.Open(fn)
if err != nil {
log.Printf("Unable to open %s: %s", fn, err)
return ret
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
cur, err := ParseAward(line)
if err != nil {
log.Printf("Skipping malformed award line %s: %s", line, err)
continue
}
ret = append(ret, *cur)
}
return ret
}
// awardPoints gives points points to team teamid in category category
func (ctx *Instance) AwardPoints(teamid string, category string, points int) error {
fn := fmt.Sprintf("%s-%s-%d", teamid, category, points)
tmpfn := ctx.StatePath("points.tmp", fn)
newfn := ctx.StatePath("points.new", fn)
contents := fmt.Sprintf("%d %s %s %d\n", time.Now().Unix(), teamid, category, points)
if err := ioutil.WriteFile(tmpfn, []byte(contents), 0644); err != nil {
return err
}
if err := os.Rename(tmpfn, newfn); err != nil {
return err
}
log.Printf("Award %s %s %d", teamid, category, points)
return nil
}