Split backend service into checker and generator

Both are linked through a unix socket.
This commit is contained in:
nemunaire 2023-07-10 09:17:02 +02:00
parent f755d7c998
commit ed091e761c
34 changed files with 657 additions and 205 deletions

View File

@ -1,7 +1,8 @@
admin/admin
backend/backend
checker/checker
dashboard/dashboard
evdist/evdist
generator/generator
receiver/receiver
repochecker/repochecker
frontend/fic/build

View File

@ -1,4 +1,4 @@
image: nemunaire/fic-backend:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
@ -6,16 +6,16 @@ tags:
{{/each}}
{{/if}}
manifests:
- image: nemunaire/fic-backend:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
- image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
platform:
architecture: amd64
os: linux
- image: nemunaire/fic-backend:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
- image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
platform:
architecture: arm64
os: linux
variant: v8
- image: nemunaire/fic-backend:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
- image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
platform:
architecture: arm
os: linux

View File

@ -0,0 +1,22 @@
image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
- image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
platform:
architecture: amd64
os: linux
- image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
platform:
architecture: arm64
os: linux
variant: v8
- image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
platform:
architecture: arm
os: linux
variant: v7

View File

@ -17,8 +17,9 @@ steps:
commands:
- apk --no-cache add git
- go get -v -d srs.epita.fr/fic-server/admin
- go get -v -d srs.epita.fr/fic-server/backend
- go get -v -d srs.epita.fr/fic-server/checker
- go get -v -d srs.epita.fr/fic-server/evdist
- go get -v -d srs.epita.fr/fic-server/generator
- go get -v -d srs.epita.fr/fic-server/receiver
- go get -v -d srs.epita.fr/fic-server/dashboard
- go get -v -d srs.epita.fr/fic-server/repochecker
@ -48,8 +49,9 @@ steps:
- go vet -v -buildvcs=false srs.epita.fr/fic-server/admin/sync
- go vet -v -buildvcs=false srs.epita.fr/fic-server/admin/pki
- go vet -v -buildvcs=false srs.epita.fr/fic-server/admin
- go vet -v -buildvcs=false srs.epita.fr/fic-server/backend
- go vet -v -buildvcs=false srs.epita.fr/fic-server/checker
- go vet -v -buildvcs=false srs.epita.fr/fic-server/evdist
- go vet -v -buildvcs=false srs.epita.fr/fic-server/generator
- go vet -v -buildvcs=false srs.epita.fr/fic-server/receiver
- go vet -v -buildvcs=false srs.epita.fr/fic-server/dashboard
- go vet -v -buildvcs=false srs.epita.fr/fic-server/repochecker
@ -70,10 +72,10 @@ steps:
environment:
CGO_ENABLED: 0
- name: build backend
- name: build checker
image: golang:alpine
commands:
- go build -v -buildvcs=false -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend
- go build -v -buildvcs=false -o deploy/checker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/checker
environment:
CGO_ENABLED: 0
@ -84,6 +86,13 @@ steps:
environment:
CGO_ENABLED: 0
- name: build generator
image: golang:alpine
commands:
- go build -v -buildvcs=false -o deploy/generator-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/generator
environment:
CGO_ENABLED: 0
- name: build receiver
image: golang:alpine
commands:
@ -176,17 +185,17 @@ steps:
branch:
- master
- name: docker backend
- name: docker checker
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: nemunaire/fic-backend
repo: nemunaire/fic-checker
auto_tag: true
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
dockerfile: Dockerfile-backend
dockerfile: Dockerfile-checker
when:
branch:
- master
@ -206,6 +215,20 @@ steps:
branch:
- master
- name: docker generator
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: nemunaire/fic-generator
auto_tag: true
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
dockerfile: Dockerfile-generator
when:
branch:
- master
- name: docker receiver
image: plugins/docker
settings:
@ -351,8 +374,9 @@ steps:
commands:
- apk --no-cache add git
- go get -v -d srs.epita.fr/fic-server/admin
- go get -v -d srs.epita.fr/fic-server/backend
- go get -v -d srs.epita.fr/fic-server/checker
- go get -v -d srs.epita.fr/fic-server/evdist
- go get -v -d srs.epita.fr/fic-server/generator
- go get -v -d srs.epita.fr/fic-server/receiver
- go get -v -d srs.epita.fr/fic-server/dashboard
@ -364,10 +388,10 @@ steps:
environment:
CGO_ENABLED: 0
- name: build backend
- name: build checker
image: golang:alpine
commands:
- go build -v -buildvcs=false -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend
- go build -v -buildvcs=false -o deploy/checker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/checker
environment:
CGO_ENABLED: 0
@ -378,6 +402,13 @@ steps:
environment:
CGO_ENABLED: 0
- name: build generator
image: golang:alpine
commands:
- go build -v -buildvcs=false -o deploy/generator-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/generator
environment:
CGO_ENABLED: 0
- name: build receiver
image: golang:alpine
commands:
@ -472,17 +503,17 @@ steps:
branch:
- master
- name: docker backend
- name: docker checker
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: nemunaire/fic-backend
repo: nemunaire/fic-checker
auto_tag: true
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
dockerfile: Dockerfile-backend
dockerfile: Dockerfile-checker
when:
branch:
- master
@ -502,6 +533,21 @@ steps:
branch:
- master
- name: docker generator
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: nemunaire/fic-generator
auto_tag: true
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
dockerfile: Dockerfile-generator
when:
branch:
- master
- name: docker receiver
image: plugins/docker
settings:
@ -598,12 +644,12 @@ steps:
password:
from_secret: docker_password
- name: publish backend
- name: publish checker
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
spec: .drone-manifest-fic-backend.yml
spec: .drone-manifest-fic-checker.yml
username:
from_secret: docker_username
password:
@ -620,6 +666,17 @@ steps:
password:
from_secret: docker_password
- name: publish generator
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
spec: .drone-manifest-fic-generator.yml
username:
from_secret: docker_username
password:
from_secret: docker_password
- name: publish receiver
image: plugins/manifest
settings:

View File

@ -1,22 +0,0 @@
FROM golang:1-alpine as gobuild
RUN apk add --no-cache git
WORKDIR /go/src/srs.epita.fr/fic-server/
COPY go.mod go.sum ./
COPY settings settings/
COPY libfic ./libfic/
COPY backend ./backend/
RUN go get -d -v ./backend && \
go build -v -buildvcs=false -o backend/backend ./backend
FROM alpine:3.18
WORKDIR /srv
ENTRYPOINT ["/srv/backend"]
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/backend/backend /srv/backend

22
Dockerfile-checker Normal file
View File

@ -0,0 +1,22 @@
FROM golang:1-alpine as gobuild
RUN apk add --no-cache git
WORKDIR /go/src/srs.epita.fr/fic-server/
COPY go.mod go.sum ./
COPY settings settings/
COPY libfic ./libfic/
COPY checker ./checker/
RUN go get -d -v ./checker && \
go build -v -buildvcs=false -o checker/checker ./checker
FROM alpine:3.18
WORKDIR /srv
ENTRYPOINT ["/srv/checker"]
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/checker/checker /srv/checker

22
Dockerfile-generator Normal file
View File

@ -0,0 +1,22 @@
FROM golang:1-alpine as gobuild
RUN apk add --no-cache git
WORKDIR /go/src/srs.epita.fr/fic-server/
COPY go.mod go.sum ./
COPY settings settings/
COPY libfic ./libfic/
COPY generator ./generator/
RUN go get -d -v ./generator && \
go build -v -buildvcs=false -o generator/generator ./generator
FROM alpine:3.18
WORKDIR /srv
ENTRYPOINT ["/srv/generator"]
COPY --from=gobuild /go/src/srs.epita.fr/fic-server/generator/generator /srv/generator

View File

@ -13,7 +13,7 @@ FROM nginx:stable-alpine
ENV FIC_BASEURL=/ \
HOST_RECEIVER=receiver:8080 HOST_ADMIN=admin:8081 HOST_DASHBOARD=dashboard:8082 HOST_QA=qa:8083 \
PATH_FILES=/srv/FILES PATH_STARTINGBLOCK=/srv/STARTINGBLOCK PATH_STATIC=/srv/htdocs-frontend PATH_SETTINGS=/srv/SETTINGSDIST PATH_SYNC=/srv/SYNC PATH_TEAMS=/srv/TEAMS
PATH_FILES=/srv/FILES PATH_STARTINGBLOCK=/srv/STARTINGBLOCK PATH_STATIC=/srv/htdocs-frontend PATH_SETTINGS=/srv/SETTINGSDIST PATH_TEAMS=/srv/TEAMS
EXPOSE 80

View File

@ -11,13 +11,14 @@ micro-services :
- `admin` is the web interface and API used to control the challenge
and doing synchronization.
- `backend` is an inotify reacting service that handles submissions
checking and team's files generation.
- `checker` is an inotify reacting service that handles submissions
checking.
- `dashboard` is a public interface to explain and follow the
conquest, aims to animate the challenge for visitors.
- `evdist` is an inotify reacting service that handles settings
changes during the challenge (eg. a 30 minutes event where hints are
free, ...).
- `generator` takes care of global and team's files generation.
- `qa` is an interface dedicated to challenge development, it stores
reports to be treated by challenges creators.
- `receiver` is only responsible for receiving submissions. It is the

View File

@ -1,13 +1,16 @@
package api
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"path"
"strconv"
"strings"
"time"
"srs.epita.fr/fic-server/libfic"
@ -15,6 +18,8 @@ import (
"github.com/gin-gonic/gin"
)
var GeneratorSocket string
func declareClaimsRoutes(router *gin.RouterGroup) {
// Tasks
router.GET("/claims", getClaims)
@ -287,12 +292,44 @@ func clearClaims(c *gin.Context) {
}
func generateTeamIssuesFile(team fic.Team) error {
if my, err := team.MyIssueFile(); err != nil {
return fmt.Errorf("Unable to generate issue FILE (tid=%d): %w", team.Id, err)
} else if j, err := json.Marshal(my); err != nil {
return fmt.Errorf("Unable to encode issues' file JSON: %w", err)
} else if err = ioutil.WriteFile(path.Join(TeamsDir, fmt.Sprintf("%d", team.Id), "issues.json"), j, 0644); err != nil {
return fmt.Errorf("Unable to write issues' file: %w", err)
if GeneratorSocket == "" {
if my, err := team.MyIssueFile(); err != nil {
return fmt.Errorf("Unable to generate issue FILE (tid=%d): %w", team.Id, err)
} else if j, err := json.Marshal(my); err != nil {
return fmt.Errorf("Unable to encode issues' file JSON: %w", err)
} else if err = ioutil.WriteFile(path.Join(TeamsDir, fmt.Sprintf("%d", team.Id), "issues.json"), j, 0644); err != nil {
return fmt.Errorf("Unable to write issues' file: %w", err)
}
} else {
buf, err := json.Marshal(fic.GenStruct{Type: fic.GenTeamIssues, TeamId: team.Id})
if err != nil {
return fmt.Errorf("Something is wrong with JSON encoder: %w", err)
}
sockType := "unix"
if strings.Contains(GeneratorSocket, ":") {
sockType = "tcp"
}
socket, err := net.Dial(sockType, GeneratorSocket)
if err != nil {
return err
}
defer socket.Close()
httpClient := &http.Client{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return socket, nil
},
},
}
resp, err := httpClient.Post("http://localhost/enqueue", "application/json", bytes.NewReader(buf))
if err != nil {
return fmt.Errorf("Unable to enqueue new generation event: %w", err)
}
resp.Body.Close()
}
return nil
}

View File

@ -108,6 +108,7 @@ func main() {
flag.StringVar(&api.DashboardDir, "dashbord", "./DASHBOARD", "Base directory where save public JSON files")
flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "Base directory where load and save settings")
flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Base directory where found challenges files, local part")
flag.StringVar(&api.GeneratorSocket, "generator", "./GENERATOR/generator.socket", "Path to the generator socket (used to trigger issues.json generations, use an empty string to generate locally)")
flag.StringVar(&localImporterDirectory, "localimport", localImporterDirectory,
"Base directory where found challenges files to import, local part")
flag.BoolVar(&localImporterSymlink, "localimportsymlink", localImporterSymlink,

1
backend/.gitignore vendored
View File

@ -1 +0,0 @@
backend

1
checker/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
checker

View File

@ -45,7 +45,7 @@ func treatWantChoices(pathname string, team *fic.Team) {
} else if err = team.DisplayChoices(flag); err != nil {
log.Printf("%s [ERR] %s\n", id, err)
} else {
genTeamQueue <- team
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
if err = os.Remove(pathname); err != nil {
log.Printf("%s [ERR] %s\n", id, err)
}

52
checker/generation.go Normal file
View File

@ -0,0 +1,52 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"strings"
"time"
"srs.epita.fr/fic-server/libfic"
)
var generatorSocket string
func appendGenQueue(gs fic.GenStruct) error {
buf, err := json.Marshal(gs)
if err != nil {
return fmt.Errorf("Something is wrong with JSON encoder: %w", err)
}
sockType := "unix"
if strings.Contains(generatorSocket, ":") {
sockType = "tcp"
}
socket, err := net.Dial(sockType, generatorSocket)
if err != nil {
log.Printf("Unable to contact generator at: %s, retring in 1 second", generatorSocket)
time.Sleep(time.Second)
return appendGenQueue(gs)
}
defer socket.Close()
httpClient := &http.Client{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return socket, nil
},
},
}
resp, err := httpClient.Post("http://localhost/enqueue", "application/json", bytes.NewReader(buf))
if err != nil {
return fmt.Errorf("Unable to enqueue new generation event: %w", err)
}
resp.Body.Close()
return nil
}

View File

@ -56,8 +56,8 @@ func treatOpeningHint(pathname string, team *fic.Team) {
log.Printf("%s [WRN] Unable to create event: %s\n", id, err)
}
genTeamQueue <- team
appendGenQueue(genStruct{Type: GenEvents})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenEvents})
if err = os.Remove(pathname); err != nil {
log.Printf("%s [ERR] %s\n", id, err)
}

View File

@ -60,7 +60,7 @@ func treatIssue(pathname string, team *fic.Team) {
if err = os.Remove(pathname); err != nil {
log.Printf("%s [ERR] %s\n", id, err)
}
genTeamIssuesFile(team)
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeamIssues, TeamId: team.Id})
}
} else {
var exercice *fic.Exercice = nil
@ -85,6 +85,6 @@ func treatIssue(pathname string, team *fic.Team) {
log.Printf("%s [ERR] %s\n", id, err)
}
}
genTeamIssuesFile(team)
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeamIssues, TeamId: team.Id})
}
}

View File

@ -75,53 +75,29 @@ func reloadSettings(config *settings.Settings) {
fic.WChoiceCoefficient = config.WChoiceCurCoefficient
fic.ExerciceCurrentCoefficient = config.ExerciceCurCoefficient
ChStarted = config.Start.Unix() > 0 && time.Since(config.Start) >= 0
if lastRegeneration != config.Generation || fic.PartialValidation != config.PartialValidation || fic.UnlockedChallengeDepth != config.UnlockedChallengeDepth || fic.UnlockedChallengeUpTo != config.UnlockedChallengeUpTo || fic.DisplayAllFlags != config.DisplayAllFlags || fic.FirstBlood != config.FirstBlood || fic.SubmissionCostBase != config.SubmissionCostBase || fic.SubmissionUniqueness != config.SubmissionUniqueness || fic.DiscountedFactor != config.DiscountedFactor {
fic.PartialValidation = config.PartialValidation
fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth
fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo
fic.DisplayAllFlags = config.DisplayAllFlags
fic.PartialValidation = config.PartialValidation
fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth
fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo
fic.DisplayAllFlags = config.DisplayAllFlags
fic.FirstBlood = config.FirstBlood
fic.SubmissionCostBase = config.SubmissionCostBase
fic.SubmissionUniqueness = config.SubmissionUniqueness
fic.GlobalScoreCoefficient = config.GlobalScoreCoefficient
fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries
fic.DiscountedFactor = config.DiscountedFactor
if !skipInitialGeneration {
log.Println("Generating files...")
go func() {
genAll()
log.Println("Full generation done")
}()
} else {
skipInitialGeneration = false
log.Println("Regeneration skipped by option.")
}
lastRegeneration = config.Generation
} else {
log.Println("No change found. Skipping regeneration.")
}
fic.FirstBlood = config.FirstBlood
fic.SubmissionCostBase = config.SubmissionCostBase
fic.SubmissionUniqueness = config.SubmissionUniqueness
fic.GlobalScoreCoefficient = config.GlobalScoreCoefficient
fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries
fic.DiscountedFactor = config.DiscountedFactor
}
func main() {
if v, exists := os.LookupEnv("FIC_BASEURL"); exists {
fic.FilesDir = v + "files"
} else {
fic.FilesDir = "/files"
}
var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server")
flag.StringVar(&generatorSocket, "generator", "./GENERATOR/generator.socket", "Path to the generator socket")
flag.StringVar(&settings.SettingsDir, "settings", "./SETTINGSDIST", "Base directory where load and save settings")
flag.StringVar(&SubmissionDir, "submission", "./submissions", "Base directory where save submissions")
flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files")
flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Request path prefix to reach files")
var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events")
flag.BoolVar(&skipInitialGeneration, "skipfullgeneration", skipInitialGeneration, "Skip the initial regeneration")
flag.IntVar(&parallelJobs, "jobs", parallelJobs, "Number of generation workers")
flag.Parse()
log.SetPrefix("[backend] ")
log.SetPrefix("[checker] ")
settings.SettingsDir = path.Clean(settings.SettingsDir)
SubmissionDir = path.Clean(SubmissionDir)
@ -129,8 +105,6 @@ func main() {
rand.Seed(time.Now().UnixNano())
launchWorkers()
log.Println("Creating submission directory...")
if _, err := os.Stat(path.Join(SubmissionDir, ".tmp")); os.IsNotExist(err) {
if err := os.MkdirAll(path.Join(SubmissionDir, ".tmp"), 0777); err != nil {
@ -158,24 +132,23 @@ func main() {
log.Fatal(err)
}
// Register SIGUSR1, SIGUSR2
// Register SIGUSR1 and SIGTERM
interrupt1 := make(chan os.Signal, 1)
signal.Notify(interrupt1, syscall.SIGUSR1)
interrupt2 := make(chan os.Signal, 1)
signal.Notify(interrupt2, syscall.SIGUSR2)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, syscall.SIGTERM)
watchedNotify := fsnotify.Create
loop:
for {
select {
case <-interrupt:
break loop
case <-interrupt1:
log.Println("SIGUSR1 received, retreating all files in queue...")
walkAndTreat(SubmissionDir)
log.Println("SIGUSR1 treated.")
case <-interrupt2:
inQueueMutex.Lock()
log.Printf("SIGUSR2 received, dumping statistics:\n parallelJobs: %d\n genTeamQueue size: %d\n genQueue: %d\n Teams in queue: %v\n Challenge started: %v\n Last regeneration: %v\n", parallelJobs, len(genTeamQueue), len(genQueue), inGenQueue, ChStarted, lastRegeneration)
inQueueMutex.Unlock()
case ev := <-watcher.Events:
if d, err := os.Lstat(ev.Name); err == nil && ev.Op&fsnotify.Create == fsnotify.Create && d.Mode().IsDir() && d.Mode()&os.ModeSymlink == 0 && d.Name() != ".tmp" {
// Register new subdirectory

View File

@ -46,8 +46,8 @@ func registrationProcess(id string, team *fic.Team, members []fic.Member, team_i
log.Println(id, "[ERR]", err)
}
genTeamQueue <- team
appendGenQueue(genStruct{Type: GenTeams})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeams})
}
func treatRegistration(pathname string, team_id string) {
@ -94,7 +94,7 @@ func treatRegistration(pathname string, team_id string) {
log.Printf("%s [WRN] Unable to create event: %s\n", id, err)
}
appendGenQueue(genStruct{Type: GenEvents})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenEvents})
}
}
}

View File

@ -38,11 +38,11 @@ func treatRename(pathname string, team *fic.Team) {
if _, err := team.Update(); err != nil {
log.Printf("%s [WRN] Unable to change team name: %s\n", id, err)
}
genTeamQueue <- team
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
if _, err := fic.NewEvent(fmt.Sprintf("Souhaitons bonne chance à l'équipe <strong>%s</strong> qui vient de nous rejoindre&#160;!", html.EscapeString(team.Name)), "info"); err != nil {
log.Printf("%s [WRN] Unable to create event: %s\n", id, err)
}
appendGenQueue(genStruct{Type: GenEvents})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenEvents})
if err := os.Remove(pathname); err != nil {
log.Printf("%s [ERR] %s\n", id, err)
}

View File

@ -129,7 +129,7 @@ func treatSubmission(pathname string, team *fic.Team, exercice_id string) {
solved, err := exercice.CheckResponse(cksum[:], responses.Keys, responses.MCQs, team)
if err != nil {
log.Println(id, "[ERR] Unable to CheckResponse:", err)
genTeamQueue <- team
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
return
}
@ -139,7 +139,7 @@ func treatSubmission(pathname string, team *fic.Team, exercice_id string) {
}
if tm != nil {
genTeamQueue <- team
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
} else if solved {
log.Printf("%s Team %d SOLVED exercice %d (%s : %s)\n", id, team.Id, exercice.Id, theme.Name, exercice.Title)
if err := exercice.Solved(team); err != nil {
@ -152,9 +152,9 @@ func treatSubmission(pathname string, team *fic.Team, exercice_id string) {
} else if _, err := fic.NewEvent(fmt.Sprintf("L'équipe %s a résolu le <strong>%d<sup>e</sup></strong> défi %s&#160;!", html.EscapeString(team.Name), lvl, theme.Name), "success"); err != nil {
log.Println(id, "[WRN] Unable to create event:", err)
}
genTeamQueue <- team
appendGenQueue(genStruct{id, GenThemes})
appendGenQueue(genStruct{id, GenTeams})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenThemes})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeams})
} else {
log.Printf("%s Team %d submit an invalid solution for exercice %d (%s : %s)\n", id, team.Id, exercice.Id, theme.Name, exercice.Title)
@ -166,8 +166,8 @@ func treatSubmission(pathname string, team *fic.Team, exercice_id string) {
log.Println(id, "[WRN] Unable to create event:", err)
}
}
genTeamQueue <- team
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
}
appendGenQueue(genStruct{id, GenEvents})
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenEvents})
}

View File

@ -1,9 +1,9 @@
10.10.10.1 phobos
172.17.0.2 admin
172.17.0.3 backend
172.17.0.3 checker
172.17.0.4 db
172.17.0.5 sshd
172.17.0.5 generator
10.10.10.2 deimos

View File

@ -28,11 +28,12 @@ services:
- /mnt/fic:/mnt/fic:ro
- dashboard:/srv/DASHBOARD
- files:/srv/FILES
- generator:/srv/GENERATOR:ro
- pki:/srv/PKI
- settings:/srv/SETTINGS
- settingsdist:/srv/SETTINGSDIST
- submissions:/srv/submissions
- teams:/srv/TEAMS
- teams:/srv/TEAMS:ro
command: -baseurl /admin/ -localimport /mnt/fic -localimportsymlink
depends_on:
- mysql
@ -51,20 +52,39 @@ services:
- settings:/srv/SETTINGS
- settingsdist:/srv/SETTINGSDIST
backend:
checker:
build:
context: .
dockerfile: Dockerfile-backend
image: nemunaire/fic-backend:latest
dockerfile: Dockerfile-checker
image: nemunaire/fic-checker:latest
links:
- mysql
networks:
- fic-net
volumes:
- files:/srv/FILES:ro
- teams:/srv/TEAMS
- generator:/srv/GENERATOR:ro
- teams:/srv/TEAMS:ro
- settingsdist:/srv/SETTINGSDIST:ro
- submissions:/srv/submissions
depends_on:
- mysql
- generator
environment:
- MYSQL_HOST=mysql
generator:
build:
context: .
dockerfile: Dockerfile-generator
image: nemunaire/fic-generator:latest
links:
- mysql
networks:
- fic-net
volumes:
- generator:/srv/GENERATOR
- teams:/srv/TEAMS
- settingsdist:/srv/SETTINGSDIST:ro
depends_on:
- mysql
environment:
@ -106,8 +126,6 @@ services:
- settingsdist:/srv/SETTINGSDIST:ro
- submissions:/srv/submissions
- startingblock:/srv/startingblock
depends_on:
- backend
dashboard:
build:
@ -123,8 +141,6 @@ services:
- dashboard:/srv/DASHBOARD
- teams:/srv/TEAMS:ro
- settingsdist:/srv/SETTINGSDIST:ro
depends_on:
- backend
front:
build:
@ -146,7 +162,7 @@ services:
- qa
- receiver
- dashboard
- backend
- checker
- admin
volumes:
@ -154,6 +170,7 @@ volumes:
dashboard:
files:
htdocs:
generator:
pki:
settings:
settingsdist:

View File

@ -61,17 +61,28 @@ onboot:
peer: veth-admin
bindNS:
net: /run/netns/fic-admin
- name: backend-ip-setup
- name: checker-ip-setup
image: linuxkit/ip:c88e3272e3b12edec454e4720da8bb70a7655bc7
command: ["/bin/sh", "-c", "ip a add 172.17.0.3/24 dev vethin-backend; ip link set vethin-backend up;" ]
command: ["/bin/sh", "-c", "ip a add 172.17.0.3/24 dev vethin-checker; ip link set vethin-checker up;" ]
net: new
runtime:
interfaces:
- name: vethin-backend
- name: vethin-checker
add: veth
peer: veth-backend
peer: veth-checker
bindNS:
net: /run/netns/fic-backend
net: /run/netns/fic-checker
- name: generator-ip-setup
image: linuxkit/ip:c88e3272e3b12edec454e4720da8bb70a7655bc7
command: ["/bin/sh", "-c", "ip a add 172.17.0.5/24 dev vethin-generator; ip link set vethin-generator up;" ]
net: new
runtime:
interfaces:
- name: vethin-generator
add: veth
peer: veth-generator
bindNS:
net: /run/netns/fic-generator
- name: mysql-ip-setup
image: linuxkit/ip:c88e3272e3b12edec454e4720da8bb70a7655bc7
command: ["/bin/sh", "-c", "ip a add 172.17.0.4/24 dev vethin-db; ip link set vethin-db up;" ]
@ -85,7 +96,7 @@ onboot:
net: /run/netns/db
- name: bridge-setup
image: linuxkit/ip:c88e3272e3b12edec454e4720da8bb70a7655bc7
command: ["/bin/sh", "-c", "ip a add 172.17.0.1/24 dev br0; ip link set veth-admin master br0; ip link set veth-backend master br0; ip link set veth-db master br0; ip link set br0 up; ip link set veth-admin up; ip link set veth-backend up; ip link set veth-db up;" ]
command: ["/bin/sh", "-c", "ip a add 172.17.0.1/24 dev br0; ip link set veth-admin master br0; ip link set veth-checker master br0; ip link set veth-generator master br0; ip link set veth-db master br0; ip link set br0 up; ip link set veth-admin up; ip link set veth-checker up; ip link set veth-generator up; ip link set veth-db up;" ]
runtime:
interfaces:
- name: br0
@ -173,11 +184,11 @@ services:
- /var/lib/fic/raw_files:/mnt/fic
- /var/lib/fic/dashboard:/srv/DASHBOARD
- /var/lib/fic/files:/srv/FILES
- /var/lib/fic/generator:/srv/GENERATOR:ro
- /var/lib/fic/pki:/srv/PKI
- /var/lib/fic/teams:/srv/TEAMS
- /var/lib/fic/settings:/srv/SETTINGS
- /var/lib/fic/sync:/srv/SYNC
- /var/lib/fic/submissions:/srv/submissions:ro
- /var/lib/fic/teams:/srv/TEAMS:ro
net: /run/netns/fic-admin
pid: new
ipc: new
@ -186,10 +197,10 @@ services:
mkdir:
- /var/lib/fic/dashboard
- /var/lib/fic/files
- /var/lib/fic/generator
- /var/lib/fic/raw_files
- /var/lib/fic/pki
- /var/lib/fic/settings
- /var/lib/fic/sync
- /var/lib/fic/submissions
- /var/lib/fic/teams
- name: fic-evdist
@ -206,21 +217,24 @@ services:
mkdir:
- /var/lib/fic/settings
- /var/lib/fic/settingsdist
- name: fic-backend
image: nemunaire/fic-backend:latest@sha256:12d3286cdbe6d18d284f21432b4eb92ce8ab9844982177562069bc0f9536c93b
- name: fic-checker
image: nemunaire/fic-checker:latest@sha256:12d3286cdbe6d18d284f21432b4eb92ce8ab9844982177562069bc0f9536c93b
env:
- MYSQL_HOST=db
- MYSQL_PASSWORD=fic
binds:
- /etc/hosts:/etc/hosts:ro
- /var/lib/fic/teams:/srv/TEAMS
- /var/lib/fic/generator:/srv/GENERATOR:ro
- /var/lib/fic/teams:/srv/TEAMS:ro
- /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro
- /var/lib/fic/submissions:/srv/submissions
net: /run/netns/fic-backend
net: /run/netns/fic-checker
pid: new
ipc: new
uts: new
runtime:
mkdir:
- /var/lib/fic/generator
- /var/lib/fic/settingsdist
- /var/lib/fic/submissions
- /var/lib/fic/teams
@ -242,6 +256,26 @@ services:
- /var/lib/fic/dashboard
- /var/lib/fic/teams
- /var/lib/fic/settingsdist
- name: fic-generator
image: nemunaire/fic-generator:latest@sha256:12d3286cdbe6d18d284f21432b4eb92ce8ab9844982177562069bc0f9536c93b
command: ["/srv/generator", "-bind=/srv/GENERATOR/generator.socket"]
env:
- MYSQL_HOST=db
- MYSQL_PASSWORD=fic
binds:
- /etc/hosts:/etc/hosts:ro
- /var/lib/fic/generator:/srv/GENERATOR:ro
- /var/lib/fic/teams:/srv/TEAMS
- /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro
net: /run/netns/fic-generator
pid: new
ipc: new
uts: new
runtime:
mkdir:
- /var/lib/fic/generator
- /var/lib/fic/settingsdist
- /var/lib/fic/teams
- name: fic-synchro
image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285
command: ["/bin/ash", "/root/synchro.sh"]

View File

@ -40,12 +40,12 @@
subPackages = [ "admin" ];
};
fic-backend = pkgs.buildGoModule {
pname = "backend";
fic-checker = pkgs.buildGoModule {
pname = "checker";
inherit version vendorSha256 overrideModAttrs;
src = ./.;
subPackages = [ "backend" ];
subPackages = [ "checker" ];
};
fic-dashboard = pkgs.buildGoModule {
@ -56,6 +56,14 @@
subPackages = [ "dashboard" ];
};
fic-generator = pkgs.buildGoModule {
pname = "generator";
inherit version vendorSha256 overrideModAttrs;
src = ./.;
subPackages = [ "generator" ];
};
fic-synchro = pkgs.writeShellApplication {
name = "synchro";
runtimeInputs = [ pkgs.rsync pkgs.openssh pkgs.coreutils ];

1
generator/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
generator

View File

@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"runtime"
@ -13,30 +14,14 @@ import (
"srs.epita.fr/fic-server/libfic"
)
type GenerateType int
const (
GenPublic GenerateType = iota
GenEvents
GenTeams
GenThemes
)
type genStruct struct {
Id string
Type GenerateType
}
var parallelJobs = runtime.NumCPU()
var genTeamQueue chan *fic.Team
var genQueue chan genStruct
var genQueue chan fic.GenStruct
var inQueueMutex sync.RWMutex
var inGenQueue map[GenerateType]bool
var inGenQueue map[fic.GenerateType]bool
func init() {
genTeamQueue = make(chan *fic.Team)
genQueue = make(chan genStruct)
inGenQueue = map[GenerateType]bool{}
genQueue = make(chan fic.GenStruct)
inGenQueue = map[fic.GenerateType]bool{}
}
func launchWorkers() {
@ -46,7 +31,29 @@ func launchWorkers() {
}
}
func appendGenQueue(gs genStruct) {
func enqueueHandler(w http.ResponseWriter, r *http.Request) {
var gs fic.GenStruct
dec := json.NewDecoder(r.Body)
err := dec.Decode(&gs)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Printf("POST /enqueue | %v", gs)
appendGenQueue(gs)
http.Error(w, "OK", http.StatusOK)
}
func appendGenQueue(gs fic.GenStruct) {
if gs.Type == fic.GenTeam || gs.Type == fic.GenTeamIssues {
genQueue <- gs
return
}
// Append only if not already in queue
inQueueMutex.RLock()
if v, ok := inGenQueue[gs.Type]; !ok || !v {
inQueueMutex.RUnlock()
@ -62,31 +69,30 @@ func appendGenQueue(gs genStruct) {
}
func consumer() {
var id string
var err error
for {
var id string
var err error
gs := <-genQueue
id = gs.Id
select {
case gt := <-genTeamQueue:
err = genTeamMyFile(gt)
inQueueMutex.Lock()
inGenQueue[gs.Type] = false
inQueueMutex.Unlock()
case gs := <-genQueue:
id = gs.Id
inQueueMutex.Lock()
inGenQueue[gs.Type] = false
inQueueMutex.Unlock()
switch gs.Type {
case GenPublic:
err = genMyPublicFile()
case GenEvents:
err = genEventsFile()
case GenTeams:
err = genTeamsFile()
case GenThemes:
err = genThemesFile()
}
switch gs.Type {
case fic.GenPublic:
err = genMyPublicFile()
case fic.GenEvents:
err = genEventsFile()
case fic.GenTeam:
err = genTeamMyFile(gs.TeamId)
case fic.GenTeams:
err = genTeamsFile()
case fic.GenThemes:
err = genThemesFile()
case fic.GenTeamIssues:
err = genTeamIssuesFile(gs.TeamId)
}
if err != nil {
@ -96,18 +102,36 @@ func consumer() {
}
// Generate issues.json for a given team
func genTeamIssuesFile(team *fic.Team) error {
func genTeamIssuesFile(teamid int64) error {
team, err := fic.GetTeam(teamid)
if err != nil {
return fmt.Errorf("Unable to GetTeam: %w", err)
}
dirPath := path.Join(TeamsDir, fmt.Sprintf("%d", team.Id))
my, err := team.MyIssueFile()
if err != nil {
return err
}
if my == nil {
if _, err := os.Stat(path.Join(dirPath, "issues.json")); !os.IsNotExist(err) {
err = os.Remove(path.Join(dirPath, "issues.json"))
if err != nil {
log.Printf("Unable to remove empty issues file: %s", path.Join(dirPath, "issues.json"))
}
}
return nil
}
if s, err := os.Stat(dirPath); os.IsNotExist(err) {
os.MkdirAll(dirPath, 0777)
} else if !s.IsDir() {
return fmt.Errorf("%s is not a directory", dirPath)
}
if my, err := team.MyIssueFile(); err != nil {
return err
} else if j, err := json.Marshal(my); err != nil {
if j, err := json.Marshal(my); err != nil {
return err
} else if err = ioutil.WriteFile(path.Join(dirPath, "issues.json"), j, 0644); err != nil {
return err
@ -117,7 +141,12 @@ func genTeamIssuesFile(team *fic.Team) error {
}
// Generate my.json, wait.json and scores.json for a given team
func genTeamMyFile(team *fic.Team) error {
func genTeamMyFile(teamid int64) error {
team, err := fic.GetTeam(teamid)
if err != nil {
return fmt.Errorf("Unable to GetTeam: %w", err)
}
dirPath := path.Join(TeamsDir, fmt.Sprintf("%d", team.Id))
if s, err := os.Stat(dirPath); os.IsNotExist(err) {
@ -235,17 +264,17 @@ func genThemesFile() error {
}
func genAll() {
appendGenQueue(genStruct{Type: GenThemes})
appendGenQueue(genStruct{Type: GenTeams})
appendGenQueue(genStruct{Type: GenEvents})
appendGenQueue(genStruct{Type: GenPublic})
appendGenQueue(fic.GenStruct{Type: fic.GenThemes})
appendGenQueue(fic.GenStruct{Type: fic.GenTeams})
appendGenQueue(fic.GenStruct{Type: fic.GenEvents})
appendGenQueue(fic.GenStruct{Type: fic.GenPublic})
if teams, err := fic.GetActiveTeams(); err != nil {
log.Println("Team retrieval error: ", err)
} else {
for _, team := range teams {
myteam := team // team is reused, we need to create a new variable here to store the value
genTeamQueue <- myteam
appendGenQueue(fic.GenStruct{Type: fic.GenTeam, TeamId: team.Id})
appendGenQueue(fic.GenStruct{Type: fic.GenTeamIssues, TeamId: team.Id})
}
}
}

151
generator/main.go Normal file
View File

@ -0,0 +1,151 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"path"
"strings"
"syscall"
"time"
"srs.epita.fr/fic-server/libfic"
"srs.epita.fr/fic-server/settings"
)
var TeamsDir string
var ChStarted = false
var lastRegeneration time.Time
var skipInitialGeneration = false
func reloadSettings(config *settings.Settings) {
fic.HintCoefficient = config.HintCurCoefficient
fic.WChoiceCoefficient = config.WChoiceCurCoefficient
fic.ExerciceCurrentCoefficient = config.ExerciceCurCoefficient
ChStarted = config.Start.Unix() > 0 && time.Since(config.Start) >= 0
if lastRegeneration != config.Generation || fic.PartialValidation != config.PartialValidation || fic.UnlockedChallengeDepth != config.UnlockedChallengeDepth || fic.UnlockedChallengeUpTo != config.UnlockedChallengeUpTo || fic.DisplayAllFlags != config.DisplayAllFlags || fic.FirstBlood != config.FirstBlood || fic.SubmissionCostBase != config.SubmissionCostBase || fic.SubmissionUniqueness != config.SubmissionUniqueness || fic.DiscountedFactor != config.DiscountedFactor {
fic.PartialValidation = config.PartialValidation
fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth
fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo
fic.DisplayAllFlags = config.DisplayAllFlags
fic.FirstBlood = config.FirstBlood
fic.SubmissionCostBase = config.SubmissionCostBase
fic.SubmissionUniqueness = config.SubmissionUniqueness
fic.GlobalScoreCoefficient = config.GlobalScoreCoefficient
fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries
fic.DiscountedFactor = config.DiscountedFactor
if !skipInitialGeneration {
log.Println("Generating files...")
go func() {
genAll()
log.Println("Full generation done")
}()
} else {
skipInitialGeneration = false
log.Println("Regeneration skipped by option.")
}
lastRegeneration = config.Generation
} else {
log.Println("No change found. Skipping regeneration.")
}
}
func main() {
if v, exists := os.LookupEnv("FIC_BASEURL"); exists {
fic.FilesDir = v + "files"
} else {
fic.FilesDir = "/files"
}
var bind = flag.String("bind", "./GENERATOR/generator.socket", "Bind path or port/socket")
var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server")
flag.StringVar(&settings.SettingsDir, "settings", "./SETTINGSDIST", "Base directory where load and save settings")
flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files")
flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Request path prefix to reach files")
flag.BoolVar(&skipInitialGeneration, "skipfullgeneration", skipInitialGeneration, "Skip the initial regeneration")
flag.IntVar(&parallelJobs, "jobs", parallelJobs, "Number of generation workers")
flag.Parse()
log.SetPrefix("[generator] ")
settings.SettingsDir = path.Clean(settings.SettingsDir)
TeamsDir = path.Clean(TeamsDir)
launchWorkers()
log.Println("Opening DB...")
if err := fic.DBInit(*dsn); err != nil {
log.Fatal("Cannot open the database: ", err)
}
defer fic.DBClose()
// Load configuration
settings.LoadAndWatchSettings(path.Join(settings.SettingsDir, settings.SettingsFile), reloadSettings)
// Register SIGUSR1, SIGUSR2
interrupt1 := make(chan os.Signal, 1)
signal.Notify(interrupt1, syscall.SIGUSR1)
interrupt2 := make(chan os.Signal, 1)
signal.Notify(interrupt2, syscall.SIGUSR2)
// Prepare graceful shutdown
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
srv := &http.Server{
Addr: *bind,
}
http.HandleFunc("/enqueue", enqueueHandler)
// Serve pages
go func() {
if !strings.Contains(*bind, ":") {
if _, err := os.Stat(*bind); !os.IsNotExist(err) {
if err := os.Remove(*bind); err != nil {
log.Fatal(err)
}
}
os.MkdirAll(path.Dir(*bind), 0777)
unixListener, err := net.Listen("unix", *bind)
if err != nil {
log.Fatal(err)
}
log.Fatal(srv.Serve(unixListener))
} else if err := srv.ListenAndServe(); err != nil {
log.Fatal(err)
}
}()
log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
// Wait shutdown signal
loop:
for {
select {
case <-interrupt:
break loop
case <-interrupt1:
log.Println("SIGUSR1 received, regenerating all files...")
genAll()
log.Println("SIGUSR1 treated.")
case <-interrupt2:
inQueueMutex.Lock()
log.Printf("SIGUSR2 received, dumping statistics:\n parallelJobs: %d\n genQueue: %d\n Teams in queue: %v\n Challenge started: %v\n Last regeneration: %v\n", parallelJobs, len(genQueue), inGenQueue, ChStarted, lastRegeneration)
inQueueMutex.Unlock()
}
}
log.Print("The service is shutting down...")
srv.Shutdown(context.Background())
log.Println("done")
}

18
libfic/generation.go Normal file
View File

@ -0,0 +1,18 @@
package fic
type GenerateType int
const (
GenPublic GenerateType = iota
GenEvents
GenTeam
GenTeams
GenThemes
GenTeamIssues
)
type GenStruct struct {
Id string `json:"id"`
Type GenerateType `json:"type"`
TeamId int64 `json:"team_id,omitempty"`
}

View File

@ -3,9 +3,10 @@
imports = [
./db.nix
./fic-admin.nix
./fic-backend.nix
./fic-checker.nix
./fic-dashboard.nix
./fic-evdist.nix
./fic-generator.nix
./fic-synchro.nix
];

View File

@ -27,13 +27,13 @@
};
volumes = [
"/etc/hosts:/etc/hosts:ro"
"/var/lib/fic/generator:/srv/GENERATOR:ro"
"/var/lib/fic/raw_files:/mnt/fic"
"/var/lib/fic/dashboard:/srv/DASHBOARD"
"/var/lib/fic/files:/srv/FILES"
"/var/lib/fic/pki:/srv/PKI"
"/var/lib/fic/teams:/srv/TEAMS"
"/var/lib/fic/teams:/srv/TEAMS:ro"
"/var/lib/fic/settings:/srv/SETTINGS"
"/var/lib/fic/sync:/srv/SYNC"
"/var/lib/fic/submissions:/srv/submissions:ro"
];
};

View File

@ -0,0 +1,27 @@
{ config, inputs, pkgs, ... }:
{
config.virtualisation.oci-containers.containers.fic-checker = {
image = "fic-checker:latest";
imageFile = pkgs.dockerTools.buildImage {
name = "fic-checker";
tag = "latest";
created = "now";
config = {
Cmd = [ "${inputs.ficpkgs.packages.x86_64-linux.fic-checker}/bin/checker" ];
};
};
autoStart = true;
environment = {
MYSQL_HOST = "db";
};
workdir = "/srv";
extraOptions = [ "--network=phobos-lan" "--ip=172.18.0.41" ];
volumes = [
"/etc/hosts:/etc/hosts:ro"
"/var/lib/fic/generator:/srv/GENERATOR:ro"
"/var/lib/fic/teams:/srv/TEAMS:ro"
"/var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro"
"/var/lib/fic/submissions:/srv/submissions"
];
};
}

View File

@ -1,13 +1,13 @@
{ config, inputs, pkgs, ... }:
{
config.virtualisation.oci-containers.containers.fic-backend = {
image = "fic-backend:latest";
config.virtualisation.oci-containers.containers.fic-generator = {
image = "fic-generator:latest";
imageFile = pkgs.dockerTools.buildImage {
name = "fic-backend";
name = "fic-generator";
tag = "latest";
created = "now";
config = {
Cmd = [ "${inputs.ficpkgs.packages.x86_64-linux.fic-backend}/bin/backend" ];
Cmd = [ "${inputs.ficpkgs.packages.x86_64-linux.fic-generator}/bin/generator" ];
};
};
autoStart = true;
@ -18,9 +18,9 @@
extraOptions = [ "--network=phobos-lan" "--ip=172.18.0.41" ];
volumes = [
"/etc/hosts:/etc/hosts:ro"
"/var/lib/fic/generator:/srv/GENERATOR"
"/var/lib/fic/teams:/srv/TEAMS"
"/var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro"
"/var/lib/fic/submissions:/srv/submissions"
];
};
}