diff --git a/Makefile b/Makefile index 37c7871c..2a0f741e 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,7 @@ WEAVE_EXPORT=weave.tar WEAVEEXEC_DOCKER_VERSION=1.3.1 DOCKER_DISTRIB=prog/weaveexec/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/x86_64/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz +COVERAGE_MODULES=./common,./common/docker,./ipam,./ipam/paxos,./ipam/ring,./ipam/space,./net,./net/address,./router all: $(WEAVE_EXPORT) @@ -43,7 +44,25 @@ travis: $(EXES) update: go get -u -f -v -tags -netgo $(addprefix ./,$(dir $(EXES))) -$(WEAVER_EXE) $(WEAVEDNS_EXE) $(WEAVEPROXY_EXE) $(NETCHECK_EXE): common/*.go common/*/*.go net/*.go +$(WEAVER_EXE): common/*.go common/*/*.go net/*.go +ifeq ($(COVERAGE),true) + go get -t -tags netgo ./$(@D) + go test -c -o ./$@ -ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)" \ + -tags netgo -v -covermode=atomic -coverpkg $(COVERAGE_MODULES) ./$(@D)/ +else + go get -tags netgo ./$(@D) + go build -ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)" -tags netgo -o $@ ./$(@D) +endif + @strings $@ | grep cgo_stub\\\.go >/dev/null || { \ + rm $@; \ + echo "\nYour go standard library was built without the 'netgo' build tag."; \ + echo "To fix that, run"; \ + echo " sudo go clean -i net"; \ + echo " sudo go install -tags netgo std"; \ + false; \ + } + +$(WEAVEDNS_EXE) $(WEAVEPROXY_EXE) $(NETCHECK_EXE): common/*.go common/*/*.go net/*.go go get -tags netgo ./$(@D) go build -ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)" -tags netgo -o $@ ./$(@D) @strings $@ | grep cgo_stub\\\.go >/dev/null || { \ @@ -96,7 +115,7 @@ $(DOCKER_DISTRIB): tests: echo "mode: count" > profile.cov fail=0 ; \ - for dir in $$(find . -type f -name '*_test.go' | xargs -n1 dirname | sort -u); do \ + for dir in $$(find . -type f -name '*_test.go' | xargs -n1 dirname | grep -v prog | sort -u); do \ go get -t -tags netgo $$dir ; \ output=$$(mktemp cover.XXXXXXXXXX) ; \ if ! go test -cpu 4 -tags netgo \ diff --git a/circle.yml b/circle.yml index 2a01b19f..5ac5cc3d 100644 --- a/circle.yml +++ b/circle.yml @@ -35,15 +35,22 @@ dependencies: test: override: - - docker run -v /var/run/docker.sock:/run/docker.sock -v /home/ubuntu:/home/go weaveworks/weave-build: + - docker run -v /var/run/docker.sock:/run/docker.sock -v /home/ubuntu:/home/go -e COVERAGE=true weaveworks/weave-build: parallel: true - cd $SRCDIR/test; ./gce.sh setup: parallel: true - cd $SRCDIR/test; . ./gce.sh hosts; ./setup.sh: parallel: true - - cd $SRCDIR/test; . ./gce.sh hosts; ./run_all.sh: + - cd $SRCDIR/test; . ./gce.sh hosts; export COVERAGE=true; ./run_all.sh: parallel: true timeout: 300 post: - cd $SRCDIR/test; ./gce.sh destroy: parallel: true + +teardown: + pre: + - test "$CIRCLE_NODE_INDEX" != "0" || (cd $SRCDIR/test; ./gen_coverage_reports.sh): + parallel: true + - test "$CIRCLE_NODE_INDEX" != "0" || (cd $SRCDIR/test; cp coverage.* $CIRCLE_ARTIFACTS): + parallel: true diff --git a/common/signals.go b/common/signals.go index e750a8ea..c2e2a683 100644 --- a/common/signals.go +++ b/common/signals.go @@ -15,12 +15,12 @@ type SignalReceiver interface { func SignalHandlerLoop(ss ...SignalReceiver) { sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGUSR1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGTERM) buf := make([]byte, 1<<20) for { switch <-sigs { - case syscall.SIGINT: - Log.Infof("=== received SIGINT ===\n*** exiting\n") + case syscall.SIGINT, syscall.SIGTERM: + Log.Infof("=== received SIGINT/SIGTERM ===\n*** exiting\n") for _, subsystem := range ss { subsystem.Stop() } diff --git a/prog/weaver/main.go b/prog/weaver/main.go index e35ac49c..04cf76c5 100644 --- a/prog/weaver/main.go +++ b/prog/weaver/main.go @@ -12,6 +12,7 @@ import ( "github.com/davecheney/profile" "github.com/docker/docker/pkg/mflag" "github.com/gorilla/mux" + . "github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common/docker" "github.com/weaveworks/weave/ipam" diff --git a/prog/weaver/main_test.go b/prog/weaver/main_test.go new file mode 100644 index 00000000..1873f4a3 --- /dev/null +++ b/prog/weaver/main_test.go @@ -0,0 +1,12 @@ +package main + +import ( + "testing" + + weavetest "github.com/weaveworks/weave/testing" +) + +func TestMain(t *testing.T) { + weavetest.TrimTestArgs() + main() +} diff --git a/test/140_weave_local_test.sh b/test/140_weave_local_test.sh index 4e392370..7912001a 100755 --- a/test/140_weave_local_test.sh +++ b/test/140_weave_local_test.sh @@ -4,15 +4,15 @@ start_suite "Run weave with --local" -run_on $HOST1 sudo weave --local reset +run_on $HOST1 sudo COVERAGE=$COVERAGE weave --local reset -run_on $HOST1 sudo weave --local launch --ipalloc-range 10.2.5.0/24 +run_on $HOST1 sudo COVERAGE=$COVERAGE weave --local launch --ipalloc-range 10.2.5.0/24 assert_raises "docker_on $HOST1 ps | grep weave" -run_on $HOST1 sudo weave --local run 10.2.6.5/24 -ti --name=c1 $SMALL_IMAGE /bin/sh +run_on $HOST1 sudo COVERAGE=$COVERAGE weave --local run 10.2.6.5/24 -ti --name=c1 $SMALL_IMAGE /bin/sh assert_raises "exec_on $HOST1 c1 $CHECK_ETHWE_UP" -run_on $HOST1 sudo weave --local run -ti --name=c2 $SMALL_IMAGE /bin/sh +run_on $HOST1 sudo COVERAGE=$COVERAGE weave --local run -ti --name=c2 $SMALL_IMAGE /bin/sh assert_raises "exec_on $HOST1 c2 $CHECK_ETHWE_UP" end_suite diff --git a/test/150_connect_forget_test.sh b/test/150_connect_forget_test.sh index 984e70e7..72130d74 100755 --- a/test/150_connect_forget_test.sh +++ b/test/150_connect_forget_test.sh @@ -33,7 +33,7 @@ assert_raises "exec_on $HOST1 c1 $PING $C2" # Forget everyone and disconnect assert_raises "weave_on $HOST2 forget $HOST1 $HOST2" -assert_raises "weave_on $HOST1 stop-router" +assert_raises "weave_stop_router $HOST1" assert_raises "weave_on $HOST1 launch-router" assert_peers $HOST2 "" assert_raises "exec_on $HOST1 c1 sh -c '! $PING $C2'" diff --git a/test/config.sh b/test/config.sh index 3b5e8e1a..542cb1df 100644 --- a/test/config.sh +++ b/test/config.sh @@ -113,6 +113,15 @@ weave_on() { DOCKER_HOST=tcp://$host:$DOCKER_PORT $WEAVE "$@" } +weave_stop_router() { + host=$1 + shift 1 + docker_on $host stop weave 1>/dev/null 2>&1 || true + if [ -n "$COVERAGE" ] ; then + collect_coverage $host + fi +} + exec_on() { host=$1 container=$2 @@ -196,6 +205,19 @@ start_suite() { end_suite() { whitely assert_end + for host in $HOSTS; do + weave_stop_router $host + done +} + +collect_coverage() { + host=$1 + rm -f cover.router.prof + docker_on $host cp weave:/home/weave/cover.router.prof . || true + + # ideally we'd know the name of the test here, and put that in the filename + mkdir -p ./coverage + mv cover.router.prof $(mktemp -u ./coverage/integration.XXXXXXXX) || true } WEAVE=$DIR/../weave diff --git a/test/gen_coverage_reports.sh b/test/gen_coverage_reports.sh new file mode 100755 index 00000000..b15248ab --- /dev/null +++ b/test/gen_coverage_reports.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -eux + +if [ -n "$CIRCLECI" ]; then + for i in $(seq 1 $(($CIRCLE_NODE_TOTAL - 1))); do + scp node$i:/home/ubuntu/src/github.com/weaveworks/weave/test/coverage/* ./coverage/ || true + done +fi + +go get github.com/weaveworks/weave/testing/cover +cover ./coverage/* >profile.cov +go tool cover -html=profile.cov -o coverage.html +go tool cover -func=profile.cov -o coverage.txt +tar czf coverage.tar.gz ./coverage diff --git a/testing/cover/cover.go b/testing/cover/cover.go new file mode 100644 index 00000000..4c5fcfd7 --- /dev/null +++ b/testing/cover/cover.go @@ -0,0 +1,97 @@ +package main + +import ( + "fmt" + "os" + "sort" + + "golang.org/x/tools/cover" +) + +func merge(p1, p2 *cover.Profile) *cover.Profile { + output := cover.Profile{ + FileName: p1.FileName, + Mode: p1.Mode, + } + + i, j := 0, 0 + for i < len(p1.Blocks) && j < len(p2.Blocks) { + bi, bj := p1.Blocks[i], p2.Blocks[j] + if bi.StartLine == bj.StartLine && bi.StartCol == bj.StartCol { + + if bi.EndLine != bj.EndLine || + bi.EndCol != bj.EndCol || + bi.NumStmt != bj.NumStmt { + panic("Not run on same source!") + } + + output.Blocks = append(output.Blocks, cover.ProfileBlock{ + StartLine: bi.StartLine, + StartCol: bi.StartCol, + EndLine: bi.EndLine, + EndCol: bi.EndCol, + NumStmt: bi.NumStmt, + Count: bi.Count + bj.Count, + }) + i++ + j++ + } else if bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol { + output.Blocks = append(output.Blocks, bi) + i++ + } else { + output.Blocks = append(output.Blocks, bj) + j++ + } + } + + for ; i < len(p1.Blocks); i++ { + output.Blocks = append(output.Blocks, p1.Blocks[i]) + } + + for ; j < len(p2.Blocks); j++ { + output.Blocks = append(output.Blocks, p2.Blocks[j]) + } + + return &output +} + +func print(profiles []*cover.Profile) { + fmt.Println("mode: atomic") + for _, profile := range profiles { + for _, block := range profile.Blocks { + fmt.Printf("%s:%d.%d,%d.%d %d %d\n", profile.FileName, block.StartLine, block.StartCol, + block.EndLine, block.EndCol, block.NumStmt, block.Count) + } + } +} + +// Copied from https://github.com/golang/tools/blob/master/cover/profile.go +type byFileName []*cover.Profile + +func (p byFileName) Len() int { return len(p) } +func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName } +func (p byFileName) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +func main() { + outputProfiles := map[string]*cover.Profile{} + for _, input := range os.Args[1:] { + inputProfiles, err := cover.ParseProfiles(input) + if err != nil { + panic(fmt.Sprintf("Error parsing %s: %v", input, err)) + } + for _, ip := range inputProfiles { + op := outputProfiles[ip.FileName] + if op == nil { + outputProfiles[ip.FileName] = ip + } else { + outputProfiles[ip.FileName] = merge(op, ip) + } + } + } + profiles := make([]*cover.Profile, 0, len(outputProfiles)) + for _, profile := range outputProfiles { + profiles = append(profiles, profile) + } + sort.Sort(byFileName(profiles)) + print(profiles) +} diff --git a/testing/util.go b/testing/util.go index 7ce98cb5..42f39cee 100644 --- a/testing/util.go +++ b/testing/util.go @@ -2,6 +2,7 @@ package testing import ( "fmt" + "os" "reflect" "runtime" "testing" @@ -50,3 +51,17 @@ func RunWithTimeout(t *testing.T, d time.Duration, f func()) { }() <-ch } + +// TrimTestArgs finds the first -- in os.Args and trim all args before that +func TrimTestArgs() { + i, l := 0, len(os.Args) + for ; i < l; i++ { + if os.Args[i] == "--" { + break + } + } + if i == l { + panic("Specify weave args after --") + } + os.Args = append(os.Args[:1], os.Args[i+1:l]...) +} diff --git a/weave b/weave index ba4f2f17..625e2ec0 100755 --- a/weave +++ b/weave @@ -86,6 +86,7 @@ exec_remote() { -e DOCKER_BRIDGE \ -e PROXY_HOST="$PROXY_HOST" \ -e WEAVE_CIDR=none \ + -e COVERAGE \ $WEAVEEXEC_DOCKER_ARGS $EXEC_IMAGE --local "$@" } @@ -412,7 +413,11 @@ ask_version() { echo "Unable to find $2 image." >&2 fi fi - [ -n "$DOCKERIMAGE" ] && docker run --rm -e WEAVE_CIDR=none $DOCKERIMAGE --version + COVERAGE_ARGS="" + if [ -n "$COVERAGE" -a "$1" = "$CONTAINER_NAME" ] ; then + COVERAGE_ARGS="-test.coverprofile=/home/weave/cover.router.prof --" + fi + [ -n "$DOCKERIMAGE" ] && docker run --rm -e WEAVE_CIDR=none $DOCKERIMAGE $COVERAGE_ARGS --version } ###################################################################### @@ -1037,6 +1042,10 @@ launch_router() { exit 1 fi fi + COVERAGE_ARGS="" + if [ -n "$COVERAGE" ] ; then + COVERAGE_ARGS="-test.coverprofile=/home/weave/cover.router.prof --" + fi # Set WEAVE_DOCKER_ARGS in the environment in order to supply # additional parameters, such as resource limits, to docker # when launching the weave container. @@ -1045,7 +1054,7 @@ launch_router() { -v /var/run/docker.sock:/var/run/docker.sock \ -e WEAVE_PASSWORD \ -e WEAVE_CIDR=none \ - $WEAVE_DOCKER_ARGS $IMAGE --iface $CONTAINER_IFNAME --port $CONTAINER_PORT --name "$PEERNAME" --nickname "$(hostname)" --ipalloc-range "$IPRANGE" "$@") + $WEAVE_DOCKER_ARGS $IMAGE $COVERAGE_ARGS --iface $CONTAINER_IFNAME --port $CONTAINER_PORT --name "$PEERNAME" --nickname "$(hostname)" --ipalloc-range "$IPRANGE" "$@") with_container_netns_or_die $CONTAINER launch wait_for_status $CONTAINER_NAME $HTTP_PORT if [ -n "$IPRANGE" ] ; then