From 513cc24d1cc2066710682369eeaa89d4ff509f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Fri, 24 Jul 2015 20:59:58 +0000 Subject: [PATCH] Purge restarts app, and first attempt at integration testing * Restart app when purging * Use log.Fatal to exit with status 1 * Quotes in entry.sh * Use JSON for request body * Handle errors for parseJsonBody * Better error printing in main * First attempt at testing nodesuper from Go * Cleaner build * Use ARCH to differentiate concurrent tests/builds * Use --rm to autoremove containers --- Makefile | 7 +++++-- build_gosuper.sh | 2 +- entry.sh | 2 +- gosuper/main.go | 13 ++++++------- gosuper/main_test.go | 2 +- gosuper/purge.go | 20 ++++++++++++++++++-- src/api.coffee | 17 ++++++++++++++++- src/app.coffee | 2 ++ src/application.coffee | 2 +- src/config.coffee | 1 + supertest/supertest.go | 32 ++++++++++++++++++++++++++++++++ 11 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 supertest/supertest.go diff --git a/Makefile b/Makefile index ce0490e9..5fa887bb 100644 --- a/Makefile +++ b/Makefile @@ -55,9 +55,12 @@ go-builder: gosuper: go-builder -mkdir -p gosuper/bin - docker run -v $(shell pwd)/gosuper/bin:/usr/src/app/bin -e USER_ID=$(shell id -u) -e GROUP_ID=$(shell id -g) -e GOARCH=$(GOARCH) resin/go-supervisor-builder:$(SUPERVISOR_VERSION) + docker run --rm -v $(shell pwd)/gosuper/bin:/usr/src/app/bin -e USER_ID=$(shell id -u) -e GROUP_ID=$(shell id -g) -e GOARCH=$(GOARCH) resin/go-supervisor-builder:$(SUPERVISOR_VERSION) test-gosuper: go-builder - docker run -v $(shell pwd)/gosuper/bin:/usr/src/app/bin resin/go-supervisor-builder:$(SUPERVISOR_VERSION) bash -c "cd src/resin-supervisor && go test -v ./..." + docker run --rm -v $(shell pwd)/gosuper/bin:/usr/src/app/bin resin/go-supervisor-builder:$(SUPERVISOR_VERSION) bash -c "cd src/resin-supervisor && go test -v ./gosuper" + +test-integration: run-supervisor go-builder + docker run --rm --net=host -e SUPERVISOR_IP="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' resin_supervisor_1)" -v $(shell pwd)/gosuper/bin:/usr/src/app/bin resin/go-supervisor-builder:$(SUPERVISOR_VERSION) bash -c "cd src/resin-supervisor && go test -v ./supertest" .PHONY: supervisor deploy supervisor-dind run-supervisor diff --git a/build_gosuper.sh b/build_gosuper.sh index 5cef11a6..30d99a9d 100644 --- a/build_gosuper.sh +++ b/build_gosuper.sh @@ -1,6 +1,6 @@ #!/bin/bash -go install -a -v ./... +go install -a -v ./gosuper RETURN_VALUE=$? # For consistency, always keep the binary within a linux_$GOARCH folder diff --git a/entry.sh b/entry.sh index a9952eaf..33fefec6 100755 --- a/entry.sh +++ b/entry.sh @@ -2,7 +2,7 @@ set -e -if [ -z $GOSUPER_SOCKET ]; then +if [ -z "$GOSUPER_SOCKET" ]; then GOSUPER_SOCKET=/var/run/gosuper.sock fi diff --git a/gosuper/main.go b/gosuper/main.go index 51d5a6f5..b3f9d1ee 100644 --- a/gosuper/main.go +++ b/gosuper/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "net" "net/http" "os" @@ -22,12 +23,12 @@ func main() { var err error Config, err = ReadConfig("/boot/config.json") if err != nil { - return + log.Fatalf("Could not read configuration file: %v", err) } */ ResinDataPath = "/resin-data/" - laddr := os.Getenv("GOSUPER_SOCKET") + listenAddress := os.Getenv("GOSUPER_SOCKET") r := mux.NewRouter() r.HandleFunc("/ping", pingHandler) @@ -35,14 +36,12 @@ func main() { apiv1.HandleFunc("/purge", PurgeHandler).Methods("POST") - listener, err := net.Listen("unix", laddr) + listener, err := net.Listen("unix", listenAddress) if err != nil { - fmt.Println("Could not listen on " + laddr) - return + log.Fatalf("Could not listen on "+listenAddress+": %v", err) } err = http.Serve(listener, r) if err != nil { - fmt.Println("Could not start HTTP server") - return + log.Fatalf("Could not start HTTP server: %v", err) } } diff --git a/gosuper/main_test.go b/gosuper/main_test.go index 29b06cca..899448e9 100644 --- a/gosuper/main_test.go +++ b/gosuper/main_test.go @@ -11,7 +11,7 @@ import ( func TestPurge(t *testing.T) { appId := "1" - req, _ := http.NewRequest("POST", "/api/v1/purge", strings.NewReader("applicationId="+appId)) + req, _ := http.NewRequest("POST", "/api/v1/purge", strings.NewReader(`{"applicationId": "`+appId+`"}`)) req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") w := httptest.NewRecorder() diff --git a/gosuper/purge.go b/gosuper/purge.go index 84bd3b7b..c04cd710 100644 --- a/gosuper/purge.go +++ b/gosuper/purge.go @@ -13,6 +13,10 @@ type ApiResponse struct { Error string } +type PurgeBody struct { + ApplicationId string +} + func jsonResponse(w http.ResponseWriter, response interface{}, status int) { j, _ := json.Marshal(response) w.Header().Set("Content-Type", "application/json") @@ -20,17 +24,29 @@ func jsonResponse(w http.ResponseWriter, response interface{}, status int) { w.Write(j) } +func parseJsonBody(r *http.Request, dest interface{}) error { + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&dest) + return err +} + func PurgeHandler(w http.ResponseWriter, r *http.Request) { fmt.Println("Purging /data") + var body PurgeBody + err := parseJsonBody(r, &body) + if err != nil { + jsonResponse(w, ApiResponse{"Error", "Invalid request"}, 422) + return + } - appId := r.FormValue("applicationId") + appId := body.ApplicationId if appId == "" { jsonResponse(w, ApiResponse{"Error", "applicationId is required"}, 422) return } // Validate that the appId is an integer - _, err := strconv.ParseInt(appId, 10, 0) + _, err = strconv.ParseInt(appId, 10, 0) if err != nil { jsonResponse(w, ApiResponse{"Error", "Invalid applicationId"}, 422) return diff --git a/src/api.coffee b/src/api.coffee index eeeaaa5a..84b1dfaf 100644 --- a/src/api.coffee +++ b/src/api.coffee @@ -18,6 +18,9 @@ module.exports = (secret) -> else res.sendStatus(401) + api.get '/ping', (req, res) -> + res.send('OK') + api.post '/v1/blink', (req, res) -> utils.mixpanelTrack('Device blink') utils.blink.pattern.start() @@ -60,6 +63,18 @@ module.exports = (secret) -> res.status(503).send(err?.message or err or 'Unknown error') api.post '/v1/purge', (req, res) -> - request.post(config.gosuperAddress + '/v1/purge', req.body).pipe(res) + appId = req.body.appId + utils.mixpanelTrack('Purge /data', appId) + if !appId? + return res.status(400).send('Missing app id') + knex('app').select().where({appId}) + .then ([ app ]) -> + if !app? + throw new Error('App not found') + application.kill(app) + .then -> + request.post(config.gosuperAddress + '/v1/purge', {json: true, body: applicationId: appId}).on 'response', -> + application.start(app) + .pipe(res) return api diff --git a/src/app.coffee b/src/app.coffee index abd5ac40..d49a58a2 100644 --- a/src/app.coffee +++ b/src/app.coffee @@ -35,7 +35,9 @@ knex.init.then -> randomstring = require 'randomstring' console.log('Starting API server..') + secret = randomstring.generate() + secret = config.forceApiSecret if config.forceApiSecret? api(secret).listen(config.listenPort) # Let API know what version we are, and our api connection info. diff --git a/src/application.coffee b/src/application.coffee index de402776..2b75e6fb 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -82,7 +82,7 @@ logSystemEvent = (logType, app, error) -> utils.mixpanelTrack(logType.eventName, {app, error}) return -kill = (app) -> +exports.kill = kill = (app) -> logSystemEvent(logTypes.stopApp, app) device.updateState(status: 'Stopping') container = docker.getContainer(app.containerId) diff --git a/src/config.coffee b/src/config.coffee index c689cb2e..b3914148 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -24,6 +24,7 @@ module.exports = config = restartSuccessTimeout: checkInt(process.env.RESTART_SUCCESS_TIMEOUT) ? 60000 appUpdatePollInterval: checkInt(process.env.APPLICATION_UPDATE_POLL_INTERVAL) ? 60000 successMessage: 'SUPERVISOR OK' + forceApiSecret: process.env.RESIN_SUPERVISOR_SECRET ? null config.supervisorContainer = Volumes: diff --git a/supertest/supertest.go b/supertest/supertest.go new file mode 100644 index 00000000..78403e10 --- /dev/null +++ b/supertest/supertest.go @@ -0,0 +1,32 @@ +package supertest + +import ( + "net/http" + "os" + "testing" +) + +func TestPing(t *testing.T) { + supervisorIP := os.getEnv("SUPERVISOR_IP") + if supervisorIP == "" { + t.Error("Supervisor IP not set") + } + + address := "http://"+ supervisorIP +":48484" + + request, err := http.NewRequest("GET", address+"/ping?apikey=bananas", nil) + + if err != nil { + t.Error(err) + } + + res, err := http.DefaultClient.Do(request) + + if err != nil { + t.Error(err) + } + + if res.StatusCode != 200 { + t.Errorf("Expected 200, got %d", res.StatusCode) + } +}