Skip to content

Commit

Permalink
Merge branch 'test'
Browse files Browse the repository at this point in the history
# Conflicts:
#	glide.yaml
  • Loading branch information
fgschwan committed Oct 5, 2017
2 parents f0ce5e6 + 36cb7f2 commit daed210
Show file tree
Hide file tree
Showing 187 changed files with 12,549 additions and 10,615 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ before_install:
- sudo add-apt-repository ppa:masterminds/glide -y
- sudo apt-get update -q
- sudo apt-get install glide -y
- go get github.com/mattn/goveralls

script:
- make all

after_success:
- goveralls -coverprofile=/tmp/coverage.out -service=travis-ci
34 changes: 32 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
include Makeroutines.mk

COVER_DIR=/tmp/

# fix sirupsen/Sirupsen problem
define fix_sirupsen_case_sensitivity_problem
@echo "# fixing sirupsen case sensitivity problem, please wait ..."
@-rm -rf vendor/github.com/Sirupsen
@-find ./ -type f -name "*.go" -exec sed -i -e 's/github.com\/Sirupsen\/logrus/github.com\/sirupsen\/logrus/g' {} \;
endef


# run all tests with coverage
define test_cover_only
@echo "# running unit tests with coverage analysis"
@go test -covermode=count -coverprofile=${COVER_DIR}coverage_unit1.out ./bgp
@go test -covermode=count -coverprofile=${COVER_DIR}coverage_unit2.out ./bgp/gobgp
@echo "# merging coverage results"
@gocovmerge ${COVER_DIR}coverage_unit1.out ${COVER_DIR}coverage_unit2.out > ${COVER_DIR}coverage.out
@echo "# coverage data generated into ${COVER_DIR}coverage.out"
@echo "# done"
endef

# build all binaries
build:
@echo "# building"
Expand All @@ -17,6 +31,7 @@ build:
get-tools:
@go get -u -f "github.com/alecthomas/gometalinter"
@gometalinter --install
@go get -u -f "github.com/wadey/gocovmerge"

# install dependencies
install-dep:
Expand All @@ -33,9 +48,23 @@ update-dep:
# run checkstyle
checkstyle:
@echo "# running code analysis"
@gometalinter --vendor --exclude=vendor --deadline 1m --enable-gc --disable=aligncheck --disable=gotype --exclude=mock ./...
@gometalinter --vendor --exclude=vendor --deadline 1m --enable-gc --disable=aligncheck --disable=gotype --disable=gotypex --exclude=mock ./...
@echo "# done"

# run all tests
test:
@echo "# running unit tests"
@go test $$(go list ./... | grep -v /vendor/)

# run tests with coverage report
test-cover:
$(call test_cover_only)

# get coverage percentage in console(without report)
test-cover-without-report:
@echo "# getting test coverage"
@go test -cover $$(go list ./... | grep -v /vendor/)

# build examples
build-examples:
@echo "# building plugin examples"
Expand Down Expand Up @@ -68,8 +97,9 @@ all:
@make update-dep
@make checkstyle
@make build
@make test-cover
@make build-examples
@make run-examples
@make clean

.PHONY: build install-dep update-dep checkstyle coverage clean all run-examples
.PHONY: build install-dep update-dep checkstyle coverage clean run-examples test test-cover-without-report
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Ligato BGP Agent
[![Build Status](https://travis-ci.org/ligato/bgp-agent.svg?branch=master)](https://travis-ci.org/ligato/bgp-agent)
[![Coverage Status](https://coveralls.io/repos/github/ligato/bgp-agent/badge.svg?branch=master)](https://coveralls.io/github/ligato/bgp-agent?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/ligato/bgp-agent)](https://goreportcard.com/report/github.com/ligato/bgp-agent)
[![GoDoc](https://godoc.org/github.com/ligato/bgp-agent?status.svg)](https://godoc.org/github.com/ligato/bgp-agent)
[![GitHub license](https://img.shields.io/badge/license-Apache%20license%202.0-blue.svg)](https://github.com/ligato/bgp-agent/blob/master/LICENSE)
Expand Down
42 changes: 42 additions & 0 deletions bgp/bgp_api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2017 Pantheon technologies s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bgp

import (
"github.com/ligato/cn-infra/logging/logrus"
. "github.com/onsi/gomega"
"net"
"reflect"
"testing"
)

// TestToChan tests ability of ToChan(...) function to create wrapping function that wraps given channel and forwards
// all ReachableIPRoute data (given to the wrapping function by parameter) to the channel inside.
// Test doesn't check logging capabilities of wrapping function.
func TestToChan(t *testing.T) {
// prepare of tested instances/helper instances
RegisterTestingT(t)
channel := make(chan ReachableIPRoute, 1)
wrappingFunc := ToChan(channel, logrus.DefaultLogger())

// testing ToChan's returned function
Expect(channel).To(BeEmpty())
sent := ReachableIPRoute{As: 1, Prefix: "1.2.3.4/32", Nexthop: net.IPv4(192, 168, 1, 1)}
wrappingFunc(&sent)
Expect(channel).ToNot(BeEmpty())
received := <-channel
reflect.DeepEqual(sent, received)
Expect(channel).To(BeEmpty())
}
244 changes: 244 additions & 0 deletions bgp/gobgp/plugin_impl_gobgp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Copyright (c) 2017 Pantheon technologies s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//Package gobgp_test contains Ligato GoBGP Plugin implementation tests
package gobgp_test

import (
"errors"
"github.com/ligato/bgp-agent/bgp"
"github.com/ligato/bgp-agent/bgp/gobgp"
"github.com/ligato/cn-infra/core"
"github.com/ligato/cn-infra/flavors/local"
"github.com/ligato/cn-infra/logging/logroot"
. "github.com/onsi/gomega"
"github.com/osrg/gobgp/config"
bgpPacket "github.com/osrg/gobgp/packet/bgp"
"github.com/osrg/gobgp/server"
"github.com/osrg/gobgp/table"
"sync"
"testing"
"time"
)

const (
nextHop1 string = "10.0.0.1"
nextHop2 string = "10.0.0.3"
prefix1 string = "10.0.0.0"
prefix2 string = "10.0.0.2"
length uint8 = 24
expectedReceivedAs = uint32(65000)
maxSessionEstablishment = 2 * time.Minute
timeoutForNotReceiving = 5 * time.Second
)

var flavor = &local.FlavorLocal{}

// TestGoBGPPluginInfoPassing tests gobgp plugin for the ability of retrieving of ReachableIPRoutes using BGP protocol and passing this information to its registered watchers.
// Test is also testing the ability of watch unregistering.
func TestGoBGPPluginInfoPassing(t *testing.T) {
// creating help variables/initialization
RegisterTestingT(t)
channel := make(chan bgp.ReachableIPRoute, 10)
var lifecycleWG sync.WaitGroup

// create components
routeReflector, err := createAndStartRouteReflector(routeReflectorConf)
Expect(err).To(BeNil(), "Can't create or start route reflector")
goBGPPlugin := createGoBGPPlugin(serverConf)

// watch -> start lifecycle of gobgp plugin -> send path to Route reflector -> check receiving on other end
watchRegistration, registrationErr := goBGPPlugin.WatchIPRoutes("TestWatcher", bgp.ToChan(channel, logroot.StandardLogger()))
Expect(registrationErr).To(BeNil(), "Can't properly register to watch IP routes")
Expect(watchRegistration).NotTo(BeNil(), "WatchRegistration must be non-nil to be able to close registration later")
lifecycleCloseChannel := startPluginLifecycle(goBGPPlugin, assertCorrectLifecycleEnd(&lifecycleWG))
Expect(waitForSessionEstablishment(routeReflector)).To(BeNil(), "Session not established within timeout")
Expect(addNewRoute(routeReflector, prefix1, nextHop1, length)).To(BeNil(), "Can't add new route")
assertThatChannelReceivesCorrectRoute(channel)

//unregister watching -> send another path to Route reflector -> check that nothing came to watcher
Expect(watchRegistration.Close()).To(BeNil(), "Closing resitration failed")
Expect(addNewRoute(routeReflector, prefix2, nextHop2, length)).To(BeNil(), "Can't add new route")
assertThatChannelDidntReceiveAnything(channel, t)

// stop all
close(lifecycleCloseChannel) //gives command to stop gobgp lifecycle
lifecycleWG.Wait() //waiting for real gobgp lifecycle stop (that means also for assertion of correct closing (assertCorrectLifecycleEnd(...))
Expect(routeReflector.Stop()).To(BeNil())
}

// assertCorrectLifecycleEnd asserts that lifecycle controlled by agent finished without error.
// This method is also used for synchronizing test run with lifecycle run, that means that above mentioned assertion happens within duration of test.
func assertCorrectLifecycleEnd(lifecycleWG *sync.WaitGroup) func(err error) {
lifecycleWG.Add(1) // adding 1 to waitgroup before lifecycle loop starts in another go routine
return func(err error) { // called insided lifecycle go routine just after ending the lifecycle
Expect(err).To(BeNil())
lifecycleWG.Done()
}
}

// startPluginLifecycle creates cn-infra agent and use it to start lifecycle for passed gobgp plugin.
// Lambda parameter assertCorrectLifecycleEnd is used to verify correct lifecycle loop end and for synchronization
// purposes so that the verification happens in the duration of tests (another go routine can be kill without performing
// needed assertions). Returned channel can be use to stop agent's lifecycle loop.
func startPluginLifecycle(plugin *gobgp.Plugin, assertCorrectLifecycleEnd func(err error)) chan struct{} {
agent := core.NewAgent(logroot.StandardLogger(), 1*time.Minute, []*core.NamedPlugin{{plugin.PluginName, plugin}}...)

closeChannel := make(chan struct{}, 1)
go func() {
assertCorrectLifecycleEnd(core.EventLoopWithInterrupt(agent, closeChannel))
}()
return closeChannel
}

// createGoBGPPlugin creates basic gobgp plugin
func createGoBGPPlugin(bgpConfig *config.Bgp) *gobgp.Plugin {
return gobgp.New(gobgp.Deps{
PluginInfraDeps: *flavor.InfraDeps("TestGoBGP", local.WithConf()),
SessionConfig: bgpConfig})
}

// assertThatChannelReceivesCorrectRoute waits for received route and then checks it for correctness
func assertThatChannelReceivesCorrectRoute(channel chan bgp.ReachableIPRoute) {
receivedRoute := <-channel
logroot.StandardLogger().Println(receivedRoute)
logroot.StandardLogger().Debug("Agent received new route ", receivedRoute)

Expect(receivedRoute.As).To(Equal(expectedReceivedAs))
Expect(receivedRoute.Nexthop.String()).To(Equal(nextHop1))
Expect(receivedRoute.Prefix).To(Equal(prefix1 + "/24"))
}

// assertThatChannelDidntReceiveAnything waits for given time (timeoutForNotReceiving constant) and if in that time period data flows in data channel, then it fails the test.
func assertThatChannelDidntReceiveAnything(dataChan chan bgp.ReachableIPRoute, t *testing.T) {
timeChan := time.NewTimer(timeoutForNotReceiving).C
for {
select {
case <-timeChan:
return
case route := <-dataChan:
t.Fatal("Channel did receive route even if it should not. Route received: ", route)
}
}
}

// createAndStartRouteReflector creates and starts Route Reflector
func createAndStartRouteReflector(bgpConfig *config.Bgp) (*server.BgpServer, error) {
bgpServer := server.NewBgpServer()
go bgpServer.Serve()

if err := bgpServer.Start(&bgpConfig.Global); err != nil {
stopFaultyBgpServer(bgpServer)
return nil, err
}
if err := bgpServer.AddNeighbor(&bgpConfig.Neighbors[0]); err != nil {
stopFaultyBgpServer(bgpServer)
return nil, err
}

return bgpServer, nil
}

// stopFaultyBgpServer stops BgpServer that already does not correctly work. Possible error from stopping of server is
// not returned because root of the problem lies in previous detection of incorrect behaviour.
func stopFaultyBgpServer(bgpServer *server.BgpServer) {
if err := bgpServer.Stop(); err != nil {
logroot.StandardLogger().Error("error stoping server", err)
}

}

// addNewRoute adds route to BgpServer
func addNewRoute(server *server.BgpServer, prefix string, nextHop string, length uint8) error {
attrs := []bgpPacket.PathAttributeInterface{
bgpPacket.NewPathAttributeOrigin(0),
bgpPacket.NewPathAttributeNextHop(nextHop),
}

_, err := server.AddPath("",
[]*table.Path{table.NewPath(
nil,
bgpPacket.NewIPAddrPrefix(length, prefix),
false,
attrs,
time.Now(),
false),
},
)
return err
}

// waitForSessionEstablishment waits until it is possible to work with server correctly after start. Many commands depends on session being correctly established.
func waitForSessionEstablishment(server *server.BgpServer) error {
timeChan := time.NewTimer(maxSessionEstablishment).C
for {
select {
case <-timeChan:
return errors.New("timer expired")
default:
time.Sleep(time.Second)
if server.GetNeighbor("", false)[0].State.SessionState == config.SESSION_STATE_ESTABLISHED {
return nil
}
logroot.StandardLogger().Debug("Waiting for session establishment")
}
}
}

var (
serverConf = &config.Bgp{
Global: config.Global{
Config: config.GlobalConfig{
As: 65001,
RouterId: "172.18.0.2",
Port: -1,
},
},
Neighbors: []config.Neighbor{
config.Neighbor{
Config: config.NeighborConfig{
PeerAs: 65000,
NeighborAddress: "127.0.0.1",
},
Transport: config.Transport{
Config: config.TransportConfig{
RemotePort: 10179,
},
},
},
},
}
routeReflectorConf = &config.Bgp{
Global: config.Global{
Config: config.GlobalConfig{
As: 65000,
RouterId: "172.18.0.254",
Port: 10179,
},
},
Neighbors: []config.Neighbor{
config.Neighbor{
Config: config.NeighborConfig{
PeerAs: 65001,
NeighborAddress: "127.0.0.1",
},
Transport: config.Transport{
Config: config.TransportConfig{
PassiveMode: true,
},
},
},
},
}
)
Loading

0 comments on commit daed210

Please sign in to comment.