Integration test is now working.

* More work on the integration test
* Correctly get supervisor IP
* Use Fatal for test errors
* test-integration working separate from run-supervisor
* Use jenkins' JOB_NAME to identify and remove containers with their volumes
* Document testing procedure
* Document the assume-unchanged tip
* Use /mnt/root for data path
* Nicer secret assignment
This commit is contained in:
Pablo Carranza Vélez 2015-07-28 21:10:17 +00:00
parent 513cc24d1c
commit 53668647c8
12 changed files with 149 additions and 45 deletions

View File

@ -5,6 +5,7 @@ ARCH = rpi# rpi/amd64/i386/armv7hf
DEPLOY_REGISTRY = registry.resindev.io/
SUPERVISOR_VERSION = master
JOB_NAME = 1
all: supervisor
@ -51,16 +52,24 @@ deploy: supervisor
docker push $(DEPLOY_REGISTRY)$(IMAGE)
go-builder:
-cp tools/dind/config.json .
docker build -f Dockerfile.gosuper -t resin/go-supervisor-builder:$(SUPERVISOR_VERSION) .
-rm config.json
gosuper: go-builder
-mkdir -p gosuper/bin
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)
-docker rm --volumes -f resin_build_gosuper_$(JOB_NAME)
docker run --name resin_build_gosuper_$(JOB_NAME) -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 rm --volumes -f resin_build_gosuper_$(JOB_NAME)
test-gosuper: go-builder
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"
-docker rm --volumes -f resin_test_gosuper_$(JOB_NAME)
docker run --name resin_test_gosuper_$(JOB_NAME) -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"
docker rm --volumes -f resin_test_gosuper_$(JOB_NAME)
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"
test-integration: go-builder
-docker rm --volumes -f resin_test_integration_$(JOB_NAME)
docker run --name resin_test_integration_$(JOB_NAME) --net=host -e SUPERVISOR_IP="$(shell docker inspect --format '{{ .NetworkSettings.IPAddress }}' resin_supervisor_1)" -v $(shell pwd)/gosuper/bin:/usr/src/app/bin --volumes-from resin_supervisor_1 resin/go-supervisor-builder:$(SUPERVISOR_VERSION) bash -c "cd src/resin-supervisor && go test -v ./supertest"
docker rm --volumes -f resin_test_integration_$(JOB_NAME)
.PHONY: supervisor deploy supervisor-dind run-supervisor

View File

@ -20,6 +20,11 @@ This file can be obtained in several ways, for instance:
* Download an Intel Edison image from staging, open `config.img` with an archive tool like [peazip](http://sourceforge.net/projects/peazip/files/)
* Download a Raspberry Pi 2 image, flash it to an SD card, then mount partition 5 (resin-conf).
Tip: to avoid git marking config.json as modified, you can run:
```bash
git update-index --assume-unchanged tools/dind/config.json
```
## Start the supervisor instance
```bash
make ARCH=amd64 run-supervisor
@ -78,3 +83,20 @@ And we save it to Godeps.json with:
godep save -r ./...
```
(The -r switch will modify the import statement to use Godep's `_workspace`)
## Testing
# Gosuper
The Go supervisor can be tested by running:
```bash
make ARCH=amd64 test-gosuper
```
The test suite is at [gosuper/main_test.go](./gosuper/main_test.go).
# Integration test
The integration test tests the supervisor API by hitting its endpoints. To run it, first run the supervisor as explained in the first section of this document.
Once it's running, you can run the test with:
```bash
make ARCH=amd64 test-integration
```
The tests will fail if the supervisor API is down - bear in mind that the supervisor image takes a while to start the actual supervisor program, so you might have to wait a few minutes between running the supervisor and testing it.
The test expects the supervisor to be already running the application (so that the app is already on the SQLite database), so check the dashboard to see if the app has already downloaded.

View File

@ -9,8 +9,8 @@ ESCAPED_BRANCH_NAME=$(echo $sourceBranch | sed 's/[^a-z0-9A-Z_.-]/-/g')
docker pull resin/${ARCH}-supervisor:${ESCAPED_BRANCH_NAME} || docker pull resin/${ARCH}-supervisor:master || true
# Test the gosuper
make SUPERVISOR_VERSION=${VERSION} test-gosuper
make SUPERVISOR_VERSION=${VERSION} JOB_NAME=${JOB_NAME} test-gosuper
# Build the images
make SUPERVISOR_VERSION=${ESCAPED_BRANCH_NAME} ARCH=${ARCH} DEPLOY_REGISTRY= deploy
make SUPERVISOR_VERSION=${VERSION} ARCH=${ARCH} DEPLOY_REGISTRY= deploy
make SUPERVISOR_VERSION=${ESCAPED_BRANCH_NAME} ARCH=${ARCH} JOB_NAME=${JOB_NAME} DEPLOY_REGISTRY= deploy
make SUPERVISOR_VERSION=${VERSION} ARCH=${ARCH} JOB_NAME=${JOB_NAME} DEPLOY_REGISTRY= deploy

View File

@ -3,7 +3,7 @@
set -e
if [ -z "$GOSUPER_SOCKET" ]; then
GOSUPER_SOCKET=/var/run/gosuper.sock
export GOSUPER_SOCKET=/var/run/gosuper.sock
fi
[ -d /dev/net ] ||

View File

@ -27,7 +27,7 @@ func main() {
}
*/
ResinDataPath = "/resin-data/"
ResinDataPath = "/mnt/root/resin-data/"
listenAddress := os.Getenv("GOSUPER_SOCKET")
r := mux.NewRouter()
@ -36,10 +36,12 @@ func main() {
apiv1.HandleFunc("/purge", PurgeHandler).Methods("POST")
fmt.Println("Going to listen on " + listenAddress)
listener, err := net.Listen("unix", listenAddress)
if err != nil {
log.Fatalf("Could not listen on "+listenAddress+": %v", err)
}
fmt.Println("Starting HTTP server")
err = http.Serve(listener, r)
if err != nil {
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", "/v1/purge", strings.NewReader(`{"applicationId": "`+appId+`"}`))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
w := httptest.NewRecorder()
@ -25,6 +25,11 @@ func TestPurge(t *testing.T) {
t.Error("Could not create test directory for purge")
}
err = ioutil.WriteFile(dataPath+"/test", []byte("test"), 777)
if err != nil {
t.Error("Could not create test file for purge")
}
PurgeHandler(w, req)
if w.Code != http.StatusOK {

View File

@ -73,7 +73,7 @@ module.exports = (secret) ->
throw new Error('App not found')
application.kill(app)
.then ->
request.post(config.gosuperAddress + '/v1/purge', {json: true, body: applicationId: appId}).on 'response', ->
request.post config.gosuperAddress + '/v1/purge', {json: true, body: applicationId: appId}, ->
application.start(app)
.pipe(res)

View File

@ -36,8 +36,7 @@ knex.init.then ->
console.log('Starting API server..')
secret = randomstring.generate()
secret = config.forceApiSecret if config.forceApiSecret?
secret = config.forceApiSecret ? randomstring.generate()
api(secret).listen(config.listenPort)
# Let API know what version we are, and our api connection info.

96
supertest/super_test.go Normal file
View File

@ -0,0 +1,96 @@
package supertest
import (
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
gosuper "resin-supervisor/gosuper"
)
func TestPing(t *testing.T) {
supervisorIP := os.Getenv("SUPERVISOR_IP")
if supervisorIP == "" {
t.Fatal("Supervisor IP not set - is it running?")
}
address := "http://" + supervisorIP + ":48484"
request, err := http.NewRequest("GET", address+"/ping?apikey=bananas", nil)
if err != nil {
t.Fatal(err)
}
res, err := http.DefaultClient.Do(request)
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 200 {
t.Fatalf("Expected 200, got %d", res.StatusCode)
}
}
func TestPurge(t *testing.T) {
supervisorIP := os.Getenv("SUPERVISOR_IP")
if supervisorIP == "" {
t.Fatal("Supervisor IP not set - is it running?")
}
address := "http://" + supervisorIP + ":48484"
gopath := os.Getenv("GOPATH")
config, err := gosuper.ReadConfig(gopath + "/src/resin-supervisor/config.json")
if err != nil {
t.Fatal(err)
}
appId := config.ApplicationId
dataPath := "/resin-data/" + appId
err = ioutil.WriteFile(dataPath+"/test", []byte("test"), 777)
if err != nil {
t.Error("Could not create test file for purge")
}
request, err := http.NewRequest("POST", address+"/v1/purge?apikey=bananas", strings.NewReader(`{"appId": "`+appId+`"}`))
request.Header.Set("Content-Type", "application/json")
if err != nil {
t.Fatal(err)
}
client := &http.Client{}
res, err := client.Do(request)
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 200 {
t.Fatalf("Expected 200, got %d", res.StatusCode)
}
defer res.Body.Close()
contents, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
if !strings.EqualFold(string(contents), `{"Status":"OK","Error":""}`) {
t.Errorf("Purge response didn't match the expected JSON, got: %s", contents)
}
dirContents, err := ioutil.ReadDir(dataPath)
if err != nil {
t.Errorf("Could not read the data path after purge: %s", err)
}
if len(dirContents) > 0 {
t.Error("Data directory not empty after purge")
}
}

View File

@ -1,32 +0,0 @@
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)
}
}

View File

@ -1,6 +1,8 @@
FROM resin/resin-base:1acfee1
VOLUME /var/lib/docker
VOLUME /resin-data
ENV DOCKER_VERSION 1.6.2
# From get.docker.com script

View File

@ -28,6 +28,7 @@ ExecStart=/usr/bin/docker run --rm --privileged --name resin_supervisor \
-e "LED_FILE=${LED_FILE}" \
-e "LISTEN_PORT=${LISTEN_PORT}" \
-e "SUPERVISOR_IMAGE=${SUPERVISOR_IMAGE}" \
-e "RESIN_SUPERVISOR_SECRET=${RESIN_SUPERVISOR_SECRET}" \
${SUPERVISOR_IMAGE}
TimeoutStartSec=0
Restart=always