Skip to content

Instantly share code, notes, and snippets.

@cstockton
Created June 20, 2018 16:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cstockton/5e1eb3164db518e7a923487ff6b73a78 to your computer and use it in GitHub Desktop.
Save cstockton/5e1eb3164db518e7a923487ff6b73a78 to your computer and use it in GitHub Desktop.
package deletetest
import (
"bytes"
"context"
"io"
"net"
"testing"
"time"
"github.com/osrg/gobgp/api"
"github.com/osrg/gobgp/packet/bgp"
"github.com/osrg/gobgp/table"
"google.golang.org/grpc"
)
func helpClient(ctx context.Context, tb testing.TB) gobgpapi.GobgpApiClient {
const testAddr = "localhost:51051"
conn, err := grpc.DialContext(ctx, testAddr,
grpc.WithInsecure(),
grpc.WithTimeout(time.Second*30),
)
if err != nil {
tb.Fatal(err)
}
return gobgpapi.NewGobgpApiClient(conn)
}
func TestAddGetDelSymmetry(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
cli := helpClient(ctx, t)
get := func(tb testing.TB, pfx string) (*gobgpapi.Path, error) {
stream, err := cli.GetPath(ctx, &gobgpapi.GetPathRequest{
Type: gobgpapi.Resource_GLOBAL,
Family: uint32(bgp.RF_IPv4_UC),
Prefixes: []*gobgpapi.TableLookupPrefix{
{
Prefix: pfx,
LookupOption: gobgpapi.TableLookupOption_LOOKUP_EXACT,
},
},
})
if err != nil {
tb.Fatal(err)
}
defer closeStream(stream)
p, err := stream.Recv()
if err != nil {
if err == io.EOF {
return nil, err
}
tb.Fatal(err)
}
return p, nil
}
// Setup a test by calling AddPath with the given Prefix and returning the
// added path.
setup := func(tb testing.TB, pfx bgp.AddrPrefixInterface, pi *table.PeerInfo) *gobgpapi.Path {
attrs := []bgp.PathAttributeInterface{
bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP),
bgp.NewPathAttributeNextHop("192.168.0.1"),
}
tblPath := table.NewPath(
pi, pfx, false, attrs, time.Now().In(time.UTC), false)
apiPath := gobgpapi.ToPathApi(tblPath, nil)
_, err := cli.AddPath(ctx, &gobgpapi.AddPathRequest{
Resource: gobgpapi.Resource_GLOBAL,
Path: apiPath,
})
if err != nil {
t.Fatalf("exp nil err from AddPath; got %v", err)
}
return apiPath
}
t.Run("WithPeerInfo/Del(Add())", func(t *testing.T) {
pfx := bgp.NewIPAddrPrefix(24, "10.0.1.0")
apiPath := setup(t, pfx, &table.PeerInfo{
AS: 26496,
ID: net.ParseIP("10.20.30.36"),
Address: net.ParseIP("10.20.30.36"),
})
// make sure path exists
nlri, err := apiPath.GetNativeNlri()
if err != nil {
t.Fatal(err)
}
apiPathBack, err := get(t, nlri.String())
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(apiPathBack.Nlri, apiPath.Nlri) {
t.Fatalf("exp same path from Get")
}
// Now call deletePath using the same path we sent to Add.
req := gobgpapi.DeletePathRequest{
Resource: gobgpapi.Resource_GLOBAL,
Path: apiPath,
}
if _, err = cli.DeletePath(ctx, &req); err != nil {
t.Fatal(err)
}
got, err := get(t, pfx.String())
if err == nil || got != nil {
t.Fatalf("exp non-nil err for request to get deleted path")
}
})
t.Run("WithOutPeerInfo/Del(Add())", func(t *testing.T) {
pfx := bgp.NewIPAddrPrefix(24, "10.0.2.0")
apiPath := setup(t, pfx, nil)
// make sure path exists
nlri, err := apiPath.GetNativeNlri()
if err != nil {
t.Fatal(err)
}
apiPathBack, err := get(t, nlri.String())
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(apiPathBack.Nlri, apiPath.Nlri) {
t.Fatalf("exp same path from Get")
}
// Now call deletePath using the same path we sent to Add.
req := gobgpapi.DeletePathRequest{
Resource: gobgpapi.Resource_GLOBAL,
Path: apiPath,
}
if _, err = cli.DeletePath(ctx, &req); err != nil {
t.Fatal(err)
}
got, err := get(t, pfx.String())
if err == nil || got != nil {
t.Fatalf("exp non-nil err for request to get deleted path")
}
})
t.Run("WithPeerInfo/Del(Get())", func(t *testing.T) {
pfx := bgp.NewIPAddrPrefix(24, "10.0.3.0")
_ = setup(t, pfx, &table.PeerInfo{
AS: 26496,
ID: net.ParseIP("10.20.30.36"),
Address: net.ParseIP("10.20.30.36"),
})
// discarded what we sent, instead will try to use the result of a call
// to GetPath for our argument to Delete.
apiPathBack, err := get(t, pfx.String())
if err != nil {
t.Fatal(err)
}
// Now call deletePath using the same path we got from Get.
req := gobgpapi.DeletePathRequest{
Resource: gobgpapi.Resource_GLOBAL,
Path: apiPathBack,
}
if _, err = cli.DeletePath(ctx, &req); err != nil {
t.Fatal(err)
}
// We expect the path to be deleted, but it still exists. Either DeletePath
// should indicate no match was found and return an error OR (much better)
// would be for the DeletePath to succeed.
got, err := get(t, pfx.String())
if err == nil || got != nil {
t.Fatalf("exp non-nil err for request to get deleted path")
}
})
t.Run("WithOutPeerInfo/Del(Get())", func(t *testing.T) {
pfx := bgp.NewIPAddrPrefix(24, "10.0.4.0")
_ = setup(t, pfx, nil)
// discarded what we sent, instead will try to use the result of a call
// to GetPath for our argument to Delete.
apiPathBack, err := get(t, pfx.String())
if err != nil {
t.Fatal(err)
}
// Now call deletePath using the same path we got from Get.
req := gobgpapi.DeletePathRequest{
Resource: gobgpapi.Resource_GLOBAL,
Path: apiPathBack,
}
if _, err = cli.DeletePath(ctx, &req); err != nil {
t.Fatal(err)
}
// We expect the path to be deleted, but it still exists. Either DeletePath
// should indicate no match was found and return an error OR (much better)
// would be for the DeletePath to succeed.
got, err := get(t, pfx.String())
if err == nil || got != nil {
t.Fatalf("exp non-nil err for request to get deleted path")
}
})
}
func closeStream(stream grpc.ClientStream) (err error) {
stream.CloseSend()
switch T := stream.(type) {
case gobgpapi.GobgpApi_GetPathClient:
for err == nil {
_, err = T.Recv()
}
}
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment