mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-01-29 15:44:13 +00:00
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:
parent
513cc24d1c
commit
53668647c8
17
Makefile
17
Makefile
@ -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
|
||||
|
22
README.md
22
README.md
@ -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.
|
||||
|
@ -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
|
||||
|
2
entry.sh
2
entry.sh
@ -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 ] ||
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
96
supertest/super_test.go
Normal 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")
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user