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
This commit is contained in:
Pablo Carranza Vélez 2015-07-24 20:59:58 +00:00
parent dd53bbd1db
commit 513cc24d1c
11 changed files with 84 additions and 16 deletions

View File

@ -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

View File

@ -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

View File

@ -2,7 +2,7 @@
set -e
if [ -z $GOSUPER_SOCKET ]; then
if [ -z "$GOSUPER_SOCKET" ]; then
GOSUPER_SOCKET=/var/run/gosuper.sock
fi

View File

@ -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)
}
}

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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:

32
supertest/supertest.go Normal file
View File

@ -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)
}
}