Skip to content
This repository has been archived by the owner on Dec 26, 2019. It is now read-only.

Commit

Permalink
simplify configuration, semantic version, multi-worker
Browse files Browse the repository at this point in the history
AMQP had several options in the config which should not be altered.
The version gets its own endpoint and uses semver. Starting
the background workers now supports the flag "-n X" to start X
workers within process to avoid having multiple detached screens.
  • Loading branch information
PatWie committed May 13, 2019
1 parent c1055c9 commit 939c14b
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 49 deletions.
4 changes: 2 additions & 2 deletions .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ steps:
pull: default
image: golang
commands:
- sed -i 's/"X-INFOMARK-VERSION", "0.0.1"/"X-INFOMARK-VERSION", "${DRONE_COMMIT_SHA}"/g' api/app/router.go
- sed -i 's/"YXZ"/"${DRONE_COMMIT_SHA}"/g' symbol/version.go
- go version
- go build infomark.go
environment:
Expand Down Expand Up @@ -126,6 +126,6 @@ services:

---
kind: signature
hmac: bec11f987bcc8b10e923674bbb63e983c274ee5b007d2f2ae9a703f8f6e829cd
hmac: 79461cde6a81a50bc7e6ad292872f4a10b9126d206196430cd4accaa4f6a61b9

...
3 changes: 0 additions & 3 deletions .infomark.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
# ------------------------------------------------------------------------------

rabbitmq_connection: amqp://user:password@localhost:5672/
rabbitmq_exchange: test-exchange
rabbitmq_exchangeType: direct
rabbitmq_queue: test-queue
rabbitmq_key: test-key

# backend
Expand Down
3 changes: 0 additions & 3 deletions .infomark.yml.ci
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
# ------------------------------------------------------------------------------

rabbitmq_connection: amqp://user:password@localhost:5672/
rabbitmq_exchange: test-exchange
rabbitmq_exchangeType: direct
rabbitmq_queue: test-queue
rabbitmq_key: test-key


Expand Down
16 changes: 16 additions & 0 deletions api/app/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ func (rs *CommonResource) PingHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
}

// VersionHandler is public endpoint for
// URL: /version
// METHOD: get
// TAG: common
// RESPONSE: 200,versionResponse
// SUMMARY: all version information
func (rs *CommonResource) VersionHandler(w http.ResponseWriter, r *http.Request) {
// render JSON reponse
if err := render.Render(w, r, newVersionResponse()); err != nil {
render.Render(w, r, ErrRender(err))
return
}

render.Status(r, http.StatusOK)
}

// PrivacyStatementHandler is public endpoint for
// URL: /privacy_statement
// METHOD: get
Expand Down
25 changes: 23 additions & 2 deletions api/app/common_responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ package app

import (
"net/http"

"github.com/cgtuebingen/infomark-backend/symbol"
)

// courseResponse is the response payload for course management.
// rawResponse is the response payload for course management.
type rawResponse struct {
Text string `json:"text" example:"some text"`
}

// Render post-processes a courseResponse.
// Render post-processes a rawResponse.
func (body *rawResponse) Render(w http.ResponseWriter, r *http.Request) error {
return nil
}
Expand All @@ -38,3 +40,22 @@ func newRawResponse(text string) *rawResponse {
Text: text,
}
}

// versionResponse is the response payload for course management.
type versionResponse struct {
Commit string `json:"commit" example:"d725269a8a7498aae1dbb07786bed4c88b002661"`
Version string `json:"version" example:"1"`
}

// newVersionResponse creates a response from a course model.
func newVersionResponse() *versionResponse {
return &versionResponse{
Commit: symbol.GitCommit,
Version: symbol.Version.String(),
}
}

// Render post-processes a versionResponse.
func (body *versionResponse) Render(w http.ResponseWriter, r *http.Request) error {
return nil
}
23 changes: 14 additions & 9 deletions api/app/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/cgtuebingen/infomark-backend/auth/authenticate"
"github.com/cgtuebingen/infomark-backend/auth/authorize"
"github.com/cgtuebingen/infomark-backend/symbol"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
Expand Down Expand Up @@ -74,21 +75,24 @@ func LoggingMiddleware(next http.Handler) http.Handler {
start := time.Now()
next.ServeHTTP(w, r)
end := time.Now()
log.WithFields(logrus.Fields{
"method": r.Method,
// "proto": r.Proto,
"agent": r.UserAgent(),
"remote": r.RemoteAddr,
"latency": end.Sub(start),
"time": end.Format(time.RFC3339),
}).Info(r.RequestURI)
if r.RequestURI != "/metrics" {
log.WithFields(logrus.Fields{
"method": r.Method,
// "proto": r.Proto,
"agent": r.UserAgent(),
"remote": r.RemoteAddr,
"latency": end.Sub(start),
"time": end.Format(time.RFC3339),
}).Info(r.RequestURI)
}
})
}

// VersionMiddleware writes the current API version to the headers.
func VersionMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-INFOMARK-VERSION", "0.0.1")
w.Header().Set("X-INFOMARK-VERSION", symbol.Version.String())
w.Header().Set("X-INFOMARK-Commit", symbol.GitCommit)
next.ServeHTTP(w, r)
})
}
Expand Down Expand Up @@ -171,6 +175,7 @@ func New(db *sqlx.DB, log bool) (*chi.Mux, error) {
r.Post("/auth/confirm_email", appAPI.Auth.ConfirmEmailHandler)
r.Post("/account", appAPI.Account.CreateHandler)
r.Get("/ping", appAPI.Common.PingHandler)
r.Get("/version", appAPI.Common.VersionHandler)
r.Get("/privacy_statement", appAPI.Common.PrivacyStatementHandler)
})

Expand Down
6 changes: 3 additions & 3 deletions api/app/submission_producer.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ func init() {

cfg := &service.Config{
Connection: viper.GetString("rabbitmq_connection"),
Exchange: viper.GetString("rabbitmq_exchange"),
ExchangeType: viper.GetString("rabbitmq_exchangeType"),
Queue: viper.GetString("rabbitmq_queue"),
Exchange: "infomark-worker-exchange",
ExchangeType: "direct",
Queue: "infomark-worker-submissions",
Key: viper.GetString("rabbitmq_key"),
Tag: "SimpleSubmission",
}
Expand Down
38 changes: 25 additions & 13 deletions api/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,19 @@ import (

background "github.com/cgtuebingen/infomark-backend/api/worker"
"github.com/cgtuebingen/infomark-backend/service"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
)

// Worker provides a background worker
type Worker struct{}
type Worker struct {
NumInstances int
}

// NewWorker creates and configures an background worker
func NewWorker() (*Worker, error) {
func NewWorker(numInstances int) (*Worker, error) {
log.Println("configuring worker...")
return &Worker{}, nil
return &Worker{NumInstances: numInstances}, nil
}

// Start runs ListenAndServe on the http.Worker with graceful shutdown.
Expand All @@ -42,25 +45,34 @@ func (srv *Worker) Start() {

cfg := &service.Config{
Connection: viper.GetString("rabbitmq_connection"),
Exchange: viper.GetString("rabbitmq_exchange"),
ExchangeType: viper.GetString("rabbitmq_exchangeType"),
Queue: viper.GetString("rabbitmq_queue"),
Exchange: "infomark-worker-exchange",
ExchangeType: "direct",
Queue: "infomark-worker-submissions",
Key: viper.GetString("rabbitmq_key"),
Tag: "SimpleSubmission",
}

consumer, _ := service.NewConsumer(cfg, background.DefaultSubmissionHandler.Handle)
deliveries, err := consumer.Setup()
if err != nil {
panic(err)
}
consumers := []*service.Consumer{}

go consumer.HandleLoop(deliveries)
for i := 0; i < srv.NumInstances; i++ {
log.WithFields(logrus.Fields{"instance": i}).Info("start")
consumer, _ := service.NewConsumer(cfg, background.DefaultSubmissionHandler.Handle, i)
deliveries, err := consumer.Setup()
if err != nil {
panic(err)
}
consumers = append(consumers, consumer)
go consumers[i].HandleLoop(deliveries)
}

quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
sig := <-quit
log.Println("Shutting down Worker... Reason:", sig)
consumer.Shutdown()

for i := 0; i < srv.NumInstances; i++ {
consumers[i].Shutdown()
}

log.Println("Worker gracefully stopped")
}
12 changes: 6 additions & 6 deletions cmd/console/submission_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ var SubmissionTriggerCmd = &cobra.Command{

cfg := &service.Config{
Connection: viper.GetString("rabbitmq_connection"),
Exchange: viper.GetString("rabbitmq_exchange"),
ExchangeType: viper.GetString("rabbitmq_exchangeType"),
Queue: viper.GetString("rabbitmq_queue"),
Exchange: "infomark-worker-exchange",
ExchangeType: "direct",
Queue: "infomark-worker-submissions",
Key: viper.GetString("rabbitmq_key"),
Tag: "SimpleSubmission",
}
Expand Down Expand Up @@ -242,9 +242,9 @@ This triggers all [kind]-tests again (private, public).

cfg := &service.Config{
Connection: viper.GetString("rabbitmq_connection"),
Exchange: viper.GetString("rabbitmq_exchange"),
ExchangeType: viper.GetString("rabbitmq_exchangeType"),
Queue: viper.GetString("rabbitmq_queue"),
Exchange: "infomark-worker-exchange",
ExchangeType: "direct",
Queue: "infomark-worker-submissions",
Key: viper.GetString("rabbitmq_key"),
Tag: "SimpleSubmission",
}
Expand Down
8 changes: 5 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ import (
var RootCmd = &cobra.Command{
Use: "infomark",
Short: "A CI based course framework",
Long: `InfoMark distributes exercise sheets in an course management system and
tests students homework submission for these exercises sheet automatically.
Long: `InfoMark is a a scalable, modern and open-source
online course management system supporting auto-testing/grading of
programming assignments and distributing exercise sheets.
The infomark-server is the REST api backend for the course distributing system.
Complete documentation is available at https://infomark.org/.
`,
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
// serveCmd represents the serve command
var serveCmd = &cobra.Command{
Use: "serve",
Short: "start http server with configured api",
Short: "start the infomark RESTful JSON backend server with configured api",
Long: `Starts a http server and serves the configured api`,
Run: func(cmd *cobra.Command, args []string) {
server, err := api.NewServer()
Expand Down
12 changes: 9 additions & 3 deletions cmd/work.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,30 @@ import (
"log"

"github.com/cgtuebingen/infomark-backend/api"

"github.com/spf13/cobra"
)

var numWorkers = 1

var workCmd = &cobra.Command{
Use: "work",
Short: "start a worker",
Long: `Starts a background worker which will use docker to test submissions`,
Long: `Starts a background worker which will use docker to test submissions.
Can be used with the flag "-n" to start multiple workers within one process.
`,
Run: func(cmd *cobra.Command, args []string) {

worker, err := api.NewWorker()
worker, err := api.NewWorker(numWorkers)
if err != nil {
log.Fatal(err)
}
worker.Start()

},
}

func init() {

workCmd.Flags().IntVarP(&numWorkers, "number", "n", 1, "number of workers within one routine")
RootCmd.AddCommand(workCmd)
}
7 changes: 6 additions & 1 deletion service/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,22 @@ type Consumer struct {
channel *amqp.Channel
done chan error

instanceID int

handleFunc func(body []byte) error
}

// NewConsumer creates new consumer which can act on AMPQ messages
func NewConsumer(cfg *Config, handleFunc func(body []byte) error) (*Consumer, error) {
func NewConsumer(cfg *Config, handleFunc func(body []byte) error, instanceID int) (*Consumer, error) {

consumer := &Consumer{
conn: nil,
channel: nil,
done: make(chan error),
handleFunc: handleFunc,

instanceID: instanceID,

Config: cfg,
}

Expand All @@ -55,6 +59,7 @@ func NewConsumer(cfg *Config, handleFunc func(body []byte) error) (*Consumer, er
func (c *Consumer) Setup() (<-chan amqp.Delivery, error) {
logger := log.WithFields(logrus.Fields{
// "connection": c.Config.Connection,
"instance": c.instanceID,
"exchange": c.Config.Exchange,
"exchangetype": c.Config.ExchangeType,
"queue": c.Config.Queue,
Expand Down
47 changes: 47 additions & 0 deletions symbol/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// InfoMark - a platform for managing courses with
// distributing exercise sheets and testing exercise submissions
// Copyright (C) 2019 ComputerGraphics Tuebingen
// Authors: Patrick Wieschollek
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package symbol

import (
"github.com/coreos/go-semver/semver"
)

var (
// GitCommit is the git commit that was compiled
GitCommit string = "YXZ"
// VersionMajor is for an API incompatible changes.
VersionMajor int64 = 0
// VersionMinor is for functionality in a backwards-compatible manner.
VersionMinor int64 = 0
// VersionPatch is for backwards-compatible bug fixes.
VersionPatch int64 = 1
// VersionPre indicates prerelease.
VersionPre = "beta"
// VersionDev indicates development branch. Releases will be empty string.
VersionDev string
)

// Version is the specification version that the package types support.
var Version = semver.Version{
Major: VersionMajor,
Minor: VersionMinor,
Patch: VersionPatch,
PreRelease: semver.PreRelease(VersionPre),
Metadata: VersionDev,
}

0 comments on commit 939c14b

Please sign in to comment.